// 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/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 &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 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 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 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 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 &triangleIndices= lodBuild.TriangleIndices; const vector &skinWeights= lodBuild.SkinWeights; const vector &uvs= lodBuild.UVs; const vector &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;i0); // for all slots not 0 for(uint j=0;j0) { 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 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 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 &tmpAlphas) const { // clear tmpAlphas.clear(); // alocate, and fill tmpAlphas.resize(getNumVertices(), 0); } // *************************************************************************** void CLodCharacterShape::addBoneAlpha(uint boneId, std::vector &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