1 #include <Engine/Scene/SkinningComponent.hpp>
3 #include <Core/Animation/PoseOperation.hpp>
5 #include <Core/Animation/DualQuaternionSkinning.hpp>
6 #include <Core/Animation/HandleWeightOperation.hpp>
7 #include <Core/Animation/LinearBlendSkinning.hpp>
8 #include <Core/Animation/RotationCenterSkinning.hpp>
9 #include <Core/Geometry/DistanceQueries.hpp>
10 #include <Core/Utils/Color.hpp>
11 #include <Core/Utils/Log.hpp>
13 #include <Engine/Data/BlinnPhongMaterial.hpp>
14 #include <Engine/Data/Mesh.hpp>
15 #include <Engine/Data/ShaderConfigFactory.hpp>
16 #include <Engine/Data/Texture.hpp>
17 #include <Engine/RadiumEngine.hpp>
18 #include <Engine/Rendering/RenderObject.hpp>
19 #include <Engine/Rendering/RenderObjectManager.hpp>
20 #include <Engine/Rendering/RenderTechnique.hpp>
22 using namespace Ra::Core;
25 using Geometry::PolyMesh;
26 using Geometry::QuadMesh;
27 using Geometry::TriangleMesh;
29 using namespace Animation;
32 using namespace Utils;
38 static const std::string tangentName =
39 Ra::Core::Geometry::getAttribName( Ra::Core::Geometry::VERTEX_TANGENT );
40 static const std::string bitangentName =
41 Ra::Core::Geometry::getAttribName( Ra::Core::Geometry::VERTEX_BITANGENT );
43 TriangleMesh triangulate(
const PolyMesh& polyMesh ) {
45 res.setVertices( polyMesh.vertices() );
46 res.setNormals( polyMesh.normals() );
47 res.copyAllAttributes( polyMesh );
50 for (
const auto& face : polyMesh.getIndices() ) {
51 if ( face.size() == 3 ) { indices.push_back( face ); }
53 int minus { int( face.size() ) - 1 };
55 while ( plus + 1 < minus ) {
56 if ( ( plus - minus ) % 2 ) {
57 indices.emplace_back( face[plus], face[plus + 1], face[minus] );
61 indices.emplace_back( face[minus], face[plus], face[minus - 1] );
67 res.setIndices( std::move( indices ) );
71 TriangleMesh triangulate(
const QuadMesh& quadMesh ) {
73 res.setVertices( quadMesh.vertices() );
74 res.setNormals( quadMesh.normals() );
75 res.copyAllAttributes( quadMesh );
78 for (
const auto& face : quadMesh.getIndices() ) {
79 indices.emplace_back( face[0], face[1], face[2] );
80 indices.emplace_back( face[0], face[2], face[3] );
82 res.setIndices( std::move( indices ) );
86 void SkinningComponent::initialize() {
87 auto compMsg = ComponentMessenger::getInstance();
89 bool hasSkel = compMsg->canGet<
Skeleton>( getEntity(), m_skelName );
90 bool hasRefPose = compMsg->canGet<RefPose>( getEntity(), m_skelName );
91 bool hasTriMesh = compMsg->canGet<TriangleMesh>( getEntity(), m_meshName );
92 m_meshIsPoly = compMsg->canGet<PolyMesh>( getEntity(), m_meshName );
93 m_meshIsQuad = compMsg->canGet<QuadMesh>( getEntity(), m_meshName );
95 if ( hasSkel && hasRefPose && ( hasTriMesh || m_meshIsPoly || m_meshIsQuad ) ) {
96 m_renderObjectReader = compMsg->getterCallback<Index>( getEntity(), m_meshName );
97 m_skeletonGetter = compMsg->getterCallback<
Skeleton>( getEntity(), m_skelName );
99 m_triMeshWriter = compMsg->rwCallback<TriangleMesh>( getEntity(), m_meshName );
101 else if ( m_meshIsQuad ) {
102 m_quadMeshWriter = compMsg->rwCallback<QuadMesh>( getEntity(), m_meshName );
104 else { m_polyMeshWriter = compMsg->rwCallback<PolyMesh>( getEntity(), m_meshName ); }
107 if ( hasTriMesh ) { m_refData.m_referenceMesh = *m_triMeshWriter(); }
108 else if ( m_meshIsQuad ) { m_refData.m_referenceMesh = triangulate( *m_quadMeshWriter() ); }
109 else { m_refData.m_referenceMesh = triangulate( *m_polyMeshWriter() ); }
111 if ( !m_refData.m_referenceMesh.hasAttrib( tangentName ) &&
112 !m_refData.m_referenceMesh.hasAttrib( bitangentName ) ) {
113 const auto& normals = m_refData.m_referenceMesh.normals();
114 Vector3Array tangents( normals.size() );
115 Vector3Array bitangents( normals.size() );
116 #pragma omp parallel for
117 for (
int i = 0; i < int( normals.size() ); ++i ) {
120 m_refData.m_referenceMesh.addAttrib( tangentName, std::move( tangents ) );
121 m_refData.m_referenceMesh.addAttrib( bitangentName, std::move( bitangents ) );
123 else if ( !m_refData.m_referenceMesh.hasAttrib( tangentName ) ) {
124 const auto& normals = m_refData.m_referenceMesh.normals();
125 const auto& bH = m_refData.m_referenceMesh.getAttribHandle<Vector3>( bitangentName );
126 const auto& bitangents = m_refData.m_referenceMesh.getAttrib( bH ).data();
127 Vector3Array tangents( normals.size() );
128 #pragma omp parallel for
129 for (
int i = 0; i < int( normals.size() ); ++i ) {
130 tangents[i] = bitangents[i].cross( normals[i] );
132 m_refData.m_referenceMesh.addAttrib( tangentName, std::move( tangents ) );
134 else if ( !m_refData.m_referenceMesh.hasAttrib( bitangentName ) ) {
135 const auto& normals = m_refData.m_referenceMesh.normals();
136 const auto& tH = m_refData.m_referenceMesh.getAttribHandle<Vector3>( tangentName );
137 const auto& tangents = m_refData.m_referenceMesh.getAttrib( tH ).data();
138 Vector3Array bitangents( normals.size() );
139 #pragma omp parallel for
140 for (
int i = 0; i < int( normals.size() ); ++i ) {
141 bitangents[i] = normals[i].cross( tangents[i] );
143 m_refData.m_referenceMesh.addAttrib( bitangentName, std::move( bitangents ) );
148 auto ro = getRoMgr()->getRenderObject( *m_renderObjectReader() );
150 m_refData.m_meshTransformInverse = ro->getLocalTransform().inverse();
151 m_refData.m_skeleton = *m_skeletonGetter();
152 createWeightMatrix();
155 m_frameData.m_skeleton = m_refData.m_skeleton;
156 m_frameData.m_currentPosition = m_refData.m_referenceMesh.vertices();
157 m_frameData.m_currentNormal = m_refData.m_referenceMesh.normals();
158 const auto& tH = m_refData.m_referenceMesh.getAttribHandle<Vector3>( tangentName );
159 m_frameData.m_currentTangent = m_refData.m_referenceMesh.getAttrib( tH ).data();
160 const auto& bH = m_refData.m_referenceMesh.getAttribHandle<Vector3>( bitangentName );
161 m_frameData.m_currentBitangent = m_refData.m_referenceMesh.getAttrib( bH ).data();
162 m_frameData.m_frameCounter = 0;
163 m_frameData.m_doSkinning =
true;
164 m_frameData.m_doReset =
false;
168 m_forceUpdate =
true;
171 m_baseMaterial = ro->getMaterial();
174 auto attrUV = Ra::Core::Geometry::getAttribName( Ra::Core::Geometry::VERTEX_TEXCOORD );
176 if ( hasTriMesh ) { geom =
const_cast<TriangleMesh*
>( m_triMeshWriter() ); }
178 if ( m_meshIsPoly ) { geom =
const_cast<PolyMesh*
>( m_polyMeshWriter() ); }
179 else { geom =
const_cast<QuadMesh*
>( m_quadMeshWriter() ); }
183 m_baseUV = geom->
getAttrib( handle ).data();
188 mat->m_kd = Color::Skin();
189 mat->m_ks = Color::White();
193 texParam.
name =
"Engine:Skinning:weights";
194 mat->addTexture( Data::BlinnPhongMaterial::TextureSemantic::TEX_DIFFUSE, texParam );
195 m_weightMaterial.reset( mat );
198 showWeightsType( STANDARD );
202 void SkinningComponent::skin() {
203 CORE_ASSERT( m_isReady,
"Skinning is not setup" );
205 const Skeleton* skel = m_skeletonGetter();
207 bool reset = ComponentMessenger::getInstance()->get<
bool>( getEntity(), m_skelName );
210 if ( reset && !m_frameData.m_doReset ) {
211 m_frameData.m_doReset =
true;
212 m_frameData.m_frameCounter = 0;
213 m_forceUpdate =
true;
215 const auto prevPose = m_frameData.m_skeleton.getPose( SpaceType::MODEL );
216 m_frameData.m_skeleton = *skel;
217 auto currentPose = m_frameData.m_skeleton.
getPose( SpaceType::MODEL );
218 if ( !areEqual( currentPose, prevPose ) || m_forceUpdate ) {
219 m_forceUpdate =
false;
220 m_frameData.m_doSkinning =
true;
221 m_frameData.m_frameCounter++;
223 const auto tH = m_refData.m_referenceMesh.getAttribHandle<Vector3>( tangentName );
224 const Vector3Array& tangents = m_refData.m_referenceMesh.getAttrib( tH ).data();
225 const auto bH = m_refData.m_referenceMesh.getAttribHandle<Vector3>( bitangentName );
226 const Vector3Array& bitangents = m_refData.m_referenceMesh.getAttrib( bH ).data();
228 switch ( m_skinningType ) {
230 dualQuaternionSkinning( m_refData, tangents, bitangents, m_frameData );
234 centerOfRotationSkinning( m_refData, tangents, bitangents, m_frameData );
239 linearBlendSkinning( m_refData, tangents, bitangents, m_frameData );
244 if ( m_normalSkinning == GEOMETRIC ) {
245 m_topoMesh.updatePositions( m_frameData.m_currentPosition );
246 m_topoMesh.updateWedgeNormals();
247 m_topoMesh.updateTriangleMeshNormals( m_frameData.m_currentNormal );
248 #pragma omp parallel for
249 for (
int i = 0; i < int( m_frameData.m_currentNormal.size() ); ++i ) {
251 m_frameData.m_currentTangent[i],
252 m_frameData.m_currentBitangent[i] );
258 void SkinningComponent::endSkinning() {
259 if ( m_frameData.m_doSkinning ) {
261 if ( !m_meshIsPoly ) {
262 if ( !m_meshIsQuad ) { geom =
const_cast<TriangleMesh*
>( m_triMeshWriter() ); }
263 else { geom =
const_cast<QuadMesh*
>( m_quadMeshWriter() ); }
265 else { geom =
const_cast<PolyMesh*
>( m_polyMeshWriter() ); }
267 geom->
setVertices( m_frameData.m_currentPosition );
268 geom->
setNormals( m_frameData.m_currentNormal );
270 if ( handle.idx().isValid() ) {
271 geom->
getAttrib( handle ).setData( m_frameData.m_currentTangent );
274 if ( handle.idx().isValid() ) {
275 geom->
getAttrib( handle ).setData( m_frameData.m_currentBitangent );
278 m_frameData.m_doReset =
false;
279 m_frameData.m_doSkinning =
false;
284 const std::string& meshName ) {
286 m_meshName = meshName;
289 auto it_w = bone.m_weights.find( meshName );
290 if ( it_w != bone.m_weights.end() ) {
291 m_loadedWeights[bone.m_name] = it_w->second;
292 auto it_b = bone.m_bindMatrices.find( meshName );
293 if ( it_b != bone.m_bindMatrices.end() ) {
294 m_loadedBindMatrices[bone.m_name] = it_b->second;
297 LOG( logWARNING ) <<
"Bone " << bone.m_name
298 <<
" has skinning weights but no bind matrix. Using Identity.";
299 m_loadedBindMatrices[bone.m_name] = Transform::Identity();
305 void SkinningComponent::createWeightMatrix() {
306 m_refData.m_bindMatrices.resize( m_refData.m_skeleton.size(), Transform::Identity() );
307 m_refData.m_weights.resize(
int( m_refData.m_referenceMesh.vertices().size() ),
308 m_refData.m_skeleton.size() );
309 std::vector<Eigen::Triplet<Scalar>> triplets;
310 for ( uint col = 0; col < m_refData.m_skeleton.size(); ++col ) {
311 std::string boneName = m_refData.m_skeleton.getLabel( col );
312 auto it = m_loadedWeights.find( boneName );
313 if ( it != m_loadedWeights.end() ) {
314 const auto& W = it->second;
315 for ( uint i = 0; i < W.size(); ++i ) {
316 const auto& w = W[i];
317 int row { int( w.first ) };
318 CORE_ASSERT( row < m_refData.m_weights.rows(),
319 "Weights are incompatible with mesh." );
320 triplets.push_back( { row, int( col ), w.second } );
322 m_refData.m_bindMatrices[col] = m_loadedBindMatrices[boneName];
325 m_refData.m_weights.setFromTriplets( triplets.begin(), triplets.end() );
327 checkWeightMatrix( m_refData.m_weights,
false,
true );
329 if ( normalizeWeights( m_refData.m_weights,
true ) ) {
330 LOG( logINFO ) <<
"Skinning weights have been normalized";
334 void SkinningComponent::setupIO(
const std::string&
id ) {
335 auto compMsg = ComponentMessenger::getInstance();
337 auto refData = std::bind( &SkinningComponent::getSkinningRefData,
this );
338 compMsg->registerOutput<
SkinningRefData>( getEntity(),
this, id, refData );
340 auto frameData = std::bind( &SkinningComponent::getSkinningFrameData,
this );
345 m_skinningType = type;
349 if ( m_skinningType == COR && m_refData.m_CoR.empty() ) { computeCoR( m_refData ); }
350 m_forceUpdate =
true;
355 m_normalSkinning = normalSkinning;
356 if ( m_isReady ) { m_forceUpdate =
true; }
359 const std::string& SkinningComponent::getMeshName()
const {
363 const std::string& SkinningComponent::getSkeletonName()
const {
367 void SkinningComponent::showWeights(
bool on ) {
368 m_showingWeights = on;
369 auto ro = getRoMgr()->getRenderObject( *m_renderObjectReader() );
370 auto attrUV = Ra::Core::Geometry::getAttribName( Ra::Core::Geometry::VERTEX_TEXCOORD );
374 if ( !m_meshIsPoly ) {
375 if ( !m_meshIsQuad ) { geom =
const_cast<TriangleMesh*
>( m_triMeshWriter() ); }
376 else { geom =
const_cast<QuadMesh*
>( m_quadMeshWriter() ); }
378 else { geom =
const_cast<PolyMesh*
>( m_polyMeshWriter() ); }
380 if ( m_showingWeights ) {
382 const auto size = m_frameData.m_currentPosition.size();
383 m_weightsUV.resize( size, Vector3::Zero() );
384 switch ( m_weightType ) {
387 #pragma omp parallel for
388 for (
int i = 0; i < int( size ); ++i ) {
389 m_weightsUV[i][0] = m_refData.m_weights.coeff( i, m_weightBone );
394 ro->setMaterial( m_weightMaterial );
396 ro->getRenderTechnique()->setParametersProvider( m_weightMaterial );
398 handle = geom->
addAttrib<Vector3>( attrUV );
399 geom->
getAttrib( handle ).setData( m_weightsUV );
403 ro->setMaterial( m_baseMaterial );
404 ro->getRenderTechnique()->setParametersProvider( m_baseMaterial );
407 if ( m_baseUV.size() > 0 ) { geom->
getAttrib( handle ).setData( m_baseUV ); }
410 m_forceUpdate =
true;
413 bool SkinningComponent::isShowingWeights() {
414 return m_showingWeights;
419 if ( m_showingWeights ) { showWeights(
true ); }
426 void SkinningComponent::setWeightBone( uint bone ) {
428 if ( m_showingWeights ) { showWeights(
true ); }
const Pose & getPose(const SpaceType MODE) const override
virtual const std::string & getName() const
Acces to the name of the asset.
const Core::AlignedStdVector< HandleComponentData > & getComponentData() const
This class represents vertex + attributes per vertex. Toplogy is handled in MultiIndexedGeometry subc...
Utils::AttribHandle< T > getAttribHandle(const std::string &name) const
void setNormals(PointAttribHandle::Container &&normals)
Set normals.
bool hasAttrib(const std::string &name) const
void setVertices(PointAttribHandle::Container &&vertices)
Set vertices.
void removeAttrib(Utils::AttribHandle< T > &h)
Utils::AttribHandle< T > addAttrib(const std::string &name)
Utils::Attrib< T > & getAttrib(const Utils::AttribHandle< T > &h)
This class implements ContainerIntrospectionInterface for AlignedStdVector.
SkinningType
The Geometric Skinning Method.
WeightType
The skinning weight type.
NormalSkinning
How to skin the normal, tangent and binormal vectors.
void getOrthogonalVectors(const Vector3 &fx, Eigen::Ref< Vector3 > fy, Eigen::Ref< Vector3 > fz)
Skinning data that get set at startup including the "reference state".
std::string name
Name of the texture.