// 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/mesh_morpher.h" #include "nel/3d/vertex_buffer.h" #include "nel/3d/raw_skin.h" using namespace std; using namespace NLMISC; namespace NL3D { // *************************************************************************** void CBlendShape::serial (NLMISC::IStream &f) throw(NLMISC::EStream) { /* *********************************************** * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance * It can be loaded/called through CAsyncFileManager for instance * ***********************************************/ // version 1 : added tangent space support sint ver = f.serialVersion (1); f.serial (Name); f.serialCont (deltaPos); f.serialCont (deltaNorm); f.serialCont (deltaUV); f.serialCont (deltaCol); if (ver >= 1) f.serialCont(deltaTgSpace); f.serialCont (VertRefs); } // *************************************************************************** CMeshMorpher::CMeshMorpher() { /* *********************************************** * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance * It can be loaded/called through CAsyncFileManager for instance * ***********************************************/ _VBOri = NULL; _VBDst = NULL; _Vertices = NULL; _Normals = NULL; _TgSpace= NULL; _SkinApplied= false; } // *************************************************************************** void CMeshMorpher::init (CVertexBuffer *vbOri, CVertexBuffer *vbDst, bool hasTgSpace) { _VBOri = vbOri; _VBDst = vbDst; _UseTgSpace = hasTgSpace; } // *************************************************************************** void CMeshMorpher::initSkinned (CVertexBuffer *vbOri, CVertexBuffer *vbDst, bool hasTgSpace, std::vector *vVertices, std::vector *vNormals, std::vector *vTgSpace, /* NULL if none */ bool bSkinApplied ) { _VBOri = vbOri; _VBDst = vbDst; _UseTgSpace = hasTgSpace; _Vertices = vVertices; _Normals = vNormals; _TgSpace = vTgSpace; _SkinApplied = bSkinApplied; } // *************************************************************************** void CMeshMorpher::update (std::vector *pBSFactor) { uint32 i, j; if (_VBOri == NULL) return; if (BlendShapes.size() == 0) return; if (_VBOri->getNumVertices() != _VBDst->getNumVertices()) { // Because the original vertex buffer is not initialized by default // we must init it here (if there are some blendshapes) *_VBOri = *_VBDst; } // Does the flags are reserved ? if (_Flags.size() != _VBOri->getNumVertices()) { _Flags.resize (_VBOri->getNumVertices()); for (i = 0; i < _Flags.size(); ++i) _Flags[i] = Modified; // Modified to update all } nlassert(_VBOri->getVertexFormat() == _VBDst->getVertexFormat()); // Cleaning with original vertex buffer uint32 VBVertexSize = _VBOri->getVertexSize(); CVertexBufferRead srcvba; _VBOri->lock (srcvba); CVertexBufferReadWrite dstvba; _VBDst->lock (dstvba); const uint8 *pOri = (const uint8*)srcvba.getVertexCoordPointer (); uint8 *pDst = (uint8*)dstvba.getVertexCoordPointer (); for (i= 0; i < _Flags.size(); ++i) if (_Flags[i] >= Modified) { _Flags[i] = OriginalVBDst; for(j = 0; j < VBVertexSize; ++j) pDst[j+i*VBVertexSize] = pOri[j+i*VBVertexSize]; } uint tgSpaceStage = 0; if (_UseTgSpace) { tgSpaceStage = _VBDst->getNumTexCoordUsed() - 1; } // Blending with blendshape for (i = 0; i < BlendShapes.size(); ++i) { CBlendShape &rBS = BlendShapes[i]; float rFactor = pBSFactor->operator[](i).getFactor()/100.0f; // todo hulud check it works // if (rFactor > 0.0f) if (rFactor != 0.0f) for (j = 0; j < rBS.VertRefs.size(); ++j) { uint32 vp = rBS.VertRefs[j]; // Modify Pos/Norm/TgSpace. //------------ if (_VBDst->getVertexFormat() & CVertexBuffer::PositionFlag) if (rBS.deltaPos.size() > 0) { CVector *pV = dstvba.getVertexCoordPointer (vp); *pV += rBS.deltaPos[j] * rFactor; } if (_VBDst->getVertexFormat() & CVertexBuffer::NormalFlag) if (rBS.deltaNorm.size() > 0) { CVector *pV = dstvba.getNormalCoordPointer (vp); *pV += rBS.deltaNorm[j] * rFactor; } if (_UseTgSpace) if (rBS.deltaTgSpace.size() > 0) { CVector *pV = (CVector*)dstvba.getTexCoordPointer (vp, tgSpaceStage); *pV += rBS.deltaTgSpace[j] * rFactor; } // Modify UV0 / Color //------------ if (_VBDst->getVertexFormat() & CVertexBuffer::TexCoord0Flag) if (rBS.deltaUV.size() > 0) { CUV *pUV = dstvba.getTexCoordPointer (vp); *pUV += rBS.deltaUV[j] * rFactor; } if (_VBDst->getVertexFormat() & CVertexBuffer::PrimaryColorFlag) if (rBS.deltaCol.size() > 0) { // todo hulud d3d vertex color RGBA / BGRA CRGBA *pRGBA = (CRGBA*)dstvba.getColorPointer (vp); CRGBAF rgbf(*pRGBA); rgbf.R += rBS.deltaCol[j].R * rFactor; rgbf.G += rBS.deltaCol[j].G * rFactor; rgbf.B += rBS.deltaCol[j].B * rFactor; rgbf.A += rBS.deltaCol[j].A * rFactor; clamp(rgbf.R, 0.0f, 1.0f); clamp(rgbf.G, 0.0f, 1.0f); clamp(rgbf.B, 0.0f, 1.0f); clamp(rgbf.A, 0.0f, 1.0f); *pRGBA = rgbf; } // Modified _Flags[vp] = Modified; } } } // *************************************************************************** void CMeshMorpher::updateSkinned (std::vector *pBSFactor) { uint32 i, j; if (_VBOri == NULL) return; if (BlendShapes.size() == 0) return; if (_VBOri->getNumVertices() != _VBDst->getNumVertices()) { // Because the original vertex buffer is not initialized by default // we must init it here (if there are some blendshapes) *_VBOri = *_VBDst; } // Does the flags are reserved ? if (_Flags.size() != _VBOri->getNumVertices()) { _Flags.resize (_VBOri->getNumVertices()); for (i = 0; i < _Flags.size(); ++i) _Flags[i] = Modified; // Modified to update all } nlassert(_VBOri->getVertexFormat() == _VBDst->getVertexFormat()); uint tgSpaceStage; uint tgSpaceOff = 0; if (_UseTgSpace && _TgSpace) { tgSpaceStage = _VBDst->getNumTexCoordUsed() - 1; tgSpaceOff = _VBDst->getTexCoordOff(tgSpaceStage); } // Cleaning with original vertex buffer uint32 VBVertexSize = _VBOri->getVertexSize(); CVertexBufferRead srcvba; _VBOri->lock (srcvba); CVertexBufferReadWrite dstvba; _VBDst->lock (dstvba); const uint8 *pOri = (const uint8*)srcvba.getVertexCoordPointer (); uint8 *pDst = (uint8*)dstvba.getVertexCoordPointer (); for (i= 0; i < _Flags.size(); ++i) if (_Flags[i] >= Modified) { for(j = 0; j < VBVertexSize; ++j) pDst[j+i*VBVertexSize] = pOri[j+i*VBVertexSize]; if (_Vertices != NULL) _Vertices->operator[](i) = ((CVector*)(pOri+i*VBVertexSize))[0]; if (_Normals != NULL) _Normals->operator[](i) = ((CVector*)(pOri+i*VBVertexSize))[1]; if (_TgSpace != NULL) (*_TgSpace)[i] = * (CVector*)(pOri + i * VBVertexSize + tgSpaceOff); _Flags[i] = OriginalVBDst; } // Blending with blendshape for (i = 0; i < BlendShapes.size(); ++i) { CBlendShape &rBS = BlendShapes[i]; float rFactor = pBSFactor->operator[](i).getFactor()/100.0f; if (rFactor != 0.0f) for (j = 0; j < rBS.VertRefs.size(); ++j) { uint32 vp = rBS.VertRefs[j]; // Modify Pos/Norm/TgSpace. //------------ if (_Vertices != NULL) if (rBS.deltaPos.size() > 0) { CVector *pV = &(_Vertices->operator[](vp)); *pV += rBS.deltaPos[j] * rFactor; } if (_Normals != NULL) if (rBS.deltaNorm.size() > 0) { CVector *pV = &(_Normals->operator[](vp)); *pV += rBS.deltaNorm[j] * rFactor; } if (_UseTgSpace && _TgSpace != NULL) if (rBS.deltaTgSpace.size() > 0) { CVector *pV = &((*_TgSpace)[vp]); *pV += rBS.deltaTgSpace[j] * rFactor; } // Modify UV0 / Color //------------ if (_VBDst->getVertexFormat() & CVertexBuffer::TexCoord0Flag) if (rBS.deltaUV.size() > 0) { CUV *pUV = dstvba.getTexCoordPointer (vp); *pUV += rBS.deltaUV[j] * rFactor; } if (_VBDst->getVertexFormat() & CVertexBuffer::PrimaryColorFlag) if (rBS.deltaCol.size() > 0) { // todo hulud d3d vertex color RGBA / BGRA CRGBA *pRGBA = (CRGBA*)dstvba.getColorPointer (vp); CRGBAF rgbf(*pRGBA); rgbf.R += rBS.deltaCol[j].R * rFactor; rgbf.G += rBS.deltaCol[j].G * rFactor; rgbf.B += rBS.deltaCol[j].B * rFactor; rgbf.A += rBS.deltaCol[j].A * rFactor; clamp(rgbf.R, 0.0f, 1.0f); clamp(rgbf.G, 0.0f, 1.0f); clamp(rgbf.B, 0.0f, 1.0f); clamp(rgbf.A, 0.0f, 1.0f); *pRGBA = rgbf; } // Modified _Flags[vp] = Modified; } } } // *************************************************************************** void CMeshMorpher::serial (NLMISC::IStream &f) throw(NLMISC::EStream) { /* *********************************************** * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance * It can be loaded/called through CAsyncFileManager for instance * ***********************************************/ (void)f.serialVersion (0); f.serialCont (BlendShapes); } // *************************************************************************** #define NL3D_RAWSKIN_NORMAL_OFF 12 #define NL3D_RAWSKIN_UV_OFF 24 #define NL3D_RAWSKIN_VERTEX_SIZE 32 void CMeshMorpher::updateRawSkin (CVertexBuffer *vbOri, NLMISC::CObjectVector &vertexRemap, std::vector *pBSFactor) { uint32 i, j; if (vbOri == NULL) return; if (BlendShapes.size() == 0) return; nlassert(vbOri->getVertexFormat() == (CVertexBuffer::PositionFlag | CVertexBuffer::NormalFlag |CVertexBuffer::TexCoord0Flag) ); nlassert(NL3D_RAWSKIN_VERTEX_SIZE == vbOri->getVertexSize()); nlassert(NL3D_RAWSKIN_NORMAL_OFF == vbOri->getNormalOff()); nlassert(NL3D_RAWSKIN_UV_OFF == vbOri->getTexCoordOff(0)); // Cleaning with original vertex buffer CVertexBufferRead srcvba; vbOri->lock (srcvba); const uint8 *pOri = (const uint8*)srcvba.getVertexCoordPointer (); CRawSkinVertex **vRemap= vertexRemap.getPtr(); uint numVertices= vbOri->getNumVertices(); // Update only the vertices of this lod for (i= 0; i < numVertices; ++i) { if(*vRemap) { (*vRemap)->Pos= *(CVector*)(pOri); (*vRemap)->Normal= *(CVector*)(pOri + NL3D_RAWSKIN_NORMAL_OFF); (*vRemap)->UV= *(CUV*)(pOri + NL3D_RAWSKIN_UV_OFF); } pOri+= NL3D_RAWSKIN_VERTEX_SIZE; vRemap++; } // Blending with blendshape for (i = 0; i < BlendShapes.size(); ++i) { CBlendShape &rBS = BlendShapes[i]; float rFactor = pBSFactor->operator[](i).getFactor(); if (rFactor != 0.0f) { rFactor*= 0.01f; uint32 numVertices= (uint32)rBS.VertRefs.size(); // don't know why, but cases happen where deltaNorm not empty while deltaPos is bool hasPos= rBS.deltaPos.size()>0; bool hasNorm= rBS.deltaNorm.size()>0; bool hasUV= rBS.deltaUV.size()>0; for (j = 0; j < numVertices; ++j) { // Get the vertex Index in the VBufferFinal uint vid= rBS.VertRefs[j]; // Then get the RawSkin vertex to modify CRawSkinVertex *rsVert= vertexRemap[vid]; // If exist in this Lod RawSkin, apply if(rsVert) { if(hasPos) rsVert->Pos+= rBS.deltaPos[j] * rFactor; if(hasNorm) rsVert->Normal+= rBS.deltaNorm[j] * rFactor; if(hasUV) rsVert->UV+= rBS.deltaUV[j] * rFactor; } } } } } } // NL3D