Radium Engine  1.5.0
SkeletonComponent.cpp
1 #include <Engine/Scene/SkeletonComponent.hpp>
2 
3 #include <fstream>
4 #include <iostream>
5 #include <queue>
6 
7 #include <Core/Animation/KeyFramedValueInterpolators.hpp>
8 #include <Core/Animation/Pose.hpp>
9 #include <Core/Asset/HandleToSkeleton.hpp>
10 #include <Core/Containers/AlignedStdVector.hpp>
11 #include <Core/Containers/MakeShared.hpp>
12 #include <Core/Geometry/TriangleMesh.hpp>
13 #include <Core/Math/Math.hpp> // areApproxEqual
14 
15 #include <Engine/Data/BlinnPhongMaterial.hpp>
16 #include <Engine/Data/Mesh.hpp>
17 #include <Engine/Data/ShaderConfigFactory.hpp>
18 #include <Engine/Rendering/RenderObject.hpp>
19 #include <Engine/Rendering/RenderObjectManager.hpp>
20 #include <Engine/Rendering/RenderTechnique.hpp>
21 #include <Engine/Scene/ComponentMessenger.hpp>
22 
24 
27 using Ra::Core::Animation::WeightMatrix;
29 
30 using namespace Ra::Core::Utils; // log
32 
33 namespace Ra {
34 namespace Engine {
35 namespace Scene {
36 
37 std::shared_ptr<Data::Mesh> SkeletonComponent::s_boneMesh { nullptr };
38 std::shared_ptr<Data::BlinnPhongMaterial> SkeletonComponent::s_boneMaterial { nullptr };
39 std::shared_ptr<Rendering::RenderTechnique> SkeletonComponent::s_boneRenderTechnique { nullptr };
40 
41 SkeletonComponent::SkeletonComponent( const std::string& name, Entity* entity ) :
42  Component( name, entity ) {}
43 
44 SkeletonComponent::~SkeletonComponent() {}
45 
46 // Component interface
47 
48 bool SkeletonComponent::canEdit( const Index& roIdx ) const {
49  // returns true if the roIdx is one of our bones.
50  return ( m_boneMap.find( roIdx ) != m_boneMap.end() );
51 }
52 
53 Core::Transform SkeletonComponent::getTransform( const Index& roIdx ) const {
54  CORE_ASSERT( canEdit( roIdx ), "Transform is not editable" );
55  const uint boneIdx = m_boneMap.at( roIdx );
56  return m_skel.getPose( SpaceType::MODEL )[boneIdx];
57 }
58 
59 void SkeletonComponent::setTransform( const Index& roIdx, const Core::Transform& transform ) {
60  CORE_ASSERT( canEdit( roIdx ), "Transform is not editable" );
61  const uint boneIdx = m_boneMap.at( roIdx );
62  const Core::Transform& TBoneModel = m_skel.getTransform( boneIdx, SpaceType::MODEL );
63  const Core::Transform& TBoneLocal = m_skel.getTransform( boneIdx, SpaceType::LOCAL );
64  auto diff = TBoneModel.inverse() * transform;
65  m_skel.setTransform( boneIdx, TBoneLocal * diff, SpaceType::LOCAL );
66 }
67 
68 Core::Transform SkeletonComponent::getBoneTransform( uint boneIdx, const SpaceType MODE ) {
69  return m_skel.getTransform( boneIdx, MODE );
70 }
71 void SkeletonComponent::setBoneTransform( uint boneIdx,
72  const Core::Transform& transform,
73  const SpaceType MODE ) {
74  m_skel.setTransform( boneIdx, transform, MODE );
75  updateDisplay();
76 }
77 // Build from fileData
78 
79 void SkeletonComponent::handleSkeletonLoading( const Core::Asset::HandleData* data ) {
80  m_skelName = data->getName();
81 
82  m_skel.setName( data->getName() );
83 
84  Core::Asset::createSkeleton( *data, m_skel );
85 
86  m_refPose = m_skel.getPose( SpaceType::LOCAL );
87 
88  setupSkeletonDisplay();
89  setupIO();
90 }
91 
92 void SkeletonComponent::handleAnimationLoading(
93  const std::vector<Core::Asset::AnimationData*>& data ) {
94  CORE_ASSERT( ( m_skel.size() != 0 ), "A Skeleton should be loaded first." );
95  m_animations.clear();
96  m_animations.reserve( data.size() );
97 
98  auto pose = m_skel.getPose( SpaceType::LOCAL );
99  for ( uint n = 0; n < data.size(); ++n ) {
100  m_animations.emplace_back();
101  m_animations.back().reserve( m_skel.size() );
102  auto handleAnim = data[n]->getHandleData();
103  for ( uint i = 0; i < m_skel.size(); ++i ) {
104  auto it =
105  std::find_if( handleAnim.cbegin(), handleAnim.cend(), [this, i]( const auto& ha ) {
106  return m_skel.getLabel( i ) == ha.m_name;
107  } );
108  if ( it == handleAnim.cend() ) {
109  m_animations.back().push_back( KeyFramedValue( 0_ra, pose[i] ) );
110  }
111  else { m_animations.back().push_back( it->m_anim ); }
112  }
113  }
114  if ( m_animations.empty() ) {
115  m_animations.emplace_back();
116  for ( uint i = 0; i < m_skel.size(); ++i ) {
117  m_animations[0].push_back( KeyFramedValue( 0_ra, pose[i] ) );
118  }
119  }
120  m_animationID = 0;
121  m_animationTime = 0_ra;
122 }
123 
124 // Skeleton-based animation data
125 
126 void SkeletonComponent::setSkeleton( const Skeleton& skel ) {
127  m_skel = skel;
128  m_refPose = skel.getPose( SpaceType::LOCAL );
129  setupSkeletonDisplay();
130 }
131 
132 SkeletonComponent::Animation& SkeletonComponent::addNewAnimation() {
133  m_animations.emplace_back();
134  for ( uint i = 0; i < m_skel.size(); ++i ) {
135  m_animations.back().push_back( KeyFramedValue( 0_ra, m_refPose[i] ) );
136  }
137  return m_animations.back();
138 }
139 
140 void SkeletonComponent::removeAnimation( const size_t i ) {
141  CORE_ASSERT( i < m_animations.size(), "Out of bound index." );
142  m_animations.erase( m_animations.begin() + i );
143  m_animationID = i > 1 ? i - 1 : 0;
144 }
145 
146 void SkeletonComponent::useAnimation( const size_t i ) {
147  if ( i < m_animations.size() ) { m_animationID = i; }
148 }
149 
150 size_t SkeletonComponent::getAnimationId() const {
151  return m_animationID;
152 }
153 
154 // Animation Process
155 
156 void SkeletonComponent::update( Scalar t ) {
157  m_wasReset = Core::Math::areApproxEqual( t, 0_ra );
158  if ( m_wasReset ) {
159  m_animationTime = t;
160  m_skel.setPose( m_refPose, SpaceType::LOCAL );
161 
162  updateDisplay();
163  return;
164  }
165 
166  m_animationTime = m_speed * t;
167  Scalar lastTime = 0;
168  if ( !m_animations.empty() ) {
169  // m_animationID is always < m_animation.size() unless m_animations.empty()
170  for ( const auto& boneAnim : m_animations[m_animationID] ) {
171  lastTime = std::max( lastTime, *boneAnim.getTimes().rbegin() );
172  }
173  }
174  if ( m_autoRepeat ) {
175  if ( !m_pingPong ) { m_animationTime = std::fmod( m_animationTime, lastTime ); }
176  else {
177  m_animationTime = std::fmod( m_animationTime, 2 * lastTime );
178  if ( m_animationTime > lastTime ) { m_animationTime = 2 * lastTime - m_animationTime; }
179  }
180  }
181  else if ( m_pingPong ) {
182  if ( m_animationTime > 2 * lastTime ) { m_animationTime = 0_ra; }
183  else if ( m_animationTime > lastTime ) { m_animationTime = 2 * lastTime - m_animationTime; }
184  }
185 
186  // get the current pose from the animation
187  Core::Animation::Pose pose = m_skel.getPose( SpaceType::LOCAL );
188  if ( !m_animations.empty() ) {
189 #pragma omp parallel for
190  for ( int i = 0; i < int( m_animations[m_animationID].size() ); ++i ) {
191  pose[uint( i )] = m_animations[m_animationID][uint( i )].at(
192  m_animationTime, Core::Animation::linearInterpolate<Core::Transform> );
193  }
194  }
195  else { pose = m_refPose; }
196  m_skel.setPose( pose, SpaceType::LOCAL );
197 
198  updateDisplay();
199 }
200 
201 Scalar SkeletonComponent::getAnimationTime() const {
202  return m_animationTime;
203 }
204 
205 std::pair<Scalar, Scalar> SkeletonComponent::getAnimationTimeInterval() const {
206  if ( m_animations.empty() ) { return { 0_ra, 0_ra }; }
207  Scalar startTime = std::numeric_limits<Scalar>::max();
208  Scalar endTime = 0;
209  for ( const auto& boneAnim : m_animations[m_animationID] ) {
210  const auto& times = boneAnim.getTimes();
211  startTime = std::min( startTime, *times.begin() );
212  endTime = std::max( endTime, *times.rbegin() );
213  }
214  return { startTime, endTime };
215 }
216 
217 void SkeletonComponent::setSpeed( const Scalar value ) {
218  m_speed = value;
219 }
220 
221 Scalar SkeletonComponent::getSpeed() const {
222  return m_speed;
223 }
224 
225 void SkeletonComponent::autoRepeat( const bool status ) {
226  m_autoRepeat = status;
227 }
228 
229 bool SkeletonComponent::isAutoRepeat() const {
230  return m_autoRepeat;
231 }
232 
233 void SkeletonComponent::pingPong( const bool status ) {
234  m_pingPong = status;
235 }
236 
237 bool SkeletonComponent::isPingPong() const {
238  return m_pingPong;
239 }
240 
241 // Skeleton display
242 
243 void SkeletonComponent::setXray( bool on ) const {
244  for ( const auto& b : m_boneDrawables ) {
245  b->setXRay( on );
246  }
247 }
248 
249 bool SkeletonComponent::isXray() const {
250  if ( m_boneDrawables.size() == 0 ) return false;
251  return m_boneDrawables.front()->isXRay();
252 }
253 
254 void SkeletonComponent::toggleSkeleton( const bool status ) {
255  for ( const auto& b : m_boneDrawables ) {
256  b->setVisible( status );
257  }
258 }
259 
260 bool SkeletonComponent::isShowingSkeleton() const {
261  if ( m_boneDrawables.size() == 0 ) return false;
262  return m_boneDrawables.front()->isVisible();
263 }
264 
265 Core::Geometry::TriangleMesh makeBoneShape() {
266  // Bone along Z axis.
267  Core::Geometry::TriangleMesh mesh;
268  const Scalar l = 0.1_ra;
269  const Scalar w = 0.1_ra;
270  mesh.setVertices( { Core::Vector3( 0, 0, 0 ),
271  Core::Vector3( 0, 0, 1 ),
272  Core::Vector3( 0, w, l ),
273  Core::Vector3( w, 0, l ),
274  Core::Vector3( 0, -w, l ),
275  Core::Vector3( -w, 0, l ) } );
276 
277  mesh.setNormals( { Core::Vector3( 0, 0, -1 ),
278  Core::Vector3( 0, 0, 1 ),
279  Core::Vector3( 0, 1, 0 ),
280  Core::Vector3( 1, 0, 0 ),
281  Core::Vector3( 0, -1, 0 ),
282  Core::Vector3( -1, 0, 0 ) } );
283 
284  mesh.setIndices( { Core::Vector3ui( 0, 2, 3 ),
285  Core::Vector3ui( 0, 5, 2 ),
286  Core::Vector3ui( 0, 3, 4 ),
287  Core::Vector3ui( 0, 4, 5 ),
288  Core::Vector3ui( 1, 3, 2 ),
289  Core::Vector3ui( 1, 2, 5 ),
290  Core::Vector3ui( 1, 4, 3 ),
291  Core::Vector3ui( 1, 5, 4 ) } );
292  return mesh;
293 }
294 
295 void SkeletonComponent::setupSkeletonDisplay() {
296  m_renderObjects.clear();
297  m_boneDrawables.clear();
298  if ( !s_boneMesh ) {
299  s_boneMesh = std::make_shared<Data::Mesh>( "Bone Mesh" );
300  s_boneMesh->loadGeometry( makeBoneShape() );
301  s_boneMaterial.reset( new Data::BlinnPhongMaterial( "Bone Material" ) );
302  s_boneMaterial->m_kd = Color::Grey( 0.4_ra );
303  s_boneMaterial->m_ks = Color::Black();
304  s_boneRenderTechnique.reset( new Rendering::RenderTechnique );
305  s_boneRenderTechnique->setConfiguration(
306  *Data::ShaderConfigurationFactory::getConfiguration( "BlinnPhong" ) );
307  s_boneRenderTechnique->setParametersProvider( s_boneMaterial );
308  }
309  for ( uint i = 0; i < m_skel.size(); ++i ) {
310  if ( !m_skel.m_graph.isLeaf( i ) && !m_skel.m_graph.isRoot( i ) &&
311  m_skel.getLabel( i ).find( "_$AssimpFbx$_" ) == std::string::npos ) {
312  std::string name = m_skel.getLabel( i ) + "_" + std::to_string( i );
313  auto ro = new Engine::Rendering::RenderObject(
314  name, this, Rendering::RenderObjectType::Geometry );
315  ro->setRenderTechnique( s_boneRenderTechnique );
316  ro->setMesh( s_boneMesh );
317  ro->setMaterial( s_boneMaterial );
318  ro->setXRay( false );
319  addRenderObject( ro );
320  m_boneMap[m_renderObjects.back()] = i;
321  m_boneDrawables.push_back( ro );
322  }
323  else { LOG( logDEBUG ) << "Bone " << m_skel.getLabel( i ) << " not displayed."; }
324  }
325  updateDisplay();
326 }
327 
328 void SkeletonComponent::printSkeleton( const Skeleton& skeleton ) {
329  std::deque<uint> queue;
330  std::deque<int> levels;
331 
332  queue.push_back( 0 );
333  levels.push_back( 0 );
334  while ( !queue.empty() ) {
335  uint i = queue.front();
336  queue.pop_front();
337  int level = levels.front();
338  levels.pop_front();
339  std::cout << i << " " << skeleton.getLabel( i ) << "\t";
340  for ( const auto& c : skeleton.m_graph.children()[i] ) {
341  queue.push_back( c );
342  levels.push_back( level + 1 );
343  }
344 
345  if ( levels.front() != level ) { std::cout << std::endl; }
346  }
347 }
348 
349 void SkeletonComponent::updateDisplay() {
350  for ( auto& bone : m_boneDrawables ) {
351  uint boneIdx = m_boneMap.at( bone->getIndex() );
352  Core::Vector3 start;
353  Core::Vector3 end;
354  m_skel.getBonePoints( boneIdx, start, end );
355 
356  Core::Transform scale = Core::Transform::Identity();
357  scale.scale( ( end - start ).norm() );
358 
359  Core::Quaternion rot =
360  Core::Quaternion::FromTwoVectors( Core::Vector3::UnitZ(), end - start );
361 
362  Core::Transform boneTransform = m_skel.getTransform( boneIdx, SpaceType::MODEL );
363  Core::Matrix3 rotation = rot.toRotationMatrix();
364  Core::Transform drawTransform;
365  drawTransform.linear() = rotation;
366  drawTransform.translation() = boneTransform.translation();
367 
368  bone->setLocalTransform( drawTransform * scale );
369  }
370 }
371 
372 void SkeletonComponent::setManipulationScheme(
374  m_skel.m_manipulation = scheme;
375 }
376 
377 Core::Animation::Skeleton::Manipulation SkeletonComponent::getManipulationScheme() {
378  return m_skel.m_manipulation;
379 }
380 
381 // Component Communication (CC)
382 
383 void SkeletonComponent::setupIO() {
385  std::bind( &SkeletonComponent::getSkeleton, this );
386  ComponentMessenger::getInstance()->registerOutput<Skeleton>(
387  getEntity(), this, m_skelName, skelOut );
388 
389  using BoneMap = std::map<Index, uint>;
391  std::bind( &SkeletonComponent::getBoneRO2idx, this );
392  ComponentMessenger::getInstance()->registerOutput<BoneMap>(
393  getEntity(), this, m_skelName, boneMapOut );
394 
396  std::bind( &SkeletonComponent::getRefPoseOutput, this );
397  ComponentMessenger::getInstance()->registerOutput<Core::Animation::Pose>(
398  getEntity(), this, m_skelName, refpOut );
399 
401  std::bind( &SkeletonComponent::getAnimationOutput, this );
402  ComponentMessenger::getInstance()->registerOutput<Animation>(
403  getEntity(), this, m_skelName, animOut );
404 
406  std::bind( &SkeletonComponent::getTimeOutput, this );
407  ComponentMessenger::getInstance()->registerOutput<Scalar>(
408  getEntity(), this, m_skelName, timeOut );
409 
411  std::bind( &SkeletonComponent::getWasReset, this );
412  ComponentMessenger::getInstance()->registerOutput<bool>(
413  getEntity(), this, m_skelName, resetOut );
414 }
415 
416 const std::map<Index, uint>* SkeletonComponent::getBoneRO2idx() const {
417  return &m_boneMap;
418 }
419 
420 const Core::Animation::RefPose* SkeletonComponent::getRefPoseOutput() const {
421  return &m_refPose;
422 }
423 
424 const SkeletonComponent::Animation* SkeletonComponent::getAnimationOutput() const {
425  if ( m_animations.empty() ) { return nullptr; }
426  return &m_animations[m_animationID];
427 }
428 
429 const Scalar* SkeletonComponent::getTimeOutput() const {
430  return &m_animationTime;
431 }
432 
433 const bool* SkeletonComponent::getWasReset() const {
434  return &m_wasReset;
435 }
436 
437 } // namespace Scene
438 } // namespace Engine
439 } // namespace Ra
Label getLabel(const uint i) const
AdjacencyList m_graph
The Joint hierarchy.
Definition: Skeleton.hpp:106
const Pose & getPose(const SpaceType MODE) const override
Definition: Skeleton.cpp:22
virtual const std::string & getName() const
Acces to the name of the asset.
Definition: AssetData.hpp:29
std::vector< Core::Animation::KeyFramedValue< Core::Transform > > Animation
Animations are lists of keyframed transforms (one per bone).
Definition: Cage.cpp:3
std::function< const T *(void)> Getter
Function pointer for a getter function.