// NeL - MMORPG Framework
// 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 .
#include "std3d.h"
#include "nel/3d/skeleton_shape.h"
#include "nel/3d/skeleton_model.h"
#include "nel/3d/scene.h"
#include "nel/misc/bsphere.h"
using namespace std;
using namespace NLMISC;
namespace NL3D
{
// ***************************************************************************
void CSkeletonShape::CLod::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
{
(void)f.serialVersion(0);
f.serial(Distance);
f.serialCont(ActiveBones);
}
// ***************************************************************************
CSkeletonShape::CSkeletonShape()
{
// By default for now....
// Temp. Have a huge BBox, so clip badly.
_BBox.setCenter(CVector(0,0,1.5));
_BBox.setSize(CVector(3,3,3));
}
// ***************************************************************************
sint32 CSkeletonShape::getBoneIdByName(const std::string &name) const
{
std::map::const_iterator it= _BoneMap.find(name);
if(it==_BoneMap.end())
return -1;
else
return it->second;
}
// ***************************************************************************
void CSkeletonShape::build(const std::vector &bones)
{
uint i;
// copy bones.
_Bones= bones;
// for all bones
for(i=0;i<_Bones.size();i++)
{
// build the map.
_BoneMap[_Bones[i].Name]= i;
// validate distances.
_Bones[i].LodDisableDistance= max(0.f, _Bones[i].LodDisableDistance);
// get fahter dist.
sint32 fatherId= _Bones[i].FatherId;
// if father exist and is not "always enabled"
if(fatherId>=0 && _Bones[fatherId].LodDisableDistance!=0)
{
float fatherDist= _Bones[fatherId].LodDisableDistance;
// I must disable me at least before my father (never after).
if(_Bones[i].LodDisableDistance==0)
_Bones[i].LodDisableDistance= fatherDist;
else
_Bones[i].LodDisableDistance= min(_Bones[i].LodDisableDistance, fatherDist);
}
}
// build Lod Information.
//==============
_Lods.clear();
// build all distances used.
set distSet;
for(i=0;i<_Bones.size();i++)
{
float dist= _Bones[i].LodDisableDistance;
// if lod enabled for this bone, add a new distance, or do nothing
if(dist>0)
distSet.insert(dist);
}
// create a lod for each distance used + 1 (the "dist==0" distance).
_Lods.resize(distSet.size() + 1);
// create the default lod: all bones activated.
_Lods[0].Distance=0;
_Lods[0].ActiveBones.resize(_Bones.size(), 0xFF);
// For each lods not 0th.
set::iterator it= distSet.begin();
for(uint j=1; j<_Lods.size(); j++, it++)
{
float lodDist= *it;
// set the distance of activation
_Lods[j].Distance= lodDist;
// resize and default to all enabled.
_Lods[j].ActiveBones.resize(_Bones.size(), 0xFF);
// Search what lod are to be disabled at this distance.
for(i=0;i<_Bones.size();i++)
{
float dist= _Bones[i].LodDisableDistance;
// if the dist of the lod is greater (or equal) to the disableDist of the bone,
// and if the bone is not "always enabled", disable the bone
if(lodDist>=dist && dist!=0 )
_Lods[j].ActiveBones[i]= 0;
}
}
}
// ***************************************************************************
void CSkeletonShape::retrieve(std::vector &bones) const
{
bones= _Bones;
}
// ***************************************************************************
CTransformShape *CSkeletonShape::createInstance(CScene &scene)
{
// Create a CSkeletonModel, an instance of a mesh.
//===============================================
CSkeletonModel *sm= (CSkeletonModel*)scene.createModel(NL3D::SkeletonModelId);
sm->Shape= this;
// setup bones.
//=================
sm->Bones.reserve(_Bones.size());
for(sint i=0;i<(sint)_Bones.size();i++)
{
// Append a new bone.
sm->Bones.push_back( CBone(&_Bones[i]) );
// Must set the Animatable father of the bone (the skeleton model!).
sm->Bones[i].setFather(sm, CSkeletonModel::OwnerBit);
}
// Must create and init skeleton bone usage to 0.
sm->initBoneUsages();
// For skinning: setup skeleton in Skin LoadBalancing group
sm->setLoadBalancingGroup("Skin");
return sm;
}
// ***************************************************************************
void CSkeletonShape::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
{
/*
Version 1:
- _Lods.
*/
sint ver= f.serialVersion(1);
f.serialCont(_Bones);
f.serialCont(_BoneMap);
if(ver>=1)
f.serialCont(_Lods);
else
{
// create a skeleton shape with bones activated all the time
_Lods.resize(1);
// create the default lod: all bones activated.
_Lods[0].Distance=0;
_Lods[0].ActiveBones.resize(_Bones.size(), 0xFF);
}
}
// ***************************************************************************
float CSkeletonShape::getNumTriangles (float distance)
{
// No polygons
return 0;
}
// ***************************************************************************
bool CSkeletonShape::clip(const std::vector &pyramid, const CMatrix &worldMatrix)
{
// Speed Clip: clip just the sphere.
CBSphere localSphere(_BBox.getCenter(), _BBox.getRadius());
CBSphere worldSphere;
// transform the sphere in WorldMatrix (with nearly good scale info).
localSphere.applyTransform(worldMatrix, worldSphere);
// if out of only plane, entirely out.
for(sint i=0;i<(sint)pyramid.size();i++)
{
// We are sure that pyramid has normalized plane normals.
// if SpherMax OUT return false.
float d= pyramid[i]*worldSphere.Center;
if(d>worldSphere.Radius)
return false;
}
return true;
}
// ***************************************************************************
void CSkeletonShape::getAABBox(NLMISC::CAABBox &bbox) const
{
bbox= _BBox;
}
// ***************************************************************************
uint CSkeletonShape::getLodForDistance(float dist) const
{
uint start=0;
uint end= _Lods.size();
// find lower_bound by dichotomy
while(end-1>start)
{
uint pivot= (end+start)/2;
// return the lower_bound, ie return first start with _Lods[pivot].Distance<=dist
if(_Lods[pivot].Distance <= dist)
start= pivot;
else
end= pivot;
}
return start;
}
} // NL3D