1 #include <Engine/Scene/SkeletonComponent.hpp>
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>
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>
27 using Ra::Core::Animation::WeightMatrix;
30 using namespace Ra::Core::Utils;
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 };
41 SkeletonComponent::SkeletonComponent(
const std::string& name, Entity* entity ) :
42 Component( name, entity ) {}
44 SkeletonComponent::~SkeletonComponent() {}
48 bool SkeletonComponent::canEdit(
const Index& roIdx )
const {
50 return ( m_boneMap.find( roIdx ) != m_boneMap.end() );
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];
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 );
68 Core::Transform SkeletonComponent::getBoneTransform( uint boneIdx,
const SpaceType MODE ) {
69 return m_skel.getTransform( boneIdx, MODE );
71 void SkeletonComponent::setBoneTransform( uint boneIdx,
72 const Core::Transform& transform,
74 m_skel.setTransform( boneIdx, transform, MODE );
82 m_skel.setName( data->
getName() );
84 Core::Asset::createSkeleton( *data, m_skel );
86 m_refPose = m_skel.getPose( SpaceType::LOCAL );
88 setupSkeletonDisplay();
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." );
96 m_animations.reserve( data.size() );
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 ) {
105 std::find_if( handleAnim.cbegin(), handleAnim.cend(), [
this, i](
const auto& ha ) {
106 return m_skel.getLabel( i ) == ha.m_name;
108 if ( it == handleAnim.cend() ) {
111 else { m_animations.back().push_back( it->m_anim ); }
114 if ( m_animations.empty() ) {
115 m_animations.emplace_back();
116 for ( uint i = 0; i < m_skel.size(); ++i ) {
121 m_animationTime = 0_ra;
126 void SkeletonComponent::setSkeleton(
const Skeleton& skel ) {
128 m_refPose = skel.
getPose( SpaceType::LOCAL );
129 setupSkeletonDisplay();
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] ) );
137 return m_animations.back();
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;
146 void SkeletonComponent::useAnimation(
const size_t i ) {
147 if ( i < m_animations.size() ) { m_animationID = i; }
150 size_t SkeletonComponent::getAnimationId()
const {
151 return m_animationID;
156 void SkeletonComponent::update( Scalar t ) {
157 m_wasReset = Core::Math::areApproxEqual( t, 0_ra );
160 m_skel.setPose( m_refPose, SpaceType::LOCAL );
166 m_animationTime = m_speed * t;
168 if ( !m_animations.empty() ) {
170 for (
const auto& boneAnim : m_animations[m_animationID] ) {
171 lastTime = std::max( lastTime, *boneAnim.getTimes().rbegin() );
174 if ( m_autoRepeat ) {
175 if ( !m_pingPong ) { m_animationTime = std::fmod( m_animationTime, lastTime ); }
177 m_animationTime = std::fmod( m_animationTime, 2 * lastTime );
178 if ( m_animationTime > lastTime ) { m_animationTime = 2 * lastTime - m_animationTime; }
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; }
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> );
195 else { pose = m_refPose; }
196 m_skel.setPose( pose, SpaceType::LOCAL );
201 Scalar SkeletonComponent::getAnimationTime()
const {
202 return m_animationTime;
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();
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() );
214 return { startTime, endTime };
217 void SkeletonComponent::setSpeed(
const Scalar value ) {
221 Scalar SkeletonComponent::getSpeed()
const {
225 void SkeletonComponent::autoRepeat(
const bool status ) {
226 m_autoRepeat = status;
229 bool SkeletonComponent::isAutoRepeat()
const {
233 void SkeletonComponent::pingPong(
const bool status ) {
237 bool SkeletonComponent::isPingPong()
const {
243 void SkeletonComponent::setXray(
bool on )
const {
244 for (
const auto& b : m_boneDrawables ) {
249 bool SkeletonComponent::isXray()
const {
250 if ( m_boneDrawables.size() == 0 )
return false;
251 return m_boneDrawables.front()->isXRay();
254 void SkeletonComponent::toggleSkeleton(
const bool status ) {
255 for (
const auto& b : m_boneDrawables ) {
256 b->setVisible( status );
260 bool SkeletonComponent::isShowingSkeleton()
const {
261 if ( m_boneDrawables.size() == 0 )
return false;
262 return m_boneDrawables.front()->isVisible();
265 Core::Geometry::TriangleMesh makeBoneShape() {
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 ) } );
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 ) } );
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 ) } );
295 void SkeletonComponent::setupSkeletonDisplay() {
296 m_renderObjects.clear();
297 m_boneDrawables.clear();
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 );
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 );
323 else { LOG( logDEBUG ) <<
"Bone " << m_skel.getLabel( i ) <<
" not displayed."; }
328 void SkeletonComponent::printSkeleton(
const Skeleton& skeleton ) {
329 std::deque<uint> queue;
330 std::deque<int> levels;
332 queue.push_back( 0 );
333 levels.push_back( 0 );
334 while ( !queue.empty() ) {
335 uint i = queue.front();
337 int level = levels.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 );
345 if ( levels.front() != level ) { std::cout << std::endl; }
349 void SkeletonComponent::updateDisplay() {
350 for (
auto& bone : m_boneDrawables ) {
351 uint boneIdx = m_boneMap.at( bone->getIndex() );
354 m_skel.getBonePoints( boneIdx, start, end );
356 Core::Transform scale = Core::Transform::Identity();
357 scale.scale( ( end - start ).norm() );
359 Core::Quaternion rot =
360 Core::Quaternion::FromTwoVectors( Core::Vector3::UnitZ(), end - start );
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();
368 bone->setLocalTransform( drawTransform * scale );
372 void SkeletonComponent::setManipulationScheme(
374 m_skel.m_manipulation = scheme;
378 return m_skel.m_manipulation;
383 void SkeletonComponent::setupIO() {
385 std::bind( &SkeletonComponent::getSkeleton,
this );
386 ComponentMessenger::getInstance()->registerOutput<
Skeleton>(
387 getEntity(),
this, m_skelName, skelOut );
389 using BoneMap = std::map<Index, uint>;
391 std::bind( &SkeletonComponent::getBoneRO2idx,
this );
392 ComponentMessenger::getInstance()->registerOutput<BoneMap>(
393 getEntity(),
this, m_skelName, boneMapOut );
396 std::bind( &SkeletonComponent::getRefPoseOutput,
this );
397 ComponentMessenger::getInstance()->registerOutput<Core::Animation::Pose>(
398 getEntity(),
this, m_skelName, refpOut );
401 std::bind( &SkeletonComponent::getAnimationOutput,
this );
402 ComponentMessenger::getInstance()->registerOutput<Animation>(
403 getEntity(),
this, m_skelName, animOut );
406 std::bind( &SkeletonComponent::getTimeOutput,
this );
407 ComponentMessenger::getInstance()->registerOutput<Scalar>(
408 getEntity(),
this, m_skelName, timeOut );
411 std::bind( &SkeletonComponent::getWasReset,
this );
412 ComponentMessenger::getInstance()->registerOutput<
bool>(
413 getEntity(),
this, m_skelName, resetOut );
416 const std::map<Index, uint>* SkeletonComponent::getBoneRO2idx()
const {
420 const Core::Animation::RefPose* SkeletonComponent::getRefPoseOutput()
const {
424 const SkeletonComponent::Animation* SkeletonComponent::getAnimationOutput()
const {
425 if ( m_animations.empty() ) {
return nullptr; }
426 return &m_animations[m_animationID];
429 const Scalar* SkeletonComponent::getTimeOutput()
const {
430 return &m_animationTime;
433 const bool* SkeletonComponent::getWasReset()
const {
Label getLabel(const uint i) const
AdjacencyList m_graph
The Joint hierarchy.
const Pose & getPose(const SpaceType MODE) const override
virtual const std::string & getName() const
Acces to the name of the asset.
std::vector< Core::Animation::KeyFramedValue< Core::Transform > > Animation
Animations are lists of keyframed transforms (one per bone).
std::function< const T *(void)> Getter
Function pointer for a getter function.