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>
27using Ra::Core::Animation::WeightMatrix;
30using namespace Ra::Core::Utils;
41SkeletonComponent::SkeletonComponent(
const std::string& name, Entity* entity ) :
42 Component( name, entity ) {}
44SkeletonComponent::~SkeletonComponent() {}
48bool SkeletonComponent::canEdit(
const Index& roIdx )
const {
50 return ( m_boneMap.find( roIdx ) != m_boneMap.end() );
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];
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 );
68Core::Transform SkeletonComponent::getBoneTransform( uint boneIdx,
const SpaceType MODE ) {
69 return m_skel.getTransform( boneIdx, MODE );
71void 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();
92void SkeletonComponent::handleAnimationLoading(
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;
126void SkeletonComponent::setSkeleton(
const Skeleton& skel ) {
128 m_refPose = skel.
getPose( SpaceType::LOCAL );
129 setupSkeletonDisplay();
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();
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;
146void SkeletonComponent::useAnimation(
const size_t i ) {
147 if ( i < m_animations.size() ) { m_animationID = i; }
150size_t SkeletonComponent::getAnimationId()
const {
151 return m_animationID;
156void 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; }
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 );
201Scalar SkeletonComponent::getAnimationTime()
const {
202 return m_animationTime;
206 if ( m_animations.empty() ) {
return { 0_ra, 0_ra }; }
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 };
217void SkeletonComponent::setSpeed(
const Scalar value ) {
221Scalar SkeletonComponent::getSpeed()
const {
225void SkeletonComponent::autoRepeat(
const bool status ) {
226 m_autoRepeat = status;
229bool SkeletonComponent::isAutoRepeat()
const {
233void SkeletonComponent::pingPong(
const bool status ) {
237bool SkeletonComponent::isPingPong()
const {
243void SkeletonComponent::setXray(
bool on )
const {
244 for (
const auto& b : m_boneDrawables ) {
249bool SkeletonComponent::isXray()
const {
250 if ( m_boneDrawables.size() == 0 )
return false;
251 return m_boneDrawables.front()->isXRay();
254void SkeletonComponent::toggleSkeleton(
const bool status ) {
255 for (
const auto& b : m_boneDrawables ) {
256 b->setVisible( status );
260bool SkeletonComponent::isShowingSkeleton()
const {
261 if ( m_boneDrawables.size() == 0 )
return false;
262 return m_boneDrawables.front()->isVisible();
265Core::Geometry::TriangleMesh makeBoneShape() {
267 Core::Geometry::TriangleMesh mesh;
268 const Scalar l = 0.1_ra;
269 const Scalar w = 0.1_ra;
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 ) } );
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 ) } );
295void SkeletonComponent::setupSkeletonDisplay() {
296 m_renderObjects.
clear();
297 m_boneDrawables.clear();
300 s_boneMesh->loadGeometry( makeBoneShape() );
302 s_boneMaterial->setDiffuseColor( Color::Grey( 0.4_ra ) );
303 s_boneMaterial->setSpecularColor( Color::Black() );
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 ) {
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."; }
328void SkeletonComponent::printSkeleton(
const Skeleton& skeleton ) {
334 while ( !queue.
empty() ) {
335 uint i = queue.
front();
337 int level = levels.
front();
340 for (
const auto& c : skeleton.
m_graph.children()[i] ) {
349void 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 );
372void SkeletonComponent::setManipulationScheme(
374 m_skel.m_manipulation = scheme;
378 return m_skel.m_manipulation;
383void SkeletonComponent::setupIO() {
385 std::bind( &SkeletonComponent::getSkeleton,
this );
386 ComponentMessenger::getInstance()->registerOutput<
Skeleton>(
387 getEntity(),
this, m_skelName, skelOut );
391 std::bind( &SkeletonComponent::getBoneRO2idx,
this );
392 ComponentMessenger::getInstance()->registerOutput<BoneMap>(
393 getEntity(),
this, m_skelName, boneMapOut );
396 std::bind( &SkeletonComponent::getRefPoseOutput,
this );
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 );
424const SkeletonComponent::Animation* SkeletonComponent::getAnimationOutput()
const {
425 if ( m_animations.empty() ) {
return nullptr; }
426 return &m_animations[m_animationID];
429const Scalar* SkeletonComponent::getTimeOutput()
const {
430 return &m_animationTime;
433const 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.
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)
hepler function to manage enum as underlying types in VariableSet