702 lines
20 KiB
C++
702 lines
20 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/3d/lod_character_shape.h"
|
|
#include "nel/misc/vectord.h"
|
|
#include "nel/misc/fast_floor.h"
|
|
#include "nel/3d/lod_character_texture.h"
|
|
#include "nel/misc/triangle.h"
|
|
#include "nel/misc/polygon.h"
|
|
#include "nel/misc/hierarchical_timer.h"
|
|
|
|
|
|
using namespace std;
|
|
using namespace NLMISC;
|
|
|
|
|
|
namespace NL3D
|
|
{
|
|
|
|
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
// CLodCharacterShape
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
|
|
|
|
// ***************************************************************************
|
|
const CLodCharacterShapeBuild::CPixelInfo CLodCharacterShapeBuild::CPixelInfo::EmptyPixel(
|
|
CVector::Null, CVector(1000,0,0));
|
|
|
|
|
|
// ***************************************************************************
|
|
CLodCharacterShapeBuild::CLodCharacterShapeBuild()
|
|
{
|
|
_Width= 0;
|
|
_Height= 0;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CLodCharacterShapeBuild::compile(const std::vector<bool> &triangleSelection, uint textureOverSample)
|
|
{
|
|
nlassert(UVs.size()==Vertices.size());
|
|
nlassert(Normals.size()==Vertices.size());
|
|
|
|
// take the sqrtf.
|
|
textureOverSample= (uint)sqrtf((float)textureOverSample);
|
|
textureOverSample= max(textureOverSample, 1U);
|
|
|
|
_Width= NL3D_CLOD_TEXT_WIDTH;
|
|
_Height= NL3D_CLOD_TEXT_HEIGHT;
|
|
|
|
uint wOver= _Width*textureOverSample;
|
|
uint hOver= _Height*textureOverSample;
|
|
std::vector<CPixelInfo> overTextureInfo;
|
|
|
|
// do some tri selection?
|
|
bool triSelectOk= triangleSelection.size()==TriangleIndices.size()/3;
|
|
|
|
|
|
// **** reset the texture and init with "empty" pixel (ie normal.x==1000, which is not possible because normal.norm()==1)
|
|
_TextureInfo.resize(_Width*_Height);
|
|
fill(_TextureInfo.begin(), _TextureInfo.end(), CPixelInfo::EmptyPixel);
|
|
// do it to the oversampled texture
|
|
overTextureInfo.resize(wOver*hOver, CPixelInfo::EmptyPixel);
|
|
|
|
|
|
// **** For each triangle in the shape, polyfill this triangle from a texture view, in the overSampledTexture
|
|
CPolygon2D poly;
|
|
poly.Vertices.resize(3);
|
|
for(uint i=0; i<TriangleIndices.size();i+=3)
|
|
{
|
|
// if selection OK, and the tri is not selected, skip
|
|
if(triSelectOk && !triangleSelection[i/3])
|
|
continue;
|
|
|
|
// Setup triangle.
|
|
// -----------
|
|
uint idx[3];
|
|
idx[0]= TriangleIndices[i+0];
|
|
idx[1]= TriangleIndices[i+1];
|
|
idx[2]= TriangleIndices[i+2];
|
|
// compute corners UVs, in texel size
|
|
CTriangle triangleUV;
|
|
triangleUV.V0= CVector(UVs[idx[0]].U*wOver, UVs[idx[0]].V*hOver, 0);
|
|
triangleUV.V1= CVector(UVs[idx[1]].U*wOver, UVs[idx[1]].V*hOver, 0);
|
|
triangleUV.V2= CVector(UVs[idx[2]].U*wOver, UVs[idx[2]].V*hOver, 0);
|
|
// compute corners pixelInfos
|
|
CPixelInfo pInf[3];
|
|
for(uint corner=0; corner<3; corner++)
|
|
{
|
|
pInf[corner].Pos= Vertices[idx[corner]];
|
|
pInf[corner].Normal= Normals[idx[corner]];
|
|
}
|
|
// Compute Gradients
|
|
CVector GradPosX, GradPosY, GradPosZ;
|
|
CVector GradNormalX, GradNormalY, GradNormalZ;
|
|
triangleUV.computeGradient(pInf[0].Pos.x, pInf[1].Pos.x, pInf[2].Pos.x, GradPosX);
|
|
triangleUV.computeGradient(pInf[0].Pos.y, pInf[1].Pos.y, pInf[2].Pos.y, GradPosY);
|
|
triangleUV.computeGradient(pInf[0].Pos.z, pInf[1].Pos.z, pInf[2].Pos.z, GradPosZ);
|
|
triangleUV.computeGradient(pInf[0].Normal.x, pInf[1].Normal.x, pInf[2].Normal.x, GradNormalX);
|
|
triangleUV.computeGradient(pInf[0].Normal.y, pInf[1].Normal.y, pInf[2].Normal.y, GradNormalY);
|
|
triangleUV.computeGradient(pInf[0].Normal.z, pInf[1].Normal.z, pInf[2].Normal.z, GradNormalZ);
|
|
// Compute Gradients offset
|
|
float OffPosX, OffPosY, OffPosZ;
|
|
float OffNormalX, OffNormalY, OffNormalZ;
|
|
OffPosX= pInf[0].Pos.x - GradPosX*triangleUV.V0;
|
|
OffPosY= pInf[0].Pos.y - GradPosY*triangleUV.V0;
|
|
OffPosZ= pInf[0].Pos.z - GradPosZ*triangleUV.V0;
|
|
OffNormalX= pInf[0].Normal.x - GradNormalX*triangleUV.V0;
|
|
OffNormalY= pInf[0].Normal.y - GradNormalY*triangleUV.V0;
|
|
OffNormalZ= pInf[0].Normal.z - GradNormalZ*triangleUV.V0;
|
|
|
|
|
|
// PolyFiller
|
|
// -----------
|
|
CVector2f dCenter(0.5f, 0.5f);
|
|
// select texels if their centers is in the triangle
|
|
poly.Vertices[0]= triangleUV.V0-dCenter;
|
|
poly.Vertices[1]= triangleUV.V1-dCenter;
|
|
poly.Vertices[2]= triangleUV.V2-dCenter;
|
|
// polyFiller
|
|
CPolygon2D::TRasterVect rasters;
|
|
sint minY;
|
|
poly.computeBorders(rasters, minY);
|
|
for(sint y=0;y<(sint)rasters.size();y++)
|
|
{
|
|
sint x0= rasters[y].first;
|
|
sint x1= rasters[y].second;
|
|
CVector v;
|
|
// get the center coord of this texel
|
|
v.y= y+minY+0.5f;
|
|
v.z= 0;
|
|
for(sint x=x0;x<=x1;x++)
|
|
{
|
|
// get the center coord of this texel
|
|
v.x= x+0.5f;
|
|
// Compute Pos/Normal on this texel.
|
|
CPixelInfo texelInf;
|
|
texelInf.Pos.x= OffPosX + GradPosX * v;
|
|
texelInf.Pos.y= OffPosY + GradPosY * v;
|
|
texelInf.Pos.z= OffPosZ + GradPosZ * v;
|
|
texelInf.Normal.x= OffNormalX + GradNormalX * v;
|
|
texelInf.Normal.y= OffNormalY + GradNormalY * v;
|
|
texelInf.Normal.z= OffNormalZ + GradNormalZ * v;
|
|
// normalize.
|
|
texelInf.Normal.normalize();
|
|
// Store (NB: overwrite any pixel)
|
|
sint texX= ((uint)x)%wOver;
|
|
sint texY= ((uint)y+minY)%hOver;
|
|
overTextureInfo[texY*wOver+texX]= texelInf;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// **** Down sample the overSampled texture.
|
|
sint x,y;
|
|
if(textureOverSample==1)
|
|
{
|
|
_TextureInfo= overTextureInfo;
|
|
}
|
|
else
|
|
{
|
|
// eg: 3 gives 1. delta ranges from -1 to 1.
|
|
float middle= (textureOverSample-1)/2.f;
|
|
|
|
// for all pixels.
|
|
for(y=0;y<(sint)_Height;y++)
|
|
{
|
|
for(x=0;x<(sint)_Width;x++)
|
|
{
|
|
// for all samples, take the best one.
|
|
sint xo, yo;
|
|
float bestDist= FLT_MAX;
|
|
CPixelInfo bestPixel= CPixelInfo::EmptyPixel;
|
|
for(yo=0;yo<(sint)textureOverSample;yo++)
|
|
{
|
|
for(xo=0;xo<(sint)textureOverSample;xo++)
|
|
{
|
|
// compute distance to the pixel center.
|
|
float dist= sqr(yo-middle)+sqr(xo-middle);
|
|
const CPixelInfo &overPixel= overTextureInfo[(y*textureOverSample+yo)*wOver+(x*textureOverSample+xo)];
|
|
// if the overPixel is not empty.
|
|
if( !(overPixel==CPixelInfo::EmptyPixel) )
|
|
{
|
|
// take it if the best: nearest to the pixel center.
|
|
if(dist<bestDist)
|
|
{
|
|
bestDist= dist;
|
|
bestPixel= overPixel;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// fill textureInfo
|
|
_TextureInfo[y*_Width+x]= bestPixel;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// **** Dilate texture 1 time: each empty pixel info get neighbor full pixel (8box)
|
|
// copy the non dilated texture
|
|
std::vector<CPixelInfo> tmpTextureInfo= _TextureInfo;
|
|
// process all pixels
|
|
for(y=0;y<(sint)_Height;y++)
|
|
{
|
|
sint y0=y-1, y1=y+1;
|
|
y0= max(y0, 0);
|
|
y1= min(y1, (sint)_Height-1);
|
|
for(x=0;x<(sint)_Width;x++)
|
|
{
|
|
CPixelInfo &texelInf= _TextureInfo[y*_Width+x];
|
|
// if an empty pixel.
|
|
if(texelInf==CPixelInfo::EmptyPixel)
|
|
{
|
|
// dilate: look around for non empty pixel.
|
|
sint x0=x-1, x1=x+1;
|
|
x0= max(x0, 0);
|
|
x1= min(x1, (sint)_Width-1);
|
|
// For the 8 possible pixels (nb: look us too, but doesn't matter since we are an empty pixel)
|
|
for(sint yb= y0; yb<=y1;yb++)
|
|
{
|
|
for(sint xb= x0; xb<=x1;xb++)
|
|
{
|
|
// if the neighbor is not an empty pixel. NB: avoid override problems using not Dilated texture
|
|
CPixelInfo &nbTexelInf= tmpTextureInfo[yb*_Width+xb];
|
|
if( !(nbTexelInf==CPixelInfo::EmptyPixel) )
|
|
{
|
|
// write it in the center pixel, and skip the search
|
|
texelInf= nbTexelInf;
|
|
yb= y1+1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CLodCharacterShapeBuild::serial(NLMISC::IStream &f)
|
|
{
|
|
// NEL_CLODBULD
|
|
f.serialCheck((uint32)'_LEN');
|
|
f.serialCheck((uint32)'DOLC');
|
|
f.serialCheck((uint32)'DLUB');
|
|
|
|
/*
|
|
Version 1:
|
|
- UVs and Normals + texture info
|
|
*/
|
|
sint ver= f.serialVersion(1);
|
|
|
|
f.serialCont(Vertices);
|
|
f.serialCont(SkinWeights);
|
|
f.serialCont(BonesNames);
|
|
#ifdef NL_LOD_CHARACTER_INDEX16
|
|
// must serial 16 bits index as 32 bits
|
|
if (f.isReading())
|
|
{
|
|
std::vector<uint32> readVect;
|
|
f.serialCont(readVect);
|
|
TriangleIndices.resize(readVect.size());
|
|
for(uint k = 0; k < readVect.size(); ++k)
|
|
{
|
|
nlassert(readVect[k] <= 0xffff);
|
|
TriangleIndices[k] = (uint16) readVect[k];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::vector<uint32> saveVect(TriangleIndices.size());
|
|
std::copy(TriangleIndices.begin(), TriangleIndices.end(), saveVect.begin()); // copy will do the job
|
|
f.serialCont(saveVect);
|
|
}
|
|
#else
|
|
f.serialCont(TriangleIndices);
|
|
#endif
|
|
if(ver>=1)
|
|
{
|
|
f.serialCont(UVs);
|
|
f.serialCont(Normals);
|
|
f.serial(_Width, _Height);
|
|
f.serialCont(_TextureInfo);
|
|
}
|
|
else
|
|
{
|
|
// Must init dummy UVs/normals
|
|
UVs.resize(Vertices.size(), CUV(0,0));
|
|
Normals.resize(Vertices.size(), CVector::K);
|
|
// Must init dummy texture
|
|
_Width= NL3D_CLOD_TEXT_WIDTH;
|
|
_Height= NL3D_CLOD_TEXT_HEIGHT;
|
|
_TextureInfo.resize(_Width*_Height, CPixelInfo::EmptyPixel);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
const CLodCharacterShapeBuild::CPixelInfo *CLodCharacterShapeBuild::getTextureInfoPtr()
|
|
{
|
|
if(_TextureInfo.empty())
|
|
return NULL;
|
|
else
|
|
return &_TextureInfo[0];
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
// CLodCharacterShape
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
|
|
|
|
// ***************************************************************************
|
|
CLodCharacterShape::CLodCharacterShape()
|
|
{
|
|
_NumVertices= 0;
|
|
_NumTriangles= 0;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CLodCharacterShape::buildMesh(const std::string &name, const CLodCharacterShapeBuild &lodBuild)
|
|
{
|
|
uint numVertices= (uint)lodBuild.Vertices.size();
|
|
const vector<uint32> &triangleIndices= lodBuild.TriangleIndices;
|
|
const vector<CMesh::CSkinWeight> &skinWeights= lodBuild.SkinWeights;
|
|
const vector<CUV> &uvs= lodBuild.UVs;
|
|
const vector<CVector> &normals= lodBuild.Normals;
|
|
|
|
nlassert(numVertices>0);
|
|
nlassert(triangleIndices.size()>0);
|
|
nlassert((triangleIndices.size()%3)==0);
|
|
nlassert(skinWeights.size() == numVertices);
|
|
nlassert(uvs.size() == numVertices);
|
|
nlassert(normals.size() == numVertices);
|
|
|
|
// reset data
|
|
contReset(_Anims);
|
|
contReset(_AnimMap);
|
|
contReset(_Bones);
|
|
contReset(_BoneMap);
|
|
contReset(_TriangleIndices);
|
|
contReset(_UVs);
|
|
contReset(_Normals);
|
|
|
|
// Copy data.
|
|
_Name= name;
|
|
_NumVertices= numVertices;
|
|
_NumTriangles= (uint32)triangleIndices.size()/3;
|
|
#ifdef NL_LOD_CHARACTER_INDEX16
|
|
_TriangleIndices.resize(triangleIndices.size());
|
|
for(uint k = 0; k < triangleIndices.size(); ++k)
|
|
{
|
|
nlassert(triangleIndices[k] <= 0xffff);
|
|
_TriangleIndices[k] = (uint16) triangleIndices[k];
|
|
}
|
|
#else
|
|
_TriangleIndices= triangleIndices;
|
|
#endif
|
|
_UVs= uvs;
|
|
_Normals= normals;
|
|
|
|
// check indices.
|
|
uint i;
|
|
for(i=0;i<triangleIndices.size();i++)
|
|
{
|
|
nlassert(triangleIndices[i]<_NumVertices);
|
|
}
|
|
|
|
// Copy bone names, and compute bone Map
|
|
_Bones.resize(lodBuild.BonesNames.size());
|
|
for(i=0; i<_Bones.size(); i++)
|
|
{
|
|
_Bones[i].Name= lodBuild.BonesNames[i];
|
|
_BoneMap.insert( make_pair(_Bones[i].Name, i) );
|
|
}
|
|
|
|
// "Normalize" SkinWeights for CLodCharacterShape
|
|
for(i=0;i<skinWeights.size();i++)
|
|
{
|
|
nlassert(skinWeights[i].Weights[0]>0);
|
|
// for all slots not 0
|
|
for(uint j=0;j<NL3D_MESH_SKINNING_MAX_MATRIX;j++)
|
|
{
|
|
// if this this slot is used.
|
|
if(skinWeights[i].Weights[j]>0)
|
|
{
|
|
uint boneId= skinWeights[i].MatrixId[j];
|
|
nlassert(boneId < _Bones.size());
|
|
// init the vInf data
|
|
CVertexInf vInf;
|
|
vInf.VertexId= i;
|
|
vInf.Influence= skinWeights[i].Weights[j];
|
|
// Insert this vertex influence in the bone.
|
|
_Bones[boneId].InfVertices.push_back(vInf);
|
|
}
|
|
else
|
|
// stop for this vertex.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
bool CLodCharacterShape::addAnim(const CAnimBuild &animBuild)
|
|
{
|
|
// first, verify don't exist.
|
|
if(getAnimIdByName(animBuild.Name)!=-1)
|
|
return false;
|
|
|
|
// build basics of the animation
|
|
CAnim dstAnim;
|
|
dstAnim.Name= animBuild.Name;
|
|
dstAnim.AnimLength= animBuild.AnimLength;
|
|
// Possible to have an Anim with just one key. setup an epsilon for animLength if 0.
|
|
if(dstAnim.AnimLength<=0)
|
|
dstAnim.AnimLength= 0.001f;
|
|
dstAnim.OOAnimLength= 1.0f / animBuild.AnimLength;
|
|
dstAnim.NumKeys= animBuild.NumKeys;
|
|
// verify size of the array
|
|
nlassert(dstAnim.NumKeys>0);
|
|
nlassert(dstAnim.NumKeys * _NumVertices == animBuild.Keys.size());
|
|
// resize dest array
|
|
dstAnim.Keys.resize(animBuild.Keys.size());
|
|
|
|
|
|
// Pack animation. 1st pass: compute max size over the animation vertices
|
|
uint i;
|
|
// minimum shape size is , say, 1 cm :)
|
|
CVector maxSize(0.01f, 0.01f, 0.01f);
|
|
for(i=0;i<animBuild.Keys.size();i++)
|
|
{
|
|
// take the maxSize of the abs values
|
|
maxSize.maxof(maxSize, -animBuild.Keys[i]);
|
|
maxSize.maxof(maxSize, animBuild.Keys[i]);
|
|
}
|
|
|
|
// compute the UnPackScaleFactor ie maxSize, to be multiplied by max Abs value of a sint16
|
|
dstAnim.UnPackScaleFactor= maxSize * (1.0f/32767);
|
|
|
|
// Pack animation. 2st pass: pack.
|
|
CVectorD packScaleFactor;
|
|
packScaleFactor.x= 1.0 / dstAnim.UnPackScaleFactor.x;
|
|
packScaleFactor.y= 1.0 / dstAnim.UnPackScaleFactor.y;
|
|
packScaleFactor.z= 1.0 / dstAnim.UnPackScaleFactor.z;
|
|
// For all key vertices
|
|
for(i=0;i<animBuild.Keys.size();i++)
|
|
{
|
|
CVector v= animBuild.Keys[i];
|
|
CVector3s &dstV= dstAnim.Keys[i];
|
|
|
|
// compress
|
|
v.x= float(v.x*packScaleFactor.x);
|
|
v.y= float(v.y*packScaleFactor.y);
|
|
v.z= float(v.z*packScaleFactor.z);
|
|
// clamp to sint16 limits (for float precision problems).
|
|
clamp(v.x, -32767, 32767);
|
|
clamp(v.y, -32767, 32767);
|
|
clamp(v.z, -32767, 32767);
|
|
// get into the vector3s
|
|
dstV.x= (sint16)floor(v.x);
|
|
dstV.y= (sint16)floor(v.y);
|
|
dstV.z= (sint16)floor(v.z);
|
|
}
|
|
|
|
|
|
// Add the anim to the array, and add an entry to the map
|
|
_Anims.push_back(dstAnim);
|
|
_AnimMap.insert(make_pair(dstAnim.Name, (uint32)_Anims.size()-1));
|
|
|
|
return true;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CLodCharacterShape::CAnim::serial(NLMISC::IStream &f)
|
|
{
|
|
(void)f.serialVersion(0);
|
|
|
|
f.serial(Name);
|
|
f.serial(NumKeys);
|
|
f.serial(AnimLength);
|
|
f.serial(OOAnimLength);
|
|
f.serial(UnPackScaleFactor);
|
|
f.serialCont(Keys);
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CLodCharacterShape::CBoneInfluence::serial(NLMISC::IStream &f)
|
|
{
|
|
(void)f.serialVersion(0);
|
|
|
|
f.serial(Name);
|
|
f.serialCont(InfVertices);
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CLodCharacterShape::serial(NLMISC::IStream &f)
|
|
{
|
|
// NEL_CLODSHAP
|
|
f.serialCheck((uint32)'_LEN');
|
|
f.serialCheck((uint32)'DOLC');
|
|
f.serialCheck((uint32)'PAHS');
|
|
|
|
/*
|
|
Version 1:
|
|
- UVs and Normals.
|
|
*/
|
|
sint ver= f.serialVersion(1);
|
|
|
|
f.serial(_Name);
|
|
f.serial(_NumVertices);
|
|
f.serial(_NumTriangles);
|
|
f.serialCont(_Bones);
|
|
f.serialCont(_BoneMap);
|
|
// nb : indices are always saved in 32 bits for compatibility
|
|
#ifndef NL_LOD_CHARACTER_INDEX16
|
|
f.serialCont(_TriangleIndices);
|
|
#else
|
|
if (f.isReading())
|
|
{
|
|
std::vector<uint32> savedIndices;
|
|
f.serialCont(savedIndices);
|
|
_TriangleIndices.resize(savedIndices.size());
|
|
for(uint k = 0; k < savedIndices.size(); ++k)
|
|
{
|
|
nlassert(savedIndices[k] <= 0xffff);
|
|
_TriangleIndices[k] = (uint16) savedIndices[k];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::vector<uint32> savedIndices;
|
|
savedIndices.resize(_TriangleIndices.size());
|
|
for(uint k = 0; k < savedIndices.size(); ++k)
|
|
{
|
|
savedIndices[k] = _TriangleIndices[k];
|
|
}
|
|
f.serialCont(savedIndices);
|
|
}
|
|
#endif
|
|
f.serialCont(_Anims);
|
|
f.serialCont(_AnimMap);
|
|
|
|
if(ver>=1)
|
|
{
|
|
f.serialCont(_UVs);
|
|
f.serialCont(_Normals);
|
|
}
|
|
else
|
|
{
|
|
// Must init dummy UVs/normals
|
|
_UVs.resize(_NumVertices, CUV(0,0));
|
|
_Normals.resize(_NumVertices, CVector::K);
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
sint CLodCharacterShape::getAnimIdByName(const std::string &name) const
|
|
{
|
|
CstItStrIdMap it= _AnimMap.find(name);
|
|
if(it == _AnimMap.end())
|
|
return -1;
|
|
else
|
|
return it->second;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
sint CLodCharacterShape::getBoneIdByName(const std::string &name) const
|
|
{
|
|
CstItStrIdMap it= _BoneMap.find(name);
|
|
if(it == _BoneMap.end())
|
|
return -1;
|
|
else
|
|
return it->second;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
const TLodCharacterIndexType *CLodCharacterShape::getTriangleArray() const
|
|
{
|
|
if(_NumTriangles)
|
|
return &_TriangleIndices[0];
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
const CLodCharacterShape::CVector3s *CLodCharacterShape::getAnimKey(uint animId, TGlobalAnimationTime time, bool wrapMode, CVector &unPackScaleFactor) const
|
|
{
|
|
H_AUTO( NL3D_LodCharacterShape_getAnimKey )
|
|
|
|
float localTime;
|
|
|
|
if(animId>=_Anims.size())
|
|
return NULL;
|
|
|
|
// get the anim.
|
|
const CAnim &anim= _Anims[animId];
|
|
|
|
// scale info
|
|
unPackScaleFactor= anim.UnPackScaleFactor;
|
|
|
|
// Loop mgt.
|
|
if(wrapMode)
|
|
localTime= (float)fmod((float)time, (float)anim.AnimLength);
|
|
else
|
|
localTime= (float)time;
|
|
|
|
// Clamp to the range.
|
|
clamp(localTime, 0, anim.AnimLength);
|
|
|
|
// get the key.
|
|
sint keyId= (sint)floor( (localTime*anim.OOAnimLength) * anim.NumKeys );
|
|
clamp(keyId, 0, sint(anim.NumKeys-1));
|
|
|
|
// return the key.
|
|
return &anim.Keys[keyId * _NumVertices];
|
|
}
|
|
|
|
// ***************************************************************************
|
|
const CUV *CLodCharacterShape::getUVs() const
|
|
{
|
|
if(_NumVertices==0)
|
|
return NULL;
|
|
|
|
return &_UVs[0];
|
|
}
|
|
|
|
// ***************************************************************************
|
|
const CVector *CLodCharacterShape::getNormals() const
|
|
{
|
|
if(_NumVertices==0)
|
|
return NULL;
|
|
|
|
return &_Normals[0];
|
|
}
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
// Bone Alpha Testing
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
|
|
|
|
// ***************************************************************************
|
|
void CLodCharacterShape::startBoneAlpha(std::vector<uint8> &tmpAlphas) const
|
|
{
|
|
// clear
|
|
tmpAlphas.clear();
|
|
// alocate, and fill
|
|
tmpAlphas.resize(getNumVertices(), 0);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CLodCharacterShape::addBoneAlpha(uint boneId, std::vector<uint8> &tmpAlphas) const
|
|
{
|
|
// Yoyo: This is an error to not have the same skeleton that the one stored in the lod shape. But must not crash
|
|
if(boneId>=_Bones.size())
|
|
return;
|
|
const CBoneInfluence &bone= _Bones[boneId];
|
|
|
|
// for all vertices influenced by this bone, must set the alpha to full
|
|
for(uint i=0; i<bone.InfVertices.size(); i++)
|
|
{
|
|
const CVertexInf &vInf= bone.InfVertices[i];
|
|
tmpAlphas[vInf.VertexId]= 255;
|
|
}
|
|
}
|
|
|
|
|
|
} // NL3D
|