Radium Engine  1.5.20
Loading...
Searching...
No Matches
Skeleton.cpp
1#include <Core/Animation/Skeleton.hpp>
2#include <Core/Math/LinearAlgebra.hpp> // Math::clamp
3
4#include <algorithm>
5#include <stack>
6
7namespace Ra {
8namespace Core {
9namespace Animation {
10
12Skeleton::Skeleton() : HandleArray(), m_graph(), m_modelSpace() {}
13
14Skeleton::Skeleton( const uint n ) : HandleArray( n ), m_graph( n ), m_modelSpace( n ) {}
15
21
22const Pose& Skeleton::getPose( const SpaceType MODE ) const {
24 "SpaceType is not a boolean" );
25 if ( MODE == SpaceType::LOCAL ) return m_pose;
26 return m_modelSpace;
27}
28
29void Skeleton::setPose( const Pose& pose, const SpaceType MODE ) {
30 CORE_ASSERT( ( size() == pose.size() ), "Size mismatching" );
32 "SpaceType is not a boolean" );
33 if ( MODE == SpaceType::LOCAL ) {
34 m_pose = pose;
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;
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
55const Transform& Skeleton::getTransform( const uint i, const SpaceType MODE ) const {
56 CORE_ASSERT( ( i < size() ), "Index i out of bounds" );
58 "SpaceType is not a boolean" );
59 if ( MODE == SpaceType::LOCAL ) return m_pose[i];
60 return m_modelSpace[i];
61}
62
63void Skeleton::setTransform( const uint i, const Transform& T, const SpaceType MODE ) {
64 CORE_ASSERT( ( i < size() ), "Index i out of bounds" );
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 default:
99 // nothing can be done, error
100 break;
101 }
102}
103
104void Skeleton::setLocalTransform( const uint i, const Transform& T ) {
105 m_pose[i] = T;
106 // Compute the model space pose
107 if ( m_graph.isRoot( i ) ) { m_modelSpace[i] = m_pose[i]; }
108 else { m_modelSpace[i] = m_modelSpace[m_graph.parents()[i]] * T; }
109 if ( !m_graph.isLeaf( i ) ) {
110 std::stack<uint> stack;
111 stack.push( i );
112 while ( !stack.empty() ) {
113 uint parent = stack.top();
114 stack.pop();
115 for ( const auto& child : m_graph.children()[parent] ) {
116 m_modelSpace[child] = m_modelSpace[parent] * m_pose[child];
117 stack.push( child );
118 }
119 }
120 }
121}
122
123void Skeleton::setModelTransform( const uint i, const Transform& T ) {
124 m_modelSpace[i] = T;
125 // Compute the local space pose
126 if ( m_graph.isRoot( i ) ) { m_pose[i] = m_modelSpace[i]; }
127 else { m_pose[i] = m_modelSpace[m_graph.parents()[i]].inverse() * T; }
128 if ( !m_graph.isLeaf( i ) ) {
129 std::stack<uint> stack;
130 stack.push( i );
131 while ( !stack.empty() ) {
132 uint parent = stack.top();
133 stack.pop();
134 for ( const auto& child : m_graph.children()[parent] ) {
135 m_pose[child] = m_modelSpace[parent].inverse() * m_modelSpace[child];
136 stack.push( child );
137 }
138 }
139 }
140}
141
142uint Skeleton::addRoot( const Transform& T, const Label label ) {
143 m_pose.push_back( T );
145 m_label.push_back( label );
146 return m_graph.addRoot();
147}
148
149uint Skeleton::addBone( const uint parent,
150 const Transform& T,
151 const SpaceType MODE,
152 const Label label ) {
154 "SpaceType is not a boolean" );
155 if ( MODE == SpaceType::LOCAL ) {
156 m_pose.push_back( T );
157 m_modelSpace.push_back( T * m_modelSpace[parent] );
158 }
159 else {
161 m_pose.push_back( m_modelSpace[parent].inverse() * T );
162 }
163 m_label.push_back( label );
164 return m_graph.addNode( parent );
165}
166
167void Skeleton::getBonePoints( const uint i, Vector3& startOut, Vector3& endOut ) const {
168 // Check bone index is valid
169 CORE_ASSERT( i < m_modelSpace.size(), "invalid bone index" );
170
171 startOut = m_modelSpace[i].translation();
172 // A leaf bone has length 0
173 if ( m_graph.isLeaf( i ) ) { endOut = startOut; }
174 else {
175 const auto& children = m_graph.children()[i];
176 CORE_ASSERT( children.size() > 0, "non-leaf bone has no children." );
177 // End point is the average of chidren start points.
178 endOut = Vector3::Zero();
179 for ( auto child : children ) {
180 endOut += m_modelSpace[child].translation();
181 }
182 endOut *= ( 1_ra / children.size() );
183 }
184}
185
186Vector3 Skeleton::projectOnBone( uint boneIdx, const Ra::Core::Vector3& pos ) const {
187 Vector3 start, end;
188 getBonePoints( boneIdx, start, end );
189
190 auto op = pos - start;
191 auto dir = ( end - start );
192 // Square length of bone
193 const Scalar length_sq = dir.squaredNorm();
194 CORE_ASSERT( length_sq != 0.f, "bone has lenght 0, cannot project." );
195
196 // Project on the line segment
197 const Scalar t = std::clamp( op.dot( dir ) / length_sq, Scalar( 0 ), Scalar( 1 ) );
198 return start + ( t * dir );
199}
200
201std::ostream& operator<<( std::ostream& os, const Skeleton& skeleton ) {
202 for ( uint i = 0; i < skeleton.size(); ++i ) {
203 const uint id = i;
204 const std::string name = skeleton.getLabel( i );
205 const std::string type = skeleton.m_graph.isRoot( i ) ? "ROOT"
206 : skeleton.m_graph.isJoint( i ) ? "JOINT"
207 : skeleton.m_graph.isBranch( i ) ? "BRANCH"
208 : skeleton.m_graph.isLeaf( i ) ? "LEAF"
209 : "UNKNOWN";
210 const int pid = skeleton.m_graph.parents()[i];
211 const std::string pname =
212 ( pid == -1 ) ? "" : ( "(" + std::to_string( pid ) + ") " + skeleton.getLabel( pid ) );
213
214 const auto& children = skeleton.m_graph.children()[i];
215
216 os << "Bone " << id << "\t: " << name << std::endl;
217 os << "Type\t: " << type << std::endl;
218 os << "Parent\t: " << pname << std::endl;
219 os << "Children#\t: " << children.size() << std::endl;
220 os << "Children\t: ";
221 for ( const auto& cid : children ) {
222 const std::string cname = skeleton.getLabel( cid );
223 os << "(" << cid << ") " << cname << " | ";
224 }
225
226 os << " " << std::endl;
227 os << " " << std::endl;
228 }
229 return os;
230}
231
232} // namespace Animation
233} // namespace Core
234} // 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
void setModelTransform(uint i, const Transform &T)
Definition Skeleton.cpp:123
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:142
@ FORWARD
Standard editing scheme: rotation and / or translation of one bone.
Definition Skeleton.hpp:30
@ PSEUDO_IK
Advanced editing 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:186
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:104
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:167
uint addBone(const uint parent, const Transform &T=Transform::Identity(), const SpaceType MODE=SpaceType::LOCAL, const Label label="")
Definition Skeleton.cpp:149
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
T clear(T... args)
T empty(T... args)
T endl(T... args)
hepler function to manage enum as underlying types in VariableSet
Definition Cage.cpp:3
T pop(T... args)
T push_back(T... args)
T push(T... args)
T resize(T... args)
T size(T... args)
T to_string(T... args)
T top(T... args)