Merge branch 'ryzom/ark-features' into main/gingo-test
[ryzomcore.git] / nel / src / 3d / lod_character_builder.cpp
blob05eef0b30b3e502ee17f081e613f2bf3f78ba51b
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
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.
8 //
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/>.
17 #include "std3d.h"
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"
27 using namespace std;
28 using namespace NLMISC;
30 #ifdef DEBUG_NEW
31 #define new DEBUG_NEW
32 #endif
34 namespace NL3D
38 // ***************************************************************************
39 CLodCharacterBuilder::CLodCharacterBuilder()
41 _SkeletonShape= NULL;
42 _LodBuild= NULL;
43 _TmpScene= NULL;
45 // ***************************************************************************
46 CLodCharacterBuilder::~CLodCharacterBuilder()
48 // release the scene
49 if(_TmpScene)
51 _TmpScene->release();
52 delete _TmpScene;
53 _TmpScene= NULL;
57 // ***************************************************************************
58 void CLodCharacterBuilder::setShape(const std::string &name, CSkeletonShape *skeletonShape, CLodCharacterShapeBuild *lodBuild)
60 nlassert(skeletonShape);
61 nlassert(lodBuild);
63 // SmartPtr the skeleton Shape (NB: important because skeletonModel use it)
64 _SkeletonShape= skeletonShape;
65 // a std ptr.
66 _LodBuild= lodBuild;
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);
74 // If not found
75 if(boneId<0)
77 nlwarning("Not found a bone in the skeleton Shape: %s", boneName.c_str());
78 // use root bone.
79 _BoneRemap[i]= 0;
81 else
82 // remap
83 _BoneRemap[i]= boneId;
86 // build basics
87 _LodCharacterShape.buildMesh(name, *_LodBuild);
89 // Build a scene, for addAnim purpose
90 if(!_TmpScene)
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);
108 nlassert(animation);
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.
119 //--------------
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
130 //---------------
131 CSkeletonModel *skeleton= (CSkeletonModel*)_SkeletonShape->createInstance(*_TmpScene);
132 // and skeleton it with animation
133 skeleton->registerToChannelMixer(tmpChannelMixer, "");
134 // activate the anim
135 uint animID = tmpAnimationSet->getAnimationIdByName(animName);
136 nlassert(animID != CAnimationSet::NotFound);
137 tmpChannelMixer->setSlotAnimation(0, animID);
140 // Build Dst Animation basics.
141 //--------------
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);
147 // resize array.
148 dstAnim.Keys.resize(_LodCharacterShape.getNumVertices() * dstAnim.NumKeys);
151 // Bake the animation
152 //==========================
153 double time=0;
154 double dt= 1.0/(double)frameRate;
155 uint64 evalDetaiDate= 0;
156 for(uint i=0; i<dstAnim.NumKeys; i++, time+= dt)
158 // clamp the time
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);
181 // Delete
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();
197 // for all vertices.
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];
209 if(wgt==0)
211 // this should not happen, at least weight 0 should have an influence.
212 if(j==0)
213 dstVert= srcVert;
214 // no more influence for this vertex.
215 break;
217 else
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;
231 } // NL3D