Radium Engine  1.5.20
Loading...
Searching...
No Matches
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
27using Ra::Core::Animation::WeightMatrix;
29
30using namespace Ra::Core::Utils; // log
32
33namespace Ra {
34namespace Engine {
35namespace Scene {
36
37std::shared_ptr<Data::Mesh> SkeletonComponent::s_boneMesh { nullptr };
38std::shared_ptr<Data::BlinnPhongMaterial> SkeletonComponent::s_boneMaterial { nullptr };
39std::shared_ptr<Rendering::RenderTechnique> SkeletonComponent::s_boneRenderTechnique { nullptr };
40
41SkeletonComponent::SkeletonComponent( const std::string& name, Entity* entity ) :
42 Component( name, entity ) {}
43
44SkeletonComponent::~SkeletonComponent() {}
45
46// Component interface
47
48bool 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
53Core::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
59void 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
68Core::Transform SkeletonComponent::getBoneTransform( uint boneIdx, const SpaceType MODE ) {
69 return m_skel.getTransform( boneIdx, MODE );
70}
71void 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
79void 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
92void SkeletonComponent::handleAnimationLoading(
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
126void SkeletonComponent::setSkeleton( const Skeleton& skel ) {
127 m_skel = skel;
128 m_refPose = skel.getPose( SpaceType::LOCAL );
129 setupSkeletonDisplay();
130}
131
132SkeletonComponent::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
140void 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
146void SkeletonComponent::useAnimation( const size_t i ) {
147 if ( i < m_animations.size() ) { m_animationID = i; }
148}
149
150size_t SkeletonComponent::getAnimationId() const {
151 return m_animationID;
152}
153
154// Animation Process
155
156void 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
201Scalar SkeletonComponent::getAnimationTime() const {
202 return m_animationTime;
203}
204
205std::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
217void SkeletonComponent::setSpeed( const Scalar value ) {
218 m_speed = value;
219}
220
221Scalar SkeletonComponent::getSpeed() const {
222 return m_speed;
223}
224
225void SkeletonComponent::autoRepeat( const bool status ) {
226 m_autoRepeat = status;
227}
228
229bool SkeletonComponent::isAutoRepeat() const {
230 return m_autoRepeat;
231}
232
233void SkeletonComponent::pingPong( const bool status ) {
234 m_pingPong = status;
235}
236
237bool SkeletonComponent::isPingPong() const {
238 return m_pingPong;
239}
240
241// Skeleton display
242
243void SkeletonComponent::setXray( bool on ) const {
244 for ( const auto& b : m_boneDrawables ) {
245 b->setXRay( on );
246 }
247}
248
249bool SkeletonComponent::isXray() const {
250 if ( m_boneDrawables.size() == 0 ) return false;
251 return m_boneDrawables.front()->isXRay();
252}
253
254void SkeletonComponent::toggleSkeleton( const bool status ) {
255 for ( const auto& b : m_boneDrawables ) {
256 b->setVisible( status );
257 }
258}
259
260bool SkeletonComponent::isShowingSkeleton() const {
261 if ( m_boneDrawables.size() == 0 ) return false;
262 return m_boneDrawables.front()->isVisible();
263}
264
265Core::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
295void 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->setDiffuseColor( Color::Grey( 0.4_ra ) );
303 s_boneMaterial->setSpecularColor( 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 );
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
328void 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
349void 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
372void SkeletonComponent::setManipulationScheme(
374 m_skel.m_manipulation = scheme;
375}
376
377Core::Animation::Skeleton::Manipulation SkeletonComponent::getManipulationScheme() {
378 return m_skel.m_manipulation;
379}
380
381// Component Communication (CC)
382
383void 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
416const std::map<Index, uint>* SkeletonComponent::getBoneRO2idx() const {
417 return &m_boneMap;
418}
419
420const Core::Animation::RefPose* SkeletonComponent::getRefPoseOutput() const {
421 return &m_refPose;
422}
423
424const SkeletonComponent::Animation* SkeletonComponent::getAnimationOutput() const {
425 if ( m_animations.empty() ) { return nullptr; }
426 return &m_animations[m_animationID];
427}
428
429const Scalar* SkeletonComponent::getTimeOutput() const {
430 return &m_animationTime;
431}
432
433const bool* SkeletonComponent::getWasReset() const {
434 return &m_wasReset;
435}
436
437} // namespace Scene
438} // namespace Engine
439} // namespace Ra
T at(T... args)
T bind(T... args)
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
void setNormals(PointAttribHandle::Container &&normals)
Set normals.
void setVertices(PointAttribHandle::Container &&vertices)
Set vertices.
void setIndices(IndexContainerType &&indices)
void clear() override
Erases all data, making the AttribArrayGeometry empty.
T emplace_back(T... args)
T empty(T... args)
T endl(T... args)
T find_if(T... args)
T fmod(T... args)
T front(T... args)
T make_shared(T... args)
T max(T... args)
T min(T... args)
hepler function to manage enum as underlying types in VariableSet
Definition Cage.cpp:3
T pop_front(T... args)
T push_back(T... args)
T size(T... args)
T to_string(T... args)