#include "IrrCompileConfig.h"
#ifdef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_
#include "CSkinnedMesh.h"
#include "CBoneSceneNode.h"
#include "IAnimatedMeshSceneNode.h"
#include "os.h"
namespace irr
{
namespace scene
{
CSkinnedMesh::CSkinnedMesh()
: SkinningBuffers(0), AnimationFrames(0.f), FramesPerSecond(25.f),
LastAnimatedFrame(0.f), InterpolationMode(EIM_LINEAR),
HasAnimation(false), PreparedForSkinning(false),
AnimateNormals(true), HardwareSkinning(false)
{
#ifdef _DEBUG
setDebugName("CSkinnedMesh");
#endif
SkinningBuffers=&LocalBuffers;
}
CSkinnedMesh::~CSkinnedMesh()
{
for (u32 i=0; i<AllJoints.size(); ++i)
delete AllJoints[i];
for (u32 j=0; j<LocalBuffers.size(); ++j)
{
if (LocalBuffers[j])
LocalBuffers[j]->drop();
}
}
u32 CSkinnedMesh::getFrameCount() const
{
return core::floor32(AnimationFrames);
}
f32 CSkinnedMesh::getAnimationSpeed() const
{
return FramesPerSecond;
}
void CSkinnedMesh::setAnimationSpeed(f32 fps)
{
FramesPerSecond=fps;
}
IMesh* CSkinnedMesh::getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop)
{
if (frame==-1)
return this;
animateMesh((f32)frame, 1.0f);
skinMesh();
return this;
}
void CSkinnedMesh::animateMesh(f32 frame, f32 blend)
{
if ( !HasAnimation || LastAnimatedFrame==frame)
return;
LastAnimatedFrame=frame;
if (blend<=0.f)
return;
for (u32 i=0; i<AllJoints.size(); ++i)
{
SJoint *joint = AllJoints[i];
const core::vector3df oldPosition = joint->Animatedposition;
const core::vector3df oldScale = joint->Animatedscale;
const core::quaternion oldRotation = joint->Animatedrotation;
core::vector3df position = oldPosition;
core::vector3df scale = oldScale;
core::quaternion rotation = oldRotation;
getFrameData(frame, joint,
position, joint->positionHint,
scale, joint->scaleHint,
rotation, joint->rotationHint);
if (blend==1.0f)
{
joint->Animatedposition = position;
joint->Animatedscale = scale;
joint->Animatedrotation = rotation;
}
else
{
joint->Animatedposition = core::lerp(oldPosition, position, blend);
joint->Animatedscale = core::lerp(oldScale, scale, blend);
joint->Animatedrotation.slerp(oldRotation, rotation, blend);
}
}
buildAllLocalAnimatedMatrices();
updateBoundingBox();
}
void CSkinnedMesh::buildAllLocalAnimatedMatrices()
{
for (u32 i=0; i<AllJoints.size(); ++i)
{
SJoint *joint = AllJoints[i];
if (joint->UseAnimationFrom &&
(joint->UseAnimationFrom->PositionKeys.size() ||
joint->UseAnimationFrom->ScaleKeys.size() ||
joint->UseAnimationFrom->RotationKeys.size() ))
{
joint->LocalAnimatedMatrix=joint->Animatedrotation.getMatrix();
f32 *m1 = joint->LocalAnimatedMatrix.pointer();
core::vector3df &Pos = joint->Animatedposition;
m1[0] += Pos.X*m1[3];
m1[1] += Pos.Y*m1[3];
m1[2] += Pos.Z*m1[3];
m1[4] += Pos.X*m1[7];
m1[5] += Pos.Y*m1[7];
m1[6] += Pos.Z*m1[7];
m1[8] += Pos.X*m1[11];
m1[9] += Pos.Y*m1[11];
m1[10] += Pos.Z*m1[11];
m1[12] += Pos.X*m1[15];
m1[13] += Pos.Y*m1[15];
m1[14] += Pos.Z*m1[15];
joint->GlobalSkinningSpace=false;
if (joint->ScaleKeys.size())
{
core::matrix4& mat = joint->LocalAnimatedMatrix;
mat[0] *= joint->Animatedscale.X;
mat[1] *= joint->Animatedscale.X;
mat[2] *= joint->Animatedscale.X;
mat[3] *= joint->Animatedscale.X;
mat[4] *= joint->Animatedscale.Y;
mat[5] *= joint->Animatedscale.Y;
mat[6] *= joint->Animatedscale.Y;
mat[7] *= joint->Animatedscale.Y;
mat[8] *= joint->Animatedscale.Z;
mat[9] *= joint->Animatedscale.Z;
mat[10] *= joint->Animatedscale.Z;
mat[11] *= joint->Animatedscale.Z;
}
}
else
{
joint->LocalAnimatedMatrix=joint->LocalMatrix;
}
}
}
void CSkinnedMesh::buildAllGlobalAnimatedMatrices(SJoint *joint, SJoint *parentJoint)
{
if (!joint)
{
for (u32 i=0; i<RootJoints.size(); ++i)
buildAllGlobalAnimatedMatrices(RootJoints[i], 0);
return;
}
else
{
if (!parentJoint || joint->GlobalSkinningSpace)
joint->GlobalAnimatedMatrix = joint->LocalAnimatedMatrix;
else
joint->GlobalAnimatedMatrix = parentJoint->GlobalAnimatedMatrix * joint->LocalAnimatedMatrix;
}
for (u32 j=0; j<joint->Children.size(); ++j)
buildAllGlobalAnimatedMatrices(joint->Children[j], joint);
}
void CSkinnedMesh::getFrameData(f32 frame, SJoint *joint,
core::vector3df &position, s32 &positionHint,
core::vector3df &scale, s32 &scaleHint,
core::quaternion &rotation, s32 &rotationHint)
{
s32 foundPositionIndex = -1;
s32 foundScaleIndex = -1;
s32 foundRotationIndex = -1;
if (joint->UseAnimationFrom)
{
const core::array<SPositionKey> &PositionKeys=joint->UseAnimationFrom->PositionKeys;
const core::array<SScaleKey> &ScaleKeys=joint->UseAnimationFrom->ScaleKeys;
const core::array<SRotationKey> &RotationKeys=joint->UseAnimationFrom->RotationKeys;
if (PositionKeys.size())
{
foundPositionIndex = -1;
if (positionHint>=0 && (u32)positionHint < PositionKeys.size())
{
if (positionHint>0 && PositionKeys[positionHint].frame>=frame && PositionKeys[positionHint-1].frame<frame )
foundPositionIndex=positionHint;
else if (positionHint+1 < (s32)PositionKeys.size())
{
if ( PositionKeys[positionHint+1].frame>=frame &&
PositionKeys[positionHint+0].frame<frame)
{
positionHint++;
foundPositionIndex=positionHint;
}
}
}
if (foundPositionIndex==-1)
{
for (u32 i=0; i<PositionKeys.size(); ++i)
{
if (PositionKeys[i].frame >= frame)
{
foundPositionIndex=i;
positionHint=i;
break;
}
}
}
if (foundPositionIndex!=-1)
{
if (InterpolationMode==EIM_CONSTANT || foundPositionIndex==0)
{
position = PositionKeys[foundPositionIndex].position;
}
else if (InterpolationMode==EIM_LINEAR)
{
const SPositionKey& KeyA = PositionKeys[foundPositionIndex];
const SPositionKey& KeyB = PositionKeys[foundPositionIndex-1];
const f32 fd1 = frame - KeyA.frame;
const f32 fd2 = KeyB.frame - frame;
position = ((KeyB.position-KeyA.position)/(fd1+fd2))*fd1 + KeyA.position;
}
}
}
if (ScaleKeys.size())
{
foundScaleIndex = -1;
if (scaleHint>=0 && (u32)scaleHint < ScaleKeys.size())
{
if (scaleHint>0 && ScaleKeys[scaleHint].frame>=frame && ScaleKeys[scaleHint-1].frame<frame )
foundScaleIndex=scaleHint;
else if (scaleHint+1 < (s32)ScaleKeys.size())
{
if ( ScaleKeys[scaleHint+1].frame>=frame &&
ScaleKeys[scaleHint+0].frame<frame)
{
scaleHint++;
foundScaleIndex=scaleHint;
}
}
}
if (foundScaleIndex==-1)
{
for (u32 i=0; i<ScaleKeys.size(); ++i)
{
if (ScaleKeys[i].frame >= frame)
{
foundScaleIndex=i;
scaleHint=i;
break;
}
}
}
if (foundScaleIndex!=-1)
{
if (InterpolationMode==EIM_CONSTANT || foundScaleIndex==0)
{
scale = ScaleKeys[foundScaleIndex].scale;
}
else if (InterpolationMode==EIM_LINEAR)
{
const SScaleKey& KeyA = ScaleKeys[foundScaleIndex];
const SScaleKey& KeyB = ScaleKeys[foundScaleIndex-1];
const f32 fd1 = frame - KeyA.frame;
const f32 fd2 = KeyB.frame - frame;
scale = ((KeyB.scale-KeyA.scale)/(fd1+fd2))*fd1 + KeyA.scale;
}
}
}
if (RotationKeys.size())
{
foundRotationIndex = -1;
if (rotationHint>=0 && (u32)rotationHint < RotationKeys.size())
{
if (rotationHint>0 && RotationKeys[rotationHint].frame>=frame && RotationKeys[rotationHint-1].frame<frame )
foundRotationIndex=rotationHint;
else if (rotationHint+1 < (s32)RotationKeys.size())
{
if ( RotationKeys[rotationHint+1].frame>=frame &&
RotationKeys[rotationHint+0].frame<frame)
{
rotationHint++;
foundRotationIndex=rotationHint;
}
}
}
if (foundRotationIndex==-1)
{
for (u32 i=0; i<RotationKeys.size(); ++i)
{
if (RotationKeys[i].frame >= frame)
{
foundRotationIndex=i;
rotationHint=i;
break;
}
}
}
if (foundRotationIndex!=-1)
{
if (InterpolationMode==EIM_CONSTANT || foundRotationIndex==0)
{
rotation = RotationKeys[foundRotationIndex].rotation;
}
else if (InterpolationMode==EIM_LINEAR)
{
const SRotationKey& KeyA = RotationKeys[foundRotationIndex];
const SRotationKey& KeyB = RotationKeys[foundRotationIndex-1];
const f32 fd1 = frame - KeyA.frame;
const f32 fd2 = KeyB.frame - frame;
const f32 t = fd1/(fd1+fd2);
rotation.slerp(KeyA.rotation, KeyB.rotation, t);
}
}
}
}
}
void CSkinnedMesh::skinMesh()
{
if (!HasAnimation)
return;
buildAllGlobalAnimatedMatrices();
if (!HardwareSkinning)
{
u32 i;
for (i=0; i<AllJoints.size(); ++i)
{
for (u32 j=0; j<AllJoints[i]->AttachedMeshes.size(); ++j)
{
SSkinMeshBuffer* Buffer=(*SkinningBuffers)[ AllJoints[i]->AttachedMeshes[j] ];
Buffer->Transformation=AllJoints[i]->GlobalAnimatedMatrix;
}
}
for (i=0; i<Vertices_Moved.size(); ++i)
for (u32 j=0; j<Vertices_Moved[i].size(); ++j)
Vertices_Moved[i][j]=false;
for (i=0; i<RootJoints.size(); ++i)
skinJoint(RootJoints[i], 0);
for (i=0; i<SkinningBuffers->size(); ++i)
(*SkinningBuffers)[i]->setDirty(EBT_VERTEX);
}
updateBoundingBox();
}
void CSkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint)
{
if (joint->Weights.size())
{
core::matrix4 jointVertexPull(core::matrix4::EM4CONST_NOTHING);
jointVertexPull.setbyproduct(joint->GlobalAnimatedMatrix, joint->GlobalInversedMatrix);
core::vector3df thisVertexMove, thisNormalMove;
core::array<scene::SSkinMeshBuffer*> &buffersUsed=*SkinningBuffers;
for (u32 i=0; i<joint->Weights.size(); ++i)
{
SWeight& weight = joint->Weights[i];
jointVertexPull.transformVect(thisVertexMove, weight.StaticPos);
if (AnimateNormals)
jointVertexPull.rotateVect(thisNormalMove, weight.StaticNormal);
if (! (*(weight.Moved)) )
{
*(weight.Moved) = true;
buffersUsed[weight.buffer_id]->getVertex(weight.vertex_id)->Pos = thisVertexMove * weight.strength;
if (AnimateNormals)
buffersUsed[weight.buffer_id]->getVertex(weight.vertex_id)->Normal = thisNormalMove * weight.strength;
}
else
{
buffersUsed[weight.buffer_id]->getVertex(weight.vertex_id)->Pos += thisVertexMove * weight.strength;
if (AnimateNormals)
buffersUsed[weight.buffer_id]->getVertex(weight.vertex_id)->Normal += thisNormalMove * weight.strength;
}
buffersUsed[weight.buffer_id]->boundingBoxNeedsRecalculated();
}
}
for (u32 j=0; j<joint->Children.size(); ++j)
skinJoint(joint->Children[j], joint);
}
E_ANIMATED_MESH_TYPE CSkinnedMesh::getMeshType() const
{
return EAMT_SKINNED;
}
u32 CSkinnedMesh::getJointCount() const
{
return AllJoints.size();
}
const c8* CSkinnedMesh::getJointName(u32 number) const
{
if (number >= AllJoints.size())
return 0;
return AllJoints[number]->Name.c_str();
}
s32 CSkinnedMesh::getJointNumber(const c8* name) const
{
for (u32 i=0; i<AllJoints.size(); ++i)
{
if (AllJoints[i]->Name == name)
return i;
}
return -1;
}
u32 CSkinnedMesh::getMeshBufferCount() const
{
return LocalBuffers.size();
}
IMeshBuffer* CSkinnedMesh::getMeshBuffer(u32 nr) const
{
if (nr < LocalBuffers.size())
return LocalBuffers[nr];
else
return 0;
}
IMeshBuffer* CSkinnedMesh::getMeshBuffer(const video::SMaterial &material) const
{
for (u32 i=0; i<LocalBuffers.size(); ++i)
{
if (LocalBuffers[i]->getMaterial() == material)
return LocalBuffers[i];
}
return 0;
}
const core::aabbox3d<f32>& CSkinnedMesh::getBoundingBox() const
{
return BoundingBox;
}
void CSkinnedMesh::setBoundingBox( const core::aabbox3df& box)
{
BoundingBox = box;
}
void CSkinnedMesh::setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue)
{
for (u32 i=0; i<LocalBuffers.size(); ++i)
LocalBuffers[i]->Material.setFlag(flag,newvalue);
}
void CSkinnedMesh::setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint,
E_BUFFER_TYPE buffer)
{
for (u32 i=0; i<LocalBuffers.size(); ++i)
LocalBuffers[i]->setHardwareMappingHint(newMappingHint, buffer);
}
void CSkinnedMesh::setDirty(E_BUFFER_TYPE buffer)
{
for (u32 i=0; i<LocalBuffers.size(); ++i)
LocalBuffers[i]->setDirty(buffer);
}
bool CSkinnedMesh::useAnimationFrom(const ISkinnedMesh *mesh)
{
bool unmatched=false;
for(u32 i=0;i<AllJoints.size();++i)
{
SJoint *joint=AllJoints[i];
joint->UseAnimationFrom=0;
if (joint->Name=="")
unmatched=true;
else
{
for(u32 j=0;j<mesh->getAllJoints().size();++j)
{
SJoint *otherJoint=mesh->getAllJoints()[j];
if (joint->Name==otherJoint->Name)
{
joint->UseAnimationFrom=otherJoint;
}
}
if (!joint->UseAnimationFrom)
unmatched=true;
}
}
checkForAnimation();
return !unmatched;
}
void CSkinnedMesh::updateNormalsWhenAnimating(bool on)
{
AnimateNormals = on;
}
void CSkinnedMesh::setInterpolationMode(E_INTERPOLATION_MODE mode)
{
InterpolationMode = mode;
}
core::array<scene::SSkinMeshBuffer*> &CSkinnedMesh::getMeshBuffers()
{
return LocalBuffers;
}
core::array<CSkinnedMesh::SJoint*> &CSkinnedMesh::getAllJoints()
{
return AllJoints;
}
const core::array<CSkinnedMesh::SJoint*> &CSkinnedMesh::getAllJoints() const
{
return AllJoints;
}
bool CSkinnedMesh::setHardwareSkinning(bool on)
{
if (HardwareSkinning!=on)
{
if (on)
{
for (u32 i=0; i<AllJoints.size(); ++i)
{
SJoint *joint=AllJoints[i];
for (u32 j=0; j<joint->Weights.size(); ++j)
{
const u16 buffer_id=joint->Weights[j].buffer_id;
const u32 vertex_id=joint->Weights[j].vertex_id;
LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos = joint->Weights[j].StaticPos;
LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal = joint->Weights[j].StaticNormal;
LocalBuffers[buffer_id]->boundingBoxNeedsRecalculated();
}
}
}
HardwareSkinning=on;
}
return HardwareSkinning;
}
void CSkinnedMesh::calculateGlobalMatrices(SJoint *joint,SJoint *parentJoint)
{
if (!joint && parentJoint)
return;
if (!joint)
{
for (u32 i=0; i<RootJoints.size(); ++i)
calculateGlobalMatrices(RootJoints[i],0);
return;
}
if (!parentJoint)
joint->GlobalMatrix = joint->LocalMatrix;
else
joint->GlobalMatrix = parentJoint->GlobalMatrix * joint->LocalMatrix;
joint->LocalAnimatedMatrix=joint->LocalMatrix;
joint->GlobalAnimatedMatrix=joint->GlobalMatrix;
if (joint->GlobalInversedMatrix.isIdentity())
{
joint->GlobalInversedMatrix = joint->GlobalMatrix;
joint->GlobalInversedMatrix.makeInverse();
}
for (u32 j=0; j<joint->Children.size(); ++j)
calculateGlobalMatrices(joint->Children[j],joint);
}
void CSkinnedMesh::checkForAnimation()
{
u32 i,j;
HasAnimation = false;
for(i=0;i<AllJoints.size();++i)
{
if (AllJoints[i]->UseAnimationFrom)
{
if (AllJoints[i]->UseAnimationFrom->PositionKeys.size() ||
AllJoints[i]->UseAnimationFrom->ScaleKeys.size() ||
AllJoints[i]->UseAnimationFrom->RotationKeys.size() )
{
HasAnimation = true;
}
}
}
if (!HasAnimation)
{
for(i=0;i<AllJoints.size();++i)
{
if (AllJoints[i]->Weights.size())
HasAnimation = true;
}
}
if (HasAnimation)
{
AnimationFrames=0;
for(i=0;i<AllJoints.size();++i)
{
if (AllJoints[i]->UseAnimationFrom)
{
if (AllJoints[i]->UseAnimationFrom->PositionKeys.size())
if (AllJoints[i]->UseAnimationFrom->PositionKeys.getLast().frame > AnimationFrames)
AnimationFrames=AllJoints[i]->UseAnimationFrom->PositionKeys.getLast().frame;
if (AllJoints[i]->UseAnimationFrom->ScaleKeys.size())
if (AllJoints[i]->UseAnimationFrom->ScaleKeys.getLast().frame > AnimationFrames)
AnimationFrames=AllJoints[i]->UseAnimationFrom->ScaleKeys.getLast().frame;
if (AllJoints[i]->UseAnimationFrom->RotationKeys.size())
if (AllJoints[i]->UseAnimationFrom->RotationKeys.getLast().frame > AnimationFrames)
AnimationFrames=AllJoints[i]->UseAnimationFrom->RotationKeys.getLast().frame;
}
}
}
if (HasAnimation && !PreparedForSkinning)
{
PreparedForSkinning=true;
for(i=0; i < AllJoints.size(); ++i)
{
SJoint *joint = AllJoints[i];
for (j=0; j<joint->Weights.size(); ++j)
{
const u16 buffer_id=joint->Weights[j].buffer_id;
const u32 vertex_id=joint->Weights[j].vertex_id;
if (buffer_id>=LocalBuffers.size())
{
os::Printer::log("Skinned Mesh: Weight buffer id too large", ELL_WARNING);
joint->Weights[j].buffer_id = joint->Weights[j].vertex_id =0;
}
else if (vertex_id>=LocalBuffers[buffer_id]->getVertexCount())
{
os::Printer::log("Skinned Mesh: Weight vertex id too large", ELL_WARNING);
joint->Weights[j].buffer_id = joint->Weights[j].vertex_id =0;
}
}
}
for (i=0; i<Vertices_Moved.size(); ++i)
for (j=0; j<Vertices_Moved[i].size(); ++j)
Vertices_Moved[i][j] = false;
for (i=0; i<AllJoints.size(); ++i)
{
SJoint *joint = AllJoints[i];
for (j=0; j<joint->Weights.size(); ++j)
{
const u16 buffer_id=joint->Weights[j].buffer_id;
const u32 vertex_id=joint->Weights[j].vertex_id;
joint->Weights[j].Moved = &Vertices_Moved[buffer_id] [vertex_id];
joint->Weights[j].StaticPos = LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos;
joint->Weights[j].StaticNormal = LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal;
}
}
normalizeWeights();
}
}
void CSkinnedMesh::finalize()
{
u32 i;
LastAnimatedFrame=-1;
for (i=0; i<LocalBuffers.size(); ++i)
{
LocalBuffers[i]->recalculateBoundingBox();
}
if (AllJoints.size() || RootJoints.size())
{
if (!RootJoints.size())
{
for(u32 CheckingIdx=0; CheckingIdx < AllJoints.size(); ++CheckingIdx)
{
bool foundParent=false;
for(i=0; i < AllJoints.size(); ++i)
{
for(u32 n=0; n < AllJoints[i]->Children.size(); ++n)
{
if (AllJoints[i]->Children[n] == AllJoints[CheckingIdx])
foundParent=true;
}
}
if (!foundParent)
RootJoints.push_back(AllJoints[CheckingIdx]);
}
}
else
{
AllJoints=RootJoints;
}
}
for(i=0; i < AllJoints.size(); ++i)
{
AllJoints[i]->UseAnimationFrom=AllJoints[i];
}
for (i=0; i<LocalBuffers.size(); ++i)
{
Vertices_Moved.push_back( core::array<bool>() );
Vertices_Moved[i].set_used(LocalBuffers[i]->getVertexCount());
}
checkForAnimation();
if (HasAnimation)
{
for(i=0;i<AllJoints.size();++i)
{
core::array<SPositionKey> &PositionKeys =AllJoints[i]->PositionKeys;
core::array<SScaleKey> &ScaleKeys = AllJoints[i]->ScaleKeys;
core::array<SRotationKey> &RotationKeys = AllJoints[i]->RotationKeys;
if (PositionKeys.size()>2)
{
for(u32 j=0;j<PositionKeys.size()-2;++j)
{
if (PositionKeys[j].position == PositionKeys[j+1].position && PositionKeys[j+1].position == PositionKeys[j+2].position)
{
PositionKeys.erase(j+1);
--j;
}
}
}
if (PositionKeys.size()>1)
{
for(u32 j=0;j<PositionKeys.size()-1;++j)
{
if (PositionKeys[j].frame >= PositionKeys[j+1].frame)
{
PositionKeys.erase(j+1);
--j;
}
}
}
if (ScaleKeys.size()>2)
{
for(u32 j=0;j<ScaleKeys.size()-2;++j)
{
if (ScaleKeys[j].scale == ScaleKeys[j+1].scale && ScaleKeys[j+1].scale == ScaleKeys[j+2].scale)
{
ScaleKeys.erase(j+1);
--j;
}
}
}
if (ScaleKeys.size()>1)
{
for(u32 j=0;j<ScaleKeys.size()-1;++j)
{
if (ScaleKeys[j].frame >= ScaleKeys[j+1].frame)
{
ScaleKeys.erase(j+1);
--j;
}
}
}
if (RotationKeys.size()>2)
{
for(u32 j=0;j<RotationKeys.size()-2;++j)
{
if (RotationKeys[j].rotation == RotationKeys[j+1].rotation && RotationKeys[j+1].rotation == RotationKeys[j+2].rotation)
{
RotationKeys.erase(j+1);
--j;
}
}
}
if (RotationKeys.size()>1)
{
for(u32 j=0;j<RotationKeys.size()-1;++j)
{
if (RotationKeys[j].frame >= RotationKeys[j+1].frame)
{
RotationKeys.erase(j+1);
--j;
}
}
}
if (PositionKeys.size())
{
SPositionKey *Key;
Key=&PositionKeys[0];
if (Key->frame!=0)
{
PositionKeys.push_front(*Key);
Key=&PositionKeys[0];
Key->frame=0;
}
Key=&PositionKeys.getLast();
if (Key->frame!=AnimationFrames)
{
PositionKeys.push_back(*Key);
Key=&PositionKeys.getLast();
Key->frame=AnimationFrames;
}
}
if (ScaleKeys.size())
{
SScaleKey *Key;
Key=&ScaleKeys[0];
if (Key->frame!=0)
{
ScaleKeys.push_front(*Key);
Key=&ScaleKeys[0];
Key->frame=0;
}
Key=&ScaleKeys.getLast();
if (Key->frame!=AnimationFrames)
{
ScaleKeys.push_back(*Key);
Key=&ScaleKeys.getLast();
Key->frame=AnimationFrames;
}
}
if (RotationKeys.size())
{
SRotationKey *Key;
Key=&RotationKeys[0];
if (Key->frame!=0)
{
RotationKeys.push_front(*Key);
Key=&RotationKeys[0];
Key->frame=0;
}
Key=&RotationKeys.getLast();
if (Key->frame!=AnimationFrames)
{
RotationKeys.push_back(*Key);
Key=&RotationKeys.getLast();
Key->frame=AnimationFrames;
}
}
}
}
calculateGlobalMatrices(0,0);
for (i=0; i<AllJoints.size(); ++i)
{
for (u32 j=0; j<AllJoints[i]->AttachedMeshes.size(); ++j)
{
SSkinMeshBuffer* Buffer=(*SkinningBuffers)[ AllJoints[i]->AttachedMeshes[j] ];
Buffer->Transformation=AllJoints[i]->GlobalAnimatedMatrix;
}
}
if (LocalBuffers.empty())
BoundingBox.reset(0,0,0);
else
{
irr::core::aabbox3df bb(LocalBuffers[0]->BoundingBox);
LocalBuffers[0]->Transformation.transformBoxEx(bb);
BoundingBox.reset(bb);
for (u32 j=1; j<LocalBuffers.size(); ++j)
{
bb = LocalBuffers[j]->BoundingBox;
LocalBuffers[j]->Transformation.transformBoxEx(bb);
BoundingBox.addInternalBox(bb);
}
}
}
void CSkinnedMesh::updateBoundingBox(void)
{
if(!SkinningBuffers)
return;
core::array<SSkinMeshBuffer*> & buffer = *SkinningBuffers;
BoundingBox.reset(0,0,0);
if (!buffer.empty())
{
for (u32 j=0; j<buffer.size(); ++j)
{
buffer[j]->recalculateBoundingBox();
core::aabbox3df bb = buffer[j]->BoundingBox;
buffer[j]->Transformation.transformBoxEx(bb);
BoundingBox.addInternalBox(bb);
}
}
}
scene::SSkinMeshBuffer *CSkinnedMesh::addMeshBuffer()
{
scene::SSkinMeshBuffer *buffer=new scene::SSkinMeshBuffer();
LocalBuffers.push_back(buffer);
return buffer;
}
CSkinnedMesh::SJoint *CSkinnedMesh::addJoint(SJoint *parent)
{
SJoint *joint=new SJoint;
AllJoints.push_back(joint);
if (!parent)
{
}
else
{
parent->Children.push_back(joint);
}
return joint;
}
CSkinnedMesh::SPositionKey *CSkinnedMesh::addPositionKey(SJoint *joint)
{
if (!joint)
return 0;
joint->PositionKeys.push_back(SPositionKey());
return &joint->PositionKeys.getLast();
}
CSkinnedMesh::SScaleKey *CSkinnedMesh::addScaleKey(SJoint *joint)
{
if (!joint)
return 0;
joint->ScaleKeys.push_back(SScaleKey());
return &joint->ScaleKeys.getLast();
}
CSkinnedMesh::SRotationKey *CSkinnedMesh::addRotationKey(SJoint *joint)
{
if (!joint)
return 0;
joint->RotationKeys.push_back(SRotationKey());
return &joint->RotationKeys.getLast();
}
CSkinnedMesh::SWeight *CSkinnedMesh::addWeight(SJoint *joint)
{
if (!joint)
return 0;
joint->Weights.push_back(SWeight());
return &joint->Weights.getLast();
}
bool CSkinnedMesh::isStatic()
{
return !HasAnimation;
}
void CSkinnedMesh::normalizeWeights()
{
u32 i,j;
core::array< core::array<f32> > verticesTotalWeight;
verticesTotalWeight.reallocate(LocalBuffers.size());
for (i=0; i<LocalBuffers.size(); ++i)
{
verticesTotalWeight.push_back(core::array<f32>());
verticesTotalWeight[i].set_used(LocalBuffers[i]->getVertexCount());
}
for (i=0; i<verticesTotalWeight.size(); ++i)
for (j=0; j<verticesTotalWeight[i].size(); ++j)
verticesTotalWeight[i][j] = 0;
for (i=0; i<AllJoints.size(); ++i)
{
SJoint *joint=AllJoints[i];
for (j=0; j<joint->Weights.size(); ++j)
{
if (joint->Weights[j].strength<=0)
{
joint->Weights.erase(j);
--j;
}
else
{
verticesTotalWeight[joint->Weights[j].buffer_id] [joint->Weights[j].vertex_id] += joint->Weights[j].strength;
}
}
}
for (i=0; i<AllJoints.size(); ++i)
{
SJoint *joint=AllJoints[i];
for (j=0; j< joint->Weights.size(); ++j)
{
const f32 total = verticesTotalWeight[joint->Weights[j].buffer_id] [joint->Weights[j].vertex_id];
if (total != 0 && total != 1)
joint->Weights[j].strength /= total;
}
}
}
void CSkinnedMesh::recoverJointsFromMesh(core::array<IBoneSceneNode*> &jointChildSceneNodes)
{
for (u32 i=0; i<AllJoints.size(); ++i)
{
IBoneSceneNode* node=jointChildSceneNodes[i];
SJoint *joint=AllJoints[i];
node->setPosition(joint->LocalAnimatedMatrix.getTranslation());
node->setRotation(joint->LocalAnimatedMatrix.getRotationDegrees());
node->setScale(joint->LocalAnimatedMatrix.getScale());
node->positionHint=joint->positionHint;
node->scaleHint=joint->scaleHint;
node->rotationHint=joint->rotationHint;
node->updateAbsolutePosition();
}
}
void CSkinnedMesh::transferJointsToMesh(const core::array<IBoneSceneNode*> &jointChildSceneNodes)
{
for (u32 i=0; i<AllJoints.size(); ++i)
{
const IBoneSceneNode* const node=jointChildSceneNodes[i];
SJoint *joint=AllJoints[i];
joint->LocalAnimatedMatrix.setRotationDegrees(node->getRotation());
joint->LocalAnimatedMatrix.setTranslation(node->getPosition());
joint->LocalAnimatedMatrix *= core::matrix4().setScale(node->getScale());
joint->positionHint=node->positionHint;
joint->scaleHint=node->scaleHint;
joint->rotationHint=node->rotationHint;
joint->GlobalSkinningSpace=(node->getSkinningSpace()==EBSS_GLOBAL);
}
LastAnimatedFrame=-1;
}
void CSkinnedMesh::transferOnlyJointsHintsToMesh(const core::array<IBoneSceneNode*> &jointChildSceneNodes)
{
for (u32 i=0; i<AllJoints.size(); ++i)
{
const IBoneSceneNode* const node=jointChildSceneNodes[i];
SJoint *joint=AllJoints[i];
joint->positionHint=node->positionHint;
joint->scaleHint=node->scaleHint;
joint->rotationHint=node->rotationHint;
}
}
void CSkinnedMesh::addJoints(core::array<IBoneSceneNode*> &jointChildSceneNodes,
IAnimatedMeshSceneNode* node, ISceneManager* smgr)
{
for (u32 i=0; i<AllJoints.size(); ++i)
{
jointChildSceneNodes.push_back(new CBoneSceneNode(0, smgr, 0, i, AllJoints[i]->Name.c_str()));
}
for (u32 i=0; i<jointChildSceneNodes.size(); ++i)
{
const SJoint* const joint=AllJoints[i];
s32 parentID=-1;
for (u32 j=0;(parentID==-1)&&(j<AllJoints.size());++j)
{
if (i!=j)
{
const SJoint* const parentTest=AllJoints[j];
for (u32 n=0; n<parentTest->Children.size(); ++n)
{
if (parentTest->Children[n]==joint)
{
parentID=j;
break;
}
}
}
}
IBoneSceneNode* bone=jointChildSceneNodes[i];
if (parentID!=-1)
bone->setParent(jointChildSceneNodes[parentID]);
else
bone->setParent(node);
bone->drop();
}
}
void CSkinnedMesh::convertMeshToTangents()
{
for (u32 b=0; b < LocalBuffers.size(); ++b)
{
if (LocalBuffers[b])
{
LocalBuffers[b]->convertToTangents();
const s32 idxCnt = LocalBuffers[b]->getIndexCount();
u16* idx = LocalBuffers[b]->getIndices();
video::S3DVertexTangents* v =
(video::S3DVertexTangents*)LocalBuffers[b]->getVertices();
for (s32 i=0; i<idxCnt; i+=3)
{
calculateTangents(
v[idx[i+0]].Normal,
v[idx[i+0]].Tangent,
v[idx[i+0]].Binormal,
v[idx[i+0]].Pos,
v[idx[i+1]].Pos,
v[idx[i+2]].Pos,
v[idx[i+0]].TCoords,
v[idx[i+1]].TCoords,
v[idx[i+2]].TCoords);
calculateTangents(
v[idx[i+1]].Normal,
v[idx[i+1]].Tangent,
v[idx[i+1]].Binormal,
v[idx[i+1]].Pos,
v[idx[i+2]].Pos,
v[idx[i+0]].Pos,
v[idx[i+1]].TCoords,
v[idx[i+2]].TCoords,
v[idx[i+0]].TCoords);
calculateTangents(
v[idx[i+2]].Normal,
v[idx[i+2]].Tangent,
v[idx[i+2]].Binormal,
v[idx[i+2]].Pos,
v[idx[i+0]].Pos,
v[idx[i+1]].Pos,
v[idx[i+2]].TCoords,
v[idx[i+0]].TCoords,
v[idx[i+1]].TCoords);
}
}
}
}
void CSkinnedMesh::calculateTangents(
core::vector3df& normal,
core::vector3df& tangent,
core::vector3df& binormal,
core::vector3df& vt1, core::vector3df& vt2, core::vector3df& vt3,
core::vector2df& tc1, core::vector2df& tc2, core::vector2df& tc3)
{
core::vector3df v1 = vt1 - vt2;
core::vector3df v2 = vt3 - vt1;
normal = v2.crossProduct(v1);
normal.normalize();
f32 deltaX1 = tc1.X - tc2.X;
f32 deltaX2 = tc3.X - tc1.X;
binormal = (v1 * deltaX2) - (v2 * deltaX1);
binormal.normalize();
f32 deltaY1 = tc1.Y - tc2.Y;
f32 deltaY2 = tc3.Y - tc1.Y;
tangent = (v1 * deltaY2) - (v2 * deltaY1);
tangent.normalize();
core::vector3df txb = tangent.crossProduct(binormal);
if (txb.dotProduct(normal) < 0.0f)
{
tangent *= -1.0f;
binormal *= -1.0f;
}
}
}
}
#endif