Radium Engine  1.5.0
SkeletonBasedAnimationSystem.cpp
1 #include <Engine/Scene/SkeletonBasedAnimationSystem.hpp>
2 
3 #include <string>
4 
5 #include <Core/Asset/FileData.hpp>
6 #include <Core/Math/Math.hpp>
7 #include <Core/Resources/Resources.hpp>
8 #include <Core/Tasks/Task.hpp>
9 #include <Core/Tasks/TaskQueue.hpp>
10 
11 #include <Engine/Data/Texture.hpp>
12 #include <Engine/Data/TextureManager.hpp>
13 #include <Engine/FrameInfo.hpp>
14 #include <Engine/RadiumEngine.hpp>
15 #include <Engine/Scene/SkeletonComponent.hpp>
16 #include <Engine/Scene/SkinningComponent.hpp>
17 
18 using namespace Ra::Core::Animation;
19 
20 namespace Ra {
21 namespace Engine {
22 namespace Scene {
23 
24 SkeletonBasedAnimationSystem::SkeletonBasedAnimationSystem() : System(), m_xrayOn( false ) {
25  auto resourceDir { Core::Resources::getRadiumResourcesPath() };
26  if ( resourceDir ) {
27  auto* engine = RadiumEngine::getInstance();
28  auto* texMngr = engine->getTextureManager();
29  // Register an entry into the texture manager
30  auto& heatmapData = texMngr->addTexture( "Engine:Skinning:weights", 1, 128, nullptr );
31  // load the texture image without OpenGL initialization
32  heatmapData.name = *resourceDir + "/Textures/heatmap.png";
33  texMngr->loadTextureImage( heatmapData );
34  // Set the registered name again for further access to the texture
35  heatmapData.name = "Engine:Skinning:weights";
36  }
37 }
38 
39 SkeletonBasedAnimationSystem::~SkeletonBasedAnimationSystem() {
40  SkeletonComponent::s_boneMesh.reset();
41  SkeletonComponent::s_boneMaterial.reset();
42  SkeletonComponent::s_boneRenderTechnique.reset();
43 }
44 
45 // System Interface
46 
48  const FrameInfo& frameInfo ) {
49  for ( auto compEntry : m_components ) {
50  // deal with AnimationComponents
51  if ( auto animComp = dynamic_cast<SkeletonComponent*>( compEntry.second ) ) {
52  if ( !Core::Math::areApproxEqual( m_time, frameInfo.m_animationTime ) ) {
53  // here we update the skeleton w.r.t. the animation
54  auto animFunc =
55  std::bind( &SkeletonComponent::update, animComp, frameInfo.m_animationTime );
56  auto animTask = std::make_unique<Core::FunctionTask>(
57  animFunc, "AnimatorTask_" + animComp->getSkeleton()->getName() );
58  taskQueue->registerTask( std::move( animTask ) );
59  }
60  else {
61  // here we update the skeleton w.r.t. the manipulation
62  auto animFunc = std::bind( &SkeletonComponent::updateDisplay, animComp );
63  auto animTask = std::make_unique<Core::FunctionTask>(
64  animFunc, "AnimatorTask_" + animComp->getSkeleton()->getName() );
65  taskQueue->registerTask( std::move( animTask ) );
66  }
67  }
68  // deal with SkinningComponents
69  else if ( auto skinComp = dynamic_cast<SkinningComponent*>( compEntry.second ) ) {
70  auto skinFunc = std::bind( &SkinningComponent::skin, skinComp );
71  auto skinTask = std::make_unique<Core::FunctionTask>(
72  skinFunc, "SkinnerTask_" + skinComp->getMeshName() );
73  auto endFunc = std::bind( &SkinningComponent::endSkinning, skinComp );
74  auto endTask = std::make_unique<Core::FunctionTask>(
75  endFunc, "SkinnerEndTask_" + skinComp->getMeshName() );
76 
77  auto skinTaskId = taskQueue->registerTask( std::move( skinTask ) );
78  auto endTaskId = taskQueue->registerTask( std::move( endTask ) );
79  taskQueue->addPendingDependency( "AnimatorTask_" + skinComp->getSkeletonName(),
80  skinTaskId );
81  taskQueue->addDependency( skinTaskId, endTaskId );
82  }
83  }
84 
85  m_time = frameInfo.m_animationTime;
86 }
87 
89  const Core::Asset::FileData* fileData ) {
90  auto skelData = fileData->getHandleData();
91  auto animData = fileData->getAnimationData();
92 
93  // deal with AnimationComponents
94  Scalar startTime = std::numeric_limits<Scalar>::max();
95  Scalar endTime = 0;
96  for ( const auto& skel : skelData ) {
97  auto component = new SkeletonComponent( "AC_" + skel->getName(), entity );
98  component->handleSkeletonLoading( skel );
99  component->handleAnimationLoading( animData );
100  auto [s, e] = component->getAnimationTimeInterval();
101  startTime = std::min( startTime, s );
102  endTime = std::max( endTime, e );
103  component->setXray( m_xrayOn );
104  registerComponent( entity, component );
105  }
106  // configure the time on the Engine
107  auto engine = RadiumEngine::getInstance();
108  engine->setStartTime( startTime );
109  engine->setEndTime( endTime );
110 
111  // deal with SkinningComponents
112  auto geomData = fileData->getGeometryData();
113  if ( geomData.size() > 0 && skelData.size() > 0 ) {
114  for ( const auto& geom : geomData ) {
115  // look for a skeleton skinning this mesh
116  // warning: there should be at most one such skeleton!
117  auto it = std::find_if( skelData.begin(), skelData.end(), [&geom]( const auto& skel ) {
118  return std::find_if( skel->getBindMeshes().begin(),
119  skel->getBindMeshes().end(),
120  [&geom]( const auto& meshName ) {
121  return meshName == geom->getName();
122  } ) != skel->getBindMeshes().end();
123  } );
124  if ( it != skelData.end() ) {
125  const auto& skel = *it;
126  SkinningComponent* component = new SkinningComponent(
127  "SkC_" + geom->getName(), SkinningComponent::LBS, entity );
128  component->handleSkinDataLoading( skel, geom->getName() );
129  registerComponent( entity, component );
130  }
131  }
132  }
133 }
134 
135 // Skeleton display
136 
137 void SkeletonBasedAnimationSystem::setXray( bool on ) {
138  m_xrayOn = on;
139  for ( const auto& comp : m_components ) {
140  if ( auto animComp = dynamic_cast<SkeletonComponent*>( comp.second ) ) {
141  animComp->setXray( on );
142  }
143  }
144 }
145 
146 bool SkeletonBasedAnimationSystem::isXrayOn() {
147  return m_xrayOn;
148 }
149 
150 void SkeletonBasedAnimationSystem::toggleSkeleton( const bool status ) {
151  for ( const auto& comp : m_components ) {
152  if ( auto animComp = dynamic_cast<SkeletonComponent*>( comp.second ) ) {
153  animComp->toggleSkeleton( status );
154  }
155  }
156 }
157 
158 } // namespace Scene
159 } // namespace Engine
160 } // namespace Ra
void addPendingDependency(const std::string &predecessors, TaskId successor)
Definition: TaskQueue.cpp:113
void addDependency(TaskId predecessor, TaskId successor)
Definition: TaskQueue.cpp:73
TaskId registerTask(std::unique_ptr< Task > task)
Definition: TaskQueue.cpp:30
An entity is an scene element. It ties together components with a transform.
Definition: Entity.hpp:23
void handleAssetLoading(Entity *entity, const Core::Asset::FileData *fileData) override
Loads Skeletons and Animations from a file data into the givn Entity.
void generateTasks(Core::TaskQueue *taskQueue, const FrameInfo &frameInfo) override
Creates a task for each AnimationComponent to update skeleton display.
void update(Scalar time)
Updates the skeleton pose as the pose corresponding to time time.
void updateDisplay()
Updates the skeleton display.
The SkinningComponent class is responsible for applying Geometric Skinning Methods on an animated obj...
void endSkinning()
Update internal data and update the skinned mesh.
void handleSkinDataLoading(const Core::Asset::HandleData *data, const std::string &meshName)
void skin()
Apply the Skinning Method and update the SkinningFrameData.
std::vector< std::pair< const Entity *, Component * > > m_components
List of active components.
Definition: System.hpp:103
virtual void registerComponent(const Entity *entity, Component *component)
Definition: System.cpp:10
std::enable_if<!std::numeric_limits< T >::is_integer, bool >::type areApproxEqual(T x, T y, T espilonBoostFactor=T(10))
Compare two numbers such that |x-y| < espilon*epsilonBoostFactor.
Definition: Math.hpp:42
optional< std::string > getRadiumResourcesPath()
Get the path of Radium internal resources.
Definition: Resources.cpp:35
Definition: Cage.cpp:3
Structure passed to each system before they fill the task queue.
Definition: FrameInfo.hpp:8
Scalar m_animationTime
The current animation time.
Definition: FrameInfo.hpp:10