Radium Engine  1.5.0
Skeleton.cpp
1 #include <Core/Animation/Skeleton.hpp>
2 #include <Core/Math/LinearAlgebra.hpp> // Math::clamp
3 
4 #include <algorithm>
5 #include <stack>
6 
7 namespace Ra {
8 namespace Core {
9 namespace Animation {
10 
12 Skeleton::Skeleton() : HandleArray(), m_graph(), m_modelSpace() {}
13 
14 Skeleton::Skeleton( const uint n ) : HandleArray( n ), m_graph( n ), m_modelSpace( n ) {}
15 
17  m_pose.clear();
18  m_graph.clear();
19  m_modelSpace.clear();
20 }
21 
22 const Pose& Skeleton::getPose( const SpaceType MODE ) const {
23  static_assert( std::is_same<bool, typename std::underlying_type<SpaceType>::type>::value,
24  "SpaceType is not a boolean" );
25  if ( MODE == SpaceType::LOCAL ) return m_pose;
26  return m_modelSpace;
27 }
28 
29 void Skeleton::setPose( const Pose& pose, const SpaceType MODE ) {
30  CORE_ASSERT( ( size() == pose.size() ), "Size mismatching" );
31  static_assert( std::is_same<bool, typename std::underlying_type<SpaceType>::type>::value,
32  "SpaceType is not a boolean" );
33  if ( MODE == SpaceType::LOCAL ) {
34  m_pose = pose;
35  m_modelSpace.resize( m_pose.size() );
36  for ( uint i = 0; i < m_graph.size(); ++i ) {
37  if ( m_graph.isRoot( i ) ) { m_modelSpace[i] = m_pose[i]; }
38  for ( const auto& child : m_graph.children()[i] ) {
39  m_modelSpace[child] = m_modelSpace[i] * m_pose[child];
40  }
41  }
42  }
43  else {
44  m_modelSpace = pose;
45  m_pose.resize( m_modelSpace.size() );
46  for ( uint i = 0; i < m_graph.size(); ++i ) {
47  if ( m_graph.isRoot( i ) ) { m_pose[i] = m_modelSpace[i]; }
48  for ( const auto& child : m_graph.children()[i] ) {
49  m_pose[child] = m_modelSpace[i].inverse() * m_modelSpace[child];
50  }
51  }
52  }
53 }
54 
55 const Transform& Skeleton::getTransform( const uint i, const SpaceType MODE ) const {
56  CORE_ASSERT( ( i < size() ), "Index i out of bounds" );
57  static_assert( std::is_same<bool, typename std::underlying_type<SpaceType>::type>::value,
58  "SpaceType is not a boolean" );
59  if ( MODE == SpaceType::LOCAL ) return m_pose[i];
60  return m_modelSpace[i];
61 }
62 
63 void Skeleton::setTransform( const uint i, const Transform& T, const SpaceType MODE ) {
64  CORE_ASSERT( ( i < size() ), "Index i out of bounds" );
65  static_assert( std::is_same<bool, typename std::underlying_type<SpaceType>::type>::value,
66  "SpaceType is not a boolean" );
67 
68  switch ( m_manipulation ) {
69  case FORWARD: {
70  // just set the transfrom
71  if ( MODE == SpaceType::LOCAL )
72  setLocalTransform( i, T );
73  else
74  setModelTransform( i, T );
75  } break;
76  case PSEUDO_IK: {
77  Transform modelT = T;
78  if ( MODE == SpaceType::LOCAL ) modelT = ( m_modelSpace[i] * m_pose[i].inverse() * T );
79  // turn bone translation into rotation for parent
80  const int pBoneIdx = m_graph.parents()[i];
81  if ( pBoneIdx != -1 && m_graph.children()[pBoneIdx].size() == 1 ) {
82  const auto& pTBoneModel = m_modelSpace[pBoneIdx];
83 
84  Ra::Core::Vector3 A;
85  Ra::Core::Vector3 B;
86  getBonePoints( pBoneIdx, A, B );
87  Ra::Core::Vector3 B_;
88  B_ = modelT.translation();
89  auto q = Ra::Core::Quaternion::FromTwoVectors( ( B - A ), ( B_ - A ) );
90  Ra::Core::Transform R( q );
91  R.pretranslate( A );
92  R.translate( -A );
93  setModelTransform( pBoneIdx, R * pTBoneModel );
94  }
95  // update bone transform and also children's transform
96  setLocalTransform( i, m_pose[i] * m_modelSpace[i].inverse() * modelT );
97  } break;
98  }
99 }
100 
101 void Skeleton::setLocalTransform( const uint i, const Transform& T ) {
102  m_pose[i] = T;
103  // Compute the model space pose
104  if ( m_graph.isRoot( i ) ) { m_modelSpace[i] = m_pose[i]; }
105  else { m_modelSpace[i] = m_modelSpace[m_graph.parents()[i]] * T; }
106  if ( !m_graph.isLeaf( i ) ) {
107  std::stack<uint> stack;
108  stack.push( i );
109  while ( !stack.empty() ) {
110  uint parent = stack.top();
111  stack.pop();
112  for ( const auto& child : m_graph.children()[parent] ) {
113  m_modelSpace[child] = m_modelSpace[parent] * m_pose[child];
114  stack.push( child );
115  }
116  }
117  }
118 }
119 
120 void Skeleton::setModelTransform( const uint i, const Transform& T ) {
121  m_modelSpace[i] = T;
122  // Compute the local space pose
123  if ( m_graph.isRoot( i ) ) { m_pose[i] = m_modelSpace[i]; }
124  else { m_pose[i] = m_modelSpace[m_graph.parents()[i]].inverse() * T; }
125  if ( !m_graph.isLeaf( i ) ) {
126  std::stack<uint> stack;
127  stack.push( i );
128  while ( !stack.empty() ) {
129  uint parent = stack.top();
130  stack.pop();
131  for ( const auto& child : m_graph.children()[parent] ) {
132  m_pose[child] = m_modelSpace[parent].inverse() * m_modelSpace[child];
133  stack.push( child );
134  }
135  }
136  }
137 }
138 
139 uint Skeleton::addRoot( const Transform& T, const Label label ) {
140  m_pose.push_back( T );
141  m_modelSpace.push_back( T );
142  m_label.push_back( label );
143  return m_graph.addRoot();
144 }
145 
146 uint Skeleton::addBone( const uint parent,
147  const Transform& T,
148  const SpaceType MODE,
149  const Label label ) {
150  static_assert( std::is_same<bool, typename std::underlying_type<SpaceType>::type>::value,
151  "SpaceType is not a boolean" );
152  if ( MODE == SpaceType::LOCAL ) {
153  m_pose.push_back( T );
154  m_modelSpace.push_back( T * m_modelSpace[parent] );
155  }
156  else {
157  m_modelSpace.push_back( T );
158  m_pose.push_back( m_modelSpace[parent].inverse() * T );
159  }
160  m_label.push_back( label );
161  return m_graph.addNode( parent );
162 }
163 
164 void Skeleton::getBonePoints( const uint i, Vector3& startOut, Vector3& endOut ) const {
165  // Check bone index is valid
166  CORE_ASSERT( i < m_modelSpace.size(), "invalid bone index" );
167 
168  startOut = m_modelSpace[i].translation();
169  // A leaf bone has length 0
170  if ( m_graph.isLeaf( i ) ) { endOut = startOut; }
171  else {
172  const auto& children = m_graph.children()[i];
173  CORE_ASSERT( children.size() > 0, "non-leaf bone has no children." );
174  // End point is the average of chidren start points.
175  endOut = Vector3::Zero();
176  for ( auto child : children ) {
177  endOut += m_modelSpace[child].translation();
178  }
179  endOut *= ( 1_ra / children.size() );
180  }
181 }
182 
183 Vector3 Skeleton::projectOnBone( uint boneIdx, const Ra::Core::Vector3& pos ) const {
184  Vector3 start, end;
185  getBonePoints( boneIdx, start, end );
186 
187  auto op = pos - start;
188  auto dir = ( end - start );
189  // Square length of bone
190  const Scalar length_sq = dir.squaredNorm();
191  CORE_ASSERT( length_sq != 0.f, "bone has lenght 0, cannot project." );
192 
193  // Project on the line segment
194  const Scalar t = std::clamp( op.dot( dir ) / length_sq, Scalar( 0 ), Scalar( 1 ) );
195  return start + ( t * dir );
196 }
197 
198 std::ostream& operator<<( std::ostream& os, const Skeleton& skeleton ) {
199  for ( uint i = 0; i < skeleton.size(); ++i ) {
200  const uint id = i;
201  const std::string name = skeleton.getLabel( i );
202  const std::string type = skeleton.m_graph.isRoot( i ) ? "ROOT"
203  : skeleton.m_graph.isJoint( i ) ? "JOINT"
204  : skeleton.m_graph.isBranch( i ) ? "BRANCH"
205  : skeleton.m_graph.isLeaf( i ) ? "LEAF"
206  : "UNKNOWN";
207  const int pid = skeleton.m_graph.parents()[i];
208  const std::string pname =
209  ( pid == -1 ) ? "" : ( "(" + std::to_string( pid ) + ") " + skeleton.getLabel( pid ) );
210 
211  const auto& children = skeleton.m_graph.children()[i];
212 
213  os << "Bone " << id << "\t: " << name << std::endl;
214  os << "Type\t: " << type << std::endl;
215  os << "Parent\t: " << pname << std::endl;
216  os << "Children#\t: " << children.size() << std::endl;
217  os << "Children\t: ";
218  for ( const auto& cid : children ) {
219  const std::string cname = skeleton.getLabel( cid );
220  os << "(" << cid << ") " << cname << " | ";
221  }
222 
223  os << " " << std::endl;
224  os << " " << std::endl;
225  }
226  return os;
227 }
228 
229 } // namespace Animation
230 } // namespace Core
231 } // namespace Ra
void clear()
Clear the vectors.
uint size() const
Return the number of nodes in the graph.
uint addRoot()
Return the index of the added root.
bool isJoint(const uint i) const
Return true if the node is a joint node. ( |child| == 1 )
bool isLeaf(const uint i) const
Return true if the node is a leaf node.
bool isBranch(const uint i) const
Return true if the node is a branch node. ( |child| > 1 )
bool isRoot(const uint i) const
Return true if a node is a root node.
uint addNode(const uint parent)
Return the index of the added leaf.
Label getLabel(const uint i) const
std::vector< Label > m_label
void setModelTransform(uint i, const Transform &T)
Definition: Skeleton.cpp:120
ModelPose m_modelSpace
Skeleton pose in MODEL space.
Definition: Skeleton.hpp:113
uint addRoot(const Transform &T=Transform::Identity(), const Label label="")
Definition: Skeleton.cpp:139
@ FORWARD
Standard edition scheme: rotation and / or translation of one bone.
Definition: Skeleton.hpp:30
@ PSEUDO_IK
Advanced edition scheme: translation of a bone means parent's rotation.
Definition: Skeleton.hpp:31
Vector3 projectOnBone(uint boneIdx, const Vector3 &pos) const
Projects point pos, given in Model Space, onto the bone with index boneIdx.
Definition: Skeleton.cpp:183
void setTransform(const uint i, const Transform &T, const SpaceType MODE) override
Definition: Skeleton.cpp:63
uint size() const override
Definition: Skeleton.hpp:40
void setLocalTransform(uint i, const Transform &T)
Definition: Skeleton.cpp:101
void setPose(const Pose &pose, const SpaceType MODE) override
Definition: Skeleton.cpp:29
AdjacencyList m_graph
The Joint hierarchy.
Definition: Skeleton.hpp:106
void getBonePoints(uint i, Vector3 &startOut, Vector3 &endOut) const
Definition: Skeleton.cpp:164
uint addBone(const uint parent, const Transform &T=Transform::Identity(), const SpaceType MODE=SpaceType::LOCAL, const Label label="")
Definition: Skeleton.cpp:146
Manipulation m_manipulation
The manipulation scheme.
Definition: Skeleton.hpp:109
const Pose & getPose(const SpaceType MODE) const override
Definition: Skeleton.cpp:22
const Transform & getTransform(const uint i, const SpaceType MODE) const override
Definition: Skeleton.cpp:55
Definition: Cage.cpp:3