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>
25using Geometry::PolyMesh;
26using Geometry::QuadMesh;
27using Geometry::TriangleMesh;
29using namespace Animation;
39 Ra::Core::Geometry::getAttribName( Ra::Core::Geometry::VERTEX_TANGENT );
41 Ra::Core::Geometry::getAttribName( Ra::Core::Geometry::VERTEX_BITANGENT );
43TriangleMesh 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] );
71TriangleMesh 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() ) {
87 auto compMsg = ComponentMessenger::getInstance();
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 );
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 ); }
108 else if ( m_meshIsQuad ) { m_refData.
m_referenceMesh = triangulate( *m_quadMeshWriter() ); }
109 else { m_refData.
m_referenceMesh = triangulate( *m_polyMeshWriter() ); }
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 ) {
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] );
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] );
148 auto ro =
getRoMgr()->getRenderObject( *m_renderObjectReader() );
152 createWeightMatrix();
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->setDiffuseColor( Color::Skin() );
189 mat->setSpecularColor( Color::White() );
193 texParam.name =
"Engine:Skinning:weights";
194 mat->addTexture( Data::BlinnPhongMaterial::TextureSemantic::TEX_DIFFUSE, texParam );
195 m_weightMaterial.reset( mat );
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 );
213 m_forceUpdate =
true;
218 if ( !areEqual( currentPose, prevPose ) || m_forceUpdate ) {
219 m_forceUpdate =
false;
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 );
246 m_topoMesh.updateWedgeNormals();
248#pragma omp parallel for
249 for (
int i = 0; i < int( m_frameData.
m_currentNormal.size() ); ++i ) {
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() ); }
270 if ( handle.idx().isValid() ) {
274 if ( handle.idx().isValid() ) {
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();
305void SkinningComponent::createWeightMatrix() {
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 } );
327 checkWeightMatrix( m_refData.
m_weights,
false,
true );
329 if ( normalizeWeights( m_refData.
m_weights,
true ) ) {
330 LOG( logINFO ) <<
"Skinning weights have been normalized";
334void SkinningComponent::setupIO(
const std::string&
id ) {
335 auto compMsg = ComponentMessenger::getInstance();
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; }
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 ) {
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;
414 return m_showingWeights;
Label getLabel(const uint i) const
uint size() const override
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.
const NormalAttribHandle::Container & normals() const
Access the vertices normals.
void removeAttrib(Utils::AttribHandle< T > &h)
const PointAttribHandle::Container & vertices() const
Access the vertices positions.
Utils::AttribHandle< T > addAttrib(const std::string &name)
Utils::Attrib< T > & getAttrib(const Utils::AttribHandle< T > &h)
An attrib handle basically store an Index and a name.
This class implements ContainerIntrospectionInterface for AlignedStdVector.
virtual Entity * getEntity() const
Return the entity the component belongs to.
static Rendering::RenderObjectManager * getRoMgr()
Shortcut to access the render object manager.
void endSkinning()
Update internal data and update the skinned mesh.
void setWeightBone(uint bone)
Set the bone to show the weights of.
void handleSkinDataLoading(const Core::Asset::HandleData *data, const std::string &meshName)
SkinningType
The Geometric Skinning Method.
@ COR
Center of Rotation skinning.
@ DQS
Dual Quaternion Skinning.
@ LBS
Linear Blend Skinning.
WeightType getWeightsType()
Returns the type of skinning weights displayed.
const Core::Animation::SkinningRefData * getSkinningRefData() const
Returns the reference skinning data.
const Core::Animation::SkinningFrameData * getSkinningFrameData() const
Returns the current Pose data.
const std::string & getSkeletonName() const
Returns the name of the skeleton skinning the mesh.
void skin()
Apply the Skinning Method and update the SkinningFrameData.
WeightType
The skinning weight type.
@ STANDARD
Standard geometric skinning weights.
void setSkinningType(SkinningType type)
Sets the Skinning method to use.
void setNormalSkinning(NormalSkinning normalSkinning)
Sets the method to use to skin the normal, tangent and binormal vectors.
const std::string & getMeshName() const
Returns the name of the skinned mesh.
void showWeights(bool on)
Toggles display of skinning weights.
void showWeightsType(WeightType type)
Set the type of skinning weight to display.
virtual void initialize() override
Pure virtual method to be overridden by any component. When this method is called you are guaranteed ...
bool isShowingWeights()
Returns whether the skinning weights are displayed or not.
NormalSkinning
How to skin the normal, tangent and binormal vectors.
@ GEOMETRIC
Recompute from scratch from the skinned positions.
T emplace_back(T... args)
void getOrthogonalVectors(const Vector3 &fx, Eigen::Ref< Vector3 > fy, Eigen::Ref< Vector3 > fz)
This namespace contains everything "low level", related to data, datastuctures, and computation.
hepler function to manage enum as underlying types in VariableSet
bool m_doSkinning
Whether skinning must be processed for the current frame.
Vector3Array m_currentTangent
The current mesh vertex tangent vectors.
uint m_frameCounter
The number of the current frame.
Vector3Array m_currentBitangent
The current mesh vertex bitangent vectors.
Skeleton m_skeleton
The animation skeleton in the current pose.
Vector3Array m_currentNormal
The current mesh vertex normals.
Vector3Array m_currentPosition
The current mesh vertex position.
bool m_doReset
Whether the skin must be reset to its initial reference configuration.
Skinning data that get set at startup including the "reference state".
Transform m_meshTransformInverse
The inverse of the mesh's transform.
Vector3Array m_CoR
The optionnal centers of rotations for CoR skinning.
Skeleton m_skeleton
The animation skeleton saved in rest pose.
WeightMatrix m_weights
The matrix of skinning weights.
AlignedStdVector< Transform > m_bindMatrices
The per-bone bind matrices.
Geometry::TriangleMesh m_referenceMesh
The mesh in reference position.
Describes the sampler and image of a texture.