1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "nel/misc/debug.h"
20 #include "nel/3d/lod_character_builder.h"
21 #include "nel/3d/scene.h"
22 #include "nel/3d/skeleton_shape.h"
23 #include "nel/3d/mesh.h"
24 #include "nel/3d/skeleton_model.h"
28 using namespace NLMISC
;
38 // ***************************************************************************
39 CLodCharacterBuilder::CLodCharacterBuilder()
45 // ***************************************************************************
46 CLodCharacterBuilder::~CLodCharacterBuilder()
57 // ***************************************************************************
58 void CLodCharacterBuilder::setShape(const std::string
&name
, CSkeletonShape
*skeletonShape
, CLodCharacterShapeBuild
*lodBuild
)
60 nlassert(skeletonShape
);
63 // SmartPtr the skeleton Shape (NB: important because skeletonModel use it)
64 _SkeletonShape
= skeletonShape
;
68 // Remap bone, with help of lodBuild and skeleton names.
69 _BoneRemap
.resize(lodBuild
->BonesNames
.size());
70 for(uint i
=0; i
<_BoneRemap
.size(); i
++)
72 const std::string
&boneName
= lodBuild
->BonesNames
[i
];
73 sint32 boneId
= _SkeletonShape
->getBoneIdByName(boneName
);
77 nlwarning("Not found a bone in the skeleton Shape: %s", boneName
.c_str());
83 _BoneRemap
[i
]= boneId
;
87 _LodCharacterShape
.buildMesh(name
, *_LodBuild
);
89 // Build a scene, for addAnim purpose
92 _TmpScene
= new CScene(false);
93 // Must init Statics for scene (because use it in addAnim). NB: never mind if done twice.
94 CScene::registerBasics();
95 // init default Roots.
96 _TmpScene
->initDefaultRoots();
97 // Don't Set driver/viewport
98 // init QuadGridClipManager
99 _TmpScene
->initQuadGridClipManager ();
104 // ***************************************************************************
105 void CLodCharacterBuilder::addAnim(const char *animName
, CAnimation
*animation
, float frameRate
)
107 nlassert(frameRate
>0);
110 /* Create a Scene, a skeletonModel, an animation set, and a channel mixer to play the animation
111 NB: no render is made and no driver is created. The scene is just here for correct creation of the skeleton
112 Yoyo: This is a tricky way, but I found it the easier one...
115 // Create Components necesssary to play the animation
116 //==========================
118 // create an animationSet, and a channelMixer.
120 // build an animation set with the only one animation. This animation will be deleted with the animationSet
121 CAnimationSet
*tmpAnimationSet
= new CAnimationSet
;
122 tmpAnimationSet
->addAnimation(animName
, animation
);
123 tmpAnimationSet
->build();
124 // Build a channelMixer.
125 CChannelMixer
*tmpChannelMixer
= new CChannelMixer
;
126 tmpChannelMixer
->setAnimationSet(tmpAnimationSet
);
129 // create a skeleton Model for animation
131 CSkeletonModel
*skeleton
= (CSkeletonModel
*)_SkeletonShape
->createInstance(*_TmpScene
);
132 // and skeleton it with animation
133 skeleton
->registerToChannelMixer(tmpChannelMixer
, "");
135 uint animID
= tmpAnimationSet
->getAnimationIdByName(animName
);
136 nlassert(animID
!= CAnimationSet::NotFound
);
137 tmpChannelMixer
->setSlotAnimation(0, animID
);
140 // Build Dst Animation basics.
142 CLodCharacterShape::CAnimBuild dstAnim
;
143 dstAnim
.Name
= animName
;
144 dstAnim
.AnimLength
= animation
->getEndTime();
145 dstAnim
.NumKeys
= (uint
)ceil(dstAnim
.AnimLength
* frameRate
);
146 dstAnim
.NumKeys
= max(1U, dstAnim
.NumKeys
);
148 dstAnim
.Keys
.resize(_LodCharacterShape
.getNumVertices() * dstAnim
.NumKeys
);
151 // Bake the animation
152 //==========================
154 double dt
= 1.0/(double)frameRate
;
155 uint64 evalDetaiDate
= 0;
156 for(uint i
=0; i
<dstAnim
.NumKeys
; i
++, time
+= dt
)
159 time
= min(time
, (double)dstAnim
.AnimLength
);
161 // setup the channelMixer time
162 tmpChannelMixer
->setSlotTime(0, (float)time
);
164 // Eval the channelMixer, both global and detail
165 tmpChannelMixer
->eval(false);
166 tmpChannelMixer
->eval(true, evalDetaiDate
++);
168 // Use the skeleton model to compute bone skin matrix, supposing an identity skeleton worldMatrix
169 skeleton
->computeAllBones(CMatrix::Identity
);
171 // apply the skinning from the current skeleton state
172 applySkin(skeleton
, &dstAnim
.Keys
[i
*_LodCharacterShape
.getNumVertices()]);
176 // Add the animation to the lod
177 //==========================
178 _LodCharacterShape
.addAnim(dstAnim
);
182 //==========================
183 // release the skeleton
184 _TmpScene
->deleteModel(skeleton
);
185 // delete the channelMixer
186 delete tmpChannelMixer
;
187 // delete the animationSet
188 delete tmpAnimationSet
;
192 // ***************************************************************************
193 void CLodCharacterBuilder::applySkin(CSkeletonModel
*skeleton
, CVector
*dstVertices
)
195 uint numVerts
= (uint
)_LodBuild
->Vertices
.size();
198 for(uint i
=0; i
<numVerts
; i
++)
200 CMesh::CSkinWeight
&skinWgt
= _LodBuild
->SkinWeights
[i
];
201 CVector
&srcVert
= _LodBuild
->Vertices
[i
];
202 CVector
&dstVert
= dstVertices
[i
];
203 dstVert
= CVector::Null
;
204 // parse all Weights, and add influence.
205 for(uint j
=0; j
<NL3D_MESH_SKINNING_MAX_MATRIX
; j
++)
207 float wgt
= skinWgt
.Weights
[j
];
211 // this should not happen, at least weight 0 should have an influence.
214 // no more influence for this vertex.
219 // Get the skeleton bone to read.
220 uint boneId
= _BoneRemap
[skinWgt
.MatrixId
[j
]];
221 // Get the computed matrix from the skeleton.
222 const CMatrix
&boneMat
= skeleton
->Bones
[boneId
].getBoneSkinMatrix();
223 // Add the influence of this bone.
224 dstVert
+= (boneMat
* srcVert
) * wgt
;