khanat-opennel-code/code/nel/src/3d/lod_character_builder.cpp

228 lines
6.9 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "std3d.h"
#include "nel/misc/debug.h"
#include "nel/3d/lod_character_builder.h"
#include "nel/3d/scene.h"
#include "nel/3d/skeleton_shape.h"
#include "nel/3d/mesh.h"
#include "nel/3d/skeleton_model.h"
using namespace std;
using namespace NLMISC;
namespace NL3D
{
// ***************************************************************************
CLodCharacterBuilder::CLodCharacterBuilder()
{
_SkeletonShape= NULL;
_LodBuild= NULL;
_TmpScene= NULL;
}
// ***************************************************************************
CLodCharacterBuilder::~CLodCharacterBuilder()
{
// release the scene
if(_TmpScene)
{
_TmpScene->release();
delete _TmpScene;
_TmpScene= NULL;
}
}
// ***************************************************************************
void CLodCharacterBuilder::setShape(const std::string &name, CSkeletonShape *skeletonShape, CLodCharacterShapeBuild *lodBuild)
{
nlassert(skeletonShape);
nlassert(lodBuild);
// SmartPtr the skeleton Shape (NB: important because skeletonModel use it)
_SkeletonShape= skeletonShape;
// a std ptr.
_LodBuild= lodBuild;
// Remap bone, with help of lodBuild and skeleton names.
_BoneRemap.resize(lodBuild->BonesNames.size());
for(uint i=0; i<_BoneRemap.size(); i++)
{
const std::string &boneName= lodBuild->BonesNames[i];
sint32 boneId= _SkeletonShape->getBoneIdByName(boneName);
// If not found
if(boneId<0)
{
nlwarning("Not found a bone in the skeleton Shape: %s", boneName.c_str());
// use root bone.
_BoneRemap[i]= 0;
}
else
// remap
_BoneRemap[i]= boneId;
}
// build basics
_LodCharacterShape.buildMesh(name, *_LodBuild);
// Build a scene, for addAnim purpose
if(!_TmpScene)
{
_TmpScene= new CScene(false);
// Must init Statics for scene (because use it in addAnim). NB: never mind if done twice.
CScene::registerBasics();
// init default Roots.
_TmpScene->initDefaultRoots();
// Don't Set driver/viewport
// init QuadGridClipManager
_TmpScene->initQuadGridClipManager ();
}
}
// ***************************************************************************
void CLodCharacterBuilder::addAnim(const char *animName, CAnimation *animation, float frameRate)
{
nlassert(frameRate>0);
nlassert(animation);
/* Create a Scene, a skeletonModel, an animation set, and a channel mixer to play the animation
NB: no render is made and no driver is created. The scene is just here for correct creation of the skeleton
Yoyo: This is a tricky way, but I found it the easier one...
*/
// Create Components necesssary to play the animation
//==========================
// create an animationSet, and a channelMixer.
//--------------
// build an animation set with the only one animation. This animation will be deleted with the animationSet
CAnimationSet *tmpAnimationSet= new CAnimationSet;
tmpAnimationSet->addAnimation(animName, animation);
tmpAnimationSet->build();
// Build a channelMixer.
CChannelMixer *tmpChannelMixer= new CChannelMixer;
tmpChannelMixer->setAnimationSet(tmpAnimationSet);
// create a skeleton Model for animation
//---------------
CSkeletonModel *skeleton= (CSkeletonModel*)_SkeletonShape->createInstance(*_TmpScene);
// and skeleton it with animation
skeleton->registerToChannelMixer(tmpChannelMixer, "");
// activate the anim
uint animID = tmpAnimationSet->getAnimationIdByName(animName);
nlassert(animID != CAnimationSet::NotFound);
tmpChannelMixer->setSlotAnimation(0, animID);
// Build Dst Animation basics.
//--------------
CLodCharacterShape::CAnimBuild dstAnim;
dstAnim.Name= animName;
dstAnim.AnimLength= animation->getEndTime();
dstAnim.NumKeys= (uint)ceil(dstAnim.AnimLength * frameRate);
dstAnim.NumKeys= max(1U, dstAnim.NumKeys);
// resize array.
dstAnim.Keys.resize(_LodCharacterShape.getNumVertices() * dstAnim.NumKeys);
// Bake the animation
//==========================
double time=0;
double dt= 1.0/(double)frameRate;
uint64 evalDetaiDate= 0;
for(uint i=0; i<dstAnim.NumKeys; i++, time+= dt)
{
// clamp the time
time= min(time, (double)dstAnim.AnimLength);
// setup the channelMixer time
tmpChannelMixer->setSlotTime(0, (float)time);
// Eval the channelMixer, both global and detail
tmpChannelMixer->eval(false);
tmpChannelMixer->eval(true, evalDetaiDate++);
// Use the skeleton model to compute bone skin matrix, supposing an identity skeleton worldMatrix
skeleton->computeAllBones(CMatrix::Identity);
// apply the skinning from the current skeleton state
applySkin(skeleton, &dstAnim.Keys[i*_LodCharacterShape.getNumVertices()]);
}
// Add the animation to the lod
//==========================
_LodCharacterShape.addAnim(dstAnim);
// Delete
//==========================
// release the skeleton
_TmpScene->deleteModel(skeleton);
// delete the channelMixer
delete tmpChannelMixer;
// delete the animationSet
delete tmpAnimationSet;
}
// ***************************************************************************
void CLodCharacterBuilder::applySkin(CSkeletonModel *skeleton, CVector *dstVertices)
{
uint numVerts= (uint)_LodBuild->Vertices.size();
// for all vertices.
for(uint i=0; i<numVerts; i++)
{
CMesh::CSkinWeight &skinWgt= _LodBuild->SkinWeights[i];
CVector &srcVert= _LodBuild->Vertices[i];
CVector &dstVert= dstVertices[i];
dstVert= CVector::Null;
// parse all Weights, and add influence.
for(uint j=0; j<NL3D_MESH_SKINNING_MAX_MATRIX; j++)
{
float wgt= skinWgt.Weights[j];
if(wgt==0)
{
// this should not happen, at least weight 0 should have an influence.
if(j==0)
dstVert= srcVert;
// no more influence for this vertex.
break;
}
else
{
// Get the skeleton bone to read.
uint boneId= _BoneRemap[skinWgt.MatrixId[j]];
// Get the computed matrix from the skeleton.
const CMatrix &boneMat= skeleton->Bones[boneId].getBoneSkinMatrix();
// Add the influence of this bone.
dstVert+= (boneMat * srcVert) * wgt;
}
}
}
}
} // NL3D