// 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 . #ifndef NL_MESH_H #define NL_MESH_H #include "nel/misc/types_nl.h" #include "nel/3d/shape.h" #include "nel/3d/driver.h" #include "nel/misc/aabbox.h" #include "nel/misc/uv.h" #include "nel/misc/bit_set.h" #include "nel/3d/vertex_buffer.h" #include "nel/3d/material.h" #include "nel/3d/index_buffer.h" #include "nel/3d/animated_material.h" #include "nel/3d/mesh_base.h" #include "nel/3d/mesh_geom.h" #include "nel/3d/mesh_morpher.h" #include "nel/3d/mesh_vertex_program.h" #include "nel/3d/shadow_skin.h" #include #include namespace NL3D { using NLMISC::CVector; using NLMISC::CPlane; using NLMISC::CMatrix; class CMeshGeom; class CSkeletonModel; class CMatrix3x4; // *************************************************************************** // Should be 4. #define NL3D_MESH_SKINNING_MAX_MATRIX 4 // Above this distance, Mesh with a bigger Radius will use BBox clipping #define NL3D_MESH_PRECISE_CLIP_THRESHOLD 5.0f // *************************************************************************** /** * An instanciable mesh. * Skinning support: support only palette skinning. * \author Lionel Berenguier * \author Nevrax France * \date 2000 */ class CMesh : public CMeshBase { public: /// \name Structures for building a mesh. //@{ /// A corner of a face. struct CCorner { sint32 Vertex; /// The vertex Id. CVector Normal; NLMISC::CUVW Uvws[CVertexBuffer::MaxStage]; CRGBA Color; CRGBA Specular; // Setup all to 0, but Color (to white)... Important for good corner comparison. // This is slow but doesn't matter since used at mesh building.... CCorner(); void serial(NLMISC::IStream &f); }; /// A Triangle face. struct CFace { CCorner Corner[3]; sint32 MaterialId; sint32 SmoothGroup; void serial(NLMISC::IStream &f); }; /** Skinning: A skin weight for a vertex. * NB: if you don't use all matrix for this vertex, use at least the 0th matrix, and simply set 0 on Weights you don't use. */ struct CSkinWeight { /// What matrix of the skeleton shape this vertex use. uint32 MatrixId[NL3D_MESH_SKINNING_MAX_MATRIX]; /// weight of this matrix (sum of 4 must be 1). float Weights[NL3D_MESH_SKINNING_MAX_MATRIX]; /// ctor. CSkinWeight() { for(uint i=0;i Vertices; }; /// For each vertex struct CInterfaceLink { // to which interface this vertex is welded. -1 if none sint InterfaceId; // to which vertex of the interface this vertex is welded uint InterfaceVertexId; CInterfaceLink() { InterfaceId= -1; } }; /// A mesh information. struct CMeshBuild { /** the IDRV_VF* flags which tells what vertices data are used. See IDriver::setVertexFormat() for * more information. NB: IDRV_VF_XYZ is always considered to true.. * Note that is some stage use 2 textures coordinates instead of 3, then the extended vertex format must be used isntead */ sint32 VertexFlags; uint8 NumCoords[CVertexBuffer::MaxStage]; // tells for each uvw if is uses 2 or 3 coords uint8 UVRouting[CVertexBuffer::MaxStage]; // gives the uv routing table. Each final UV channel can be routed to any vertex uv // Vertices array std::vector Vertices; // Palette Skinning Vertices array (same size as Vertices). NULL if no skinning. std::vector SkinWeights; // Bones name. Each matrix id used in SkinWeights must have a corresponding string in the bone name array. std::vector BonesNames; // Faces array std::vector Faces; // Blend shapes if some std::vector BlendShapes; // Link between VB and max vertex indices std::vector VertLink; // Filled when called build // MeshVertexProgram to copy to meshGeom. NLMISC::CSmartPtr MeshVertexProgram; // Mesh Interface System for MRM building std::vector Interfaces; // must be same size as Vertices, else Mesh Interface system disabled std::vector InterfaceLinks; NLMISC::CBitSet InterfaceVertexFlag; // each bit indicate if the vertex belongs to an interface CMeshBuild(); // Serialization //void serial(NLMISC::IStream &f); }; //@} public: /* *********************************************** * 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 * ***********************************************/ /// Constructor CMesh(); /// dtor ~CMesh(); CMesh(const CMesh &mesh); CMesh &operator=(const CMesh &mesh); /// Build a mesh, replacing old. WARNING: This has a side effect of deleting AnimatedMaterials. void build(CMeshBase::CMeshBaseBuild &mbase, CMeshBuild &mbuild); /// Build a mesh from material info, and a builded MeshGeom. WARNING: This has a side effect of deleting AnimatedMaterials. void build(CMeshBase::CMeshBaseBuild &mbuild, CMeshGeom &meshGeom); /** Optimize material use. If a material in CMeshBase is not used by any renderPasses, it is removed, and ids are updated. * WARNING: This has a side effect of deleting AnimatedMaterials. * \param remap a remap material Id: newId= remap[oldId]. -1 means "no more used" */ void optimizeMaterialUsage(std::vector &remap); void setBlendShapes(std::vector&bs); /// Compute skinning id void computeBonesId (CSkeletonModel *skeleton); /// update Skeleton Usage. increment or decrement. void updateSkeletonUsage(CSkeletonModel *sm, bool increment); /// \name From IShape // @{ /// Create a CMeshInstance, which contains materials. virtual CTransformShape *createInstance(CScene &scene); /// clip this mesh in a driver. virtual bool clip(const std::vector &pyramid, const CMatrix &worldMatrix) ; /// render() this mesh in a driver. virtual void render(IDriver *drv, CTransformShape *trans, bool opaquePass); /// serial this mesh. virtual void serial(NLMISC::IStream &f); NLMISC_DECLARE_CLASS(CMesh); /// get trinagle count. virtual float getNumTriangles (float distance); /// Get bbox. virtual void getAABBox(NLMISC::CAABBox &bbox) const {bbox= getBoundingBox().getAABBox();} /// profiling virtual void profileSceneRender(CRenderTrav *rdrTrav, CTransformShape *trans, bool opaquePass); /// System Mem Geometry Copy, built at load time virtual void buildSystemGeometry(); // @} /// \name Geometry accessors // @{ /// get the extended axis aligned bounding box of the mesh const NLMISC::CAABBoxExt& getBoundingBox() const; /// get the vertex buffer used by the mesh const CVertexBuffer &getVertexBuffer() const; /// get the number of matrix block uint getNbMatrixBlock() const; /** get the number of rendering pass for a given matrix block * \param matrixBlockIndex the index of the matrix block the rendering passes belong to */ uint getNbRdrPass(uint matrixBlockIndex) const; /** get the primitive block associated with a rendering pass of a matrix block * \param matrixBlockIndex the index of the matrix block the renderin pass belong to * \param renderingPassIndex the index of the rendering pass in the matrix block */ const CIndexBuffer &getRdrPassPrimitiveBlock(uint matrixBlockIndex, uint renderingPassIndex) const; /** get the material ID associated with a rendering pass of a matrix block * \param matrixBlockIndex the index of the matrix block the renderin pass belong to * \param renderingPassIndex the index of the rendering pass in the matrix block */ uint32 getRdrPassMaterial(uint matrixBlockIndex, uint renderingPassIndex) const; /// Get the geom mesh const CMeshGeom &getMeshGeom () const; // @} /// \name Mesh Block Render Interface // @{ virtual IMeshGeom *supportMeshBlockRendering (CTransformShape *trans, float &polygonCount ) const; // @} private: // The geometry. CMeshGeom *_MeshGeom; // Called at load or build time, all that is not serialized void compileRunTime(); }; // *************************************************************************** /** * A mesh geometry. * Skinning support: support only palette skinning. * \author Lionel Berenguier * \author Nevrax France * \date 2000 */ class CMeshGeom: public IMeshGeom { public: /// Constructor CMeshGeom(); virtual ~CMeshGeom(); /// Build a meshGeom void build(CMesh::CMeshBuild &mbuild, uint numMaxMaterial); void setBlendShapes(std::vector&bs); /// change materials Ids (called from CMesh::optimizeMaterialUsage()) void applyMaterialRemap(const std::vector &remap); /// \name From IMeshGeom // @{ /// Init instance info. virtual void initInstance(CMeshBaseInstance *mbi); /// clip this mesh virtual bool clip(const std::vector &pyramid, const CMatrix &worldMatrix) ; /// render() this mesh in a driver. virtual void render(IDriver *drv, CTransformShape *trans, float polygonCount, uint32 rdrFlags, float globalAlpha); /// render() this mesh as a skin virtual void renderSkin(CTransformShape *trans, float alphaMRM); // get an approximation of the number of triangles this instance will render for a fixed distance. virtual float getNumTriangles (float distance); /// serial this mesh. virtual void serial(NLMISC::IStream &f); NLMISC_DECLARE_CLASS(CMeshGeom); // profile virtual void profileSceneRender(CRenderTrav *rdrTrav, CTransformShape *trans, float polygonCount, uint32 rdrFlags); // @} /// \name Geometry accessors // @{ /// get the extended axis aligned bounding box of the mesh const NLMISC::CAABBoxExt& getBoundingBox() const { return _BBox; } /// get the vertex buffer used by the mesh const CVertexBuffer &getVertexBuffer() const { return _VBuffer ; } /// get the number of matrix block uint getNbMatrixBlock() const { return (uint)_MatrixBlocks.size() ; } /** get the number of rendering pass for a given matrix block * \param matrixBlockIndex the index of the matrix block the rendering passes belong to */ uint getNbRdrPass(uint matrixBlockIndex) const { return (uint)_MatrixBlocks[matrixBlockIndex].RdrPass.size() ; } /** get the primitive block associated with a rendering pass of a matrix block * \param matrixBlockIndex the index of the matrix block the renderin pass belong to * \param renderingPassIndex the index of the rendering pass in the matrix block */ const CIndexBuffer &getRdrPassPrimitiveBlock(uint matrixBlockIndex, uint renderingPassIndex) const { return _MatrixBlocks[matrixBlockIndex].RdrPass[renderingPassIndex].PBlock ; } /** get the material ID associated with a rendering pass of a matrix block * \param matrixBlockIndex the index of the matrix block the renderin pass belong to * \param renderingPassIndex the index of the rendering pass in the matrix block */ uint32 getRdrPassMaterial(uint matrixBlockIndex, uint renderingPassIndex) const { return _MatrixBlocks[matrixBlockIndex].RdrPass[renderingPassIndex].MaterialId ; } /// get the number of BlendShapes uint getNbBlendShapes() const { if(_MeshMorpher) return (uint)_MeshMorpher->BlendShapes.size(); return 0; } /** Tool function to retrieve vector geometry only of the mesh. * return false if the vbuffer cannot be read (resident) */ bool retrieveVertices(std::vector &vertices) const; /** Tool function to retrieve triangles geometry only of the mesh (of all rdrpass). * return false if the index buffer cannot be read (resident) */ bool retrieveTriangles(std::vector &indices) const; // @} /// \name Skinning Behavior // @{ /// Return true if the mesh is skinned, else return false. bool isSkinned () const { return _Skinned; } /// Compute skinning id void computeBonesId (CSkeletonModel *skeleton); /// update Skeleton Usage. increment or decrement. computeBonesId must has been called before. void updateSkeletonUsage(CSkeletonModel *sm, bool increment); /// return array of bones used by the skin. computeBonesId must has been called before. const std::vector &getSkinBoneUsage() const {return _BonesId;} /// see CTransform::getSkinBoneSphere() doc for the meaning of this value. computeBonesId must has been called before. const std::vector &getSkinBoneSphere() const {return _BonesSphere;} /// Skin intersection bool supportIntersectSkin() const {return _Skinned;} bool intersectSkin(CTransformShape *mi, const CMatrix &toRaySpace, float &dist2D, float &distZ, bool computeDist2D); // @} /** render the mesh geometry with a single material. Render is said "Simple" because no special features are used: * - mesh is rendered without VertexProgram (if it has one). * - mesh is rendered without Skinning. * - mesh is rendered without use of VertexBufferHard. * - mesh is rendered without MeshMorpher. * - ..... */ void renderSimpleWithMaterial(IDriver *drv, const CMatrix &worldMatrix, CMaterial &mat); /// \name Mesh Block Render Implementation // @{ /** true if this meshGeom support meshBlock rendering. * return false if skinned/meshMorphed. */ virtual bool supportMeshBlockRendering () const; virtual bool sortPerMaterial() const; virtual uint getNumRdrPassesForMesh() const ; virtual uint getNumRdrPassesForInstance(CMeshBaseInstance *inst) const ; virtual void beginMesh(CMeshGeomRenderContext &rdrCtx) ; virtual void activeInstance(CMeshGeomRenderContext &rdrCtx, CMeshBaseInstance *inst, float polygonCount, void *vbDst) ; virtual void renderPass(CMeshGeomRenderContext &rdrCtx, CMeshBaseInstance *inst, float polygonCount, uint rdrPass) ; virtual void endMesh(CMeshGeomRenderContext &rdrCtx) ; virtual bool getVBHeapInfo(uint &vertexFormat, uint &numVertices); virtual void computeMeshVBHeap(void *dst, uint indexStart); // @} // Is this mesh Geom has a VertexProgram bound? virtual bool hasMeshVertexProgram() const {return _MeshVertexProgram!=NULL;} // get the Mesh VertexProgram IMeshVertexProgram *getMeshVertexProgram() const {return _MeshVertexProgram;} // ************************ private: /// A block of primitives, sorted by material used. class CRdrPass { public: // The id of this material. uint32 MaterialId; // The list of primitives. CIndexBuffer PBlock; // The same, shifted for VBHeap rendering. CIndexBuffer VBHeapPBlock; // Serialize a rdrpass. void serial(NLMISC::IStream &f) { (void)f.serialVersion(0); f.serial(MaterialId); f.serial(PBlock); } CRdrPass() { NL_SET_IB_NAME(PBlock, "CMesh::CRdrPass::PBlock"); NL_SET_IB_NAME(VBHeapPBlock, "CMesh::CRdrPass::VBHeapPBlock"); PBlock.setFormat(NL_MESH_INDEX_FORMAT); } }; /// A block of RdrPasses, sorted by matrix use. class CMatrixBlock { public: // ctor CMatrixBlock() : NumMatrix(0) { std::fill(MatrixId, MatrixId + IDriver::MaxModelMatrix, 0); } /// Which matrix we use for this block. uint32 MatrixId[IDriver::MaxModelMatrix]; /// Number of matrix actually used. uint32 NumMatrix; /// List of rdr pass, for this matrix block. std::vector RdrPass; void serial(NLMISC::IStream &f) { (void)f.serialVersion(0); // Code written for IDriver::MaxModelMatrix==16 matrixs. nlctassert(IDriver::MaxModelMatrix == 16); for(uint i=0;i TBoneMap; typedef TBoneMap::iterator ItBoneMap; /** Just for build process. A Triangle face. */ struct CFaceTmp { CCornerTmp Corner[3]; uint MaterialId; // which matrixblock own this face. -1 <=> Not owned. sint MatrixBlockId; CFaceTmp() { MatrixBlockId= -1; } CFaceTmp &operator=(const CMesh::CFace& o) { Corner[0]= o.Corner[0]; Corner[1]= o.Corner[1]; Corner[2]= o.Corner[2]; MaterialId= o.MaterialId; return *this; } void buildBoneUse(std::vector &boneUse, std::vector &skinWeights); }; /** Just for build process. A MatrixBlock remap. */ class CMatrixBlockRemap { public: uint32 Remap[IDriver::MaxModelMatrix]; }; private: /** Skinning: this is the list of vertices (mirror of VBuffer), at the bind Pos. * Potentially modified by the mesh morpher */ std::vector _OriginalSkinVertices; std::vector _OriginalSkinNormals; std::vector _OriginalTGSpace; /// VBuffer of the mesh (potentially modified by the mesh morpher and skinning) CVertexBuffer _VBuffer; /// The original VBuffer of the mesh used only if there are blend shapes. CVertexBuffer _VBufferOri; /// The matrix blocks. std::vector _MatrixBlocks; /// For clipping. NLMISC::CAABBoxExt _BBox; /// This tells if the mesh is correctly skinned. bool _Skinned; /// This tells if the mesh VBuffer has coorect BindPos vertices bool _OriginalSkinRestored; /// This boolean is true if the bones id have been passed in the skeleton bool _BoneIdComputed; /// true if the _BonesIdExt have been computed (for bone Usage). bool _BoneIdExtended; /// see CTransform::getSkinBoneSphere() doc for the meaning of this value std::vector _BonesSphere; /// This array give the name of the local bones used. std::vector _BonesName; /// This array give the index in the skeleton of the local bones used. computed at first computeBoneId() std::vector _BonesId; /// Same as _BonesId but with parent of bones added. (used for bone usage) std::vector _BonesIdExt; /// \name Mesh Block Render Implementation // @{ /// setuped at compileRunTime. enum TMBRSupport { MBROk= 1, MBRSortPerMaterial= 2, MBRCurrentUseVP= 4, }; // Of if don't support MBR at all uint8 _SupportMBRFlags; // @} /// Estimate if we must do a Precise clipping (ie with bboxes) bool _PreciseClipping; // The Mesh Morpher CMeshMorpher *_MeshMorpher; // Possible MeshVertexProgram to apply at render() NLMISC::CSmartPtr _MeshVertexProgram; private: // Locals, for build. class CCornerPred { public: bool operator()(const CCornerTmp *x, const CCornerTmp *y) const { return (*x<*y); } }; typedef std::set TCornerSet; typedef TCornerSet::iterator ItCornerSet; // Find and fill the VBuffer. void findVBId(TCornerSet &corners, const CCornerTmp *corn, sint ¤tVBIndex, const CVector &vert, const CMesh::CMeshBuild &mb) { ItCornerSet it= corners.find(const_cast(corn)); if(it!=corners.end()) corn->VBId= (*it)->VBId; else { // Add corner to the set to not insert same corner two times. corners.insert (const_cast(corn)); sint i; corn->VBId= currentVBIndex++; // Fill the VBuffer. _VBuffer.setNumVertices(currentVBIndex); sint id= currentVBIndex-1; CVertexBufferReadWrite vba; _VBuffer.lock (vba); // XYZ. vba.setVertexCoord(id, vert); // Normal if(CCornerTmp::Flags & CVertexBuffer::NormalFlag) vba.setNormalCoord(id, corn->Normal); // Uvws. for(i=0;iUvws[i].U, corn->Uvws[i].V); break; case 3: vba.setValueFloat3Ex((CVertexBuffer::TValue) (CVertexBuffer::TexCoord0 + i), id, corn->Uvws[i].U, corn->Uvws[i].V, corn->Uvws[i].W); break; default: // not supported nlassert(0); break; } } } // Color. if(CCornerTmp::Flags & CVertexBuffer::PrimaryColorFlag) vba.setColor(id, corn->Color); // Specular. if(CCornerTmp::Flags & CVertexBuffer::SecondaryColorFlag) vba.setSpecular(id, corn->Specular); // setup palette skinning. if ((CCornerTmp::Flags & CVertexBuffer::PaletteSkinFlag)==CVertexBuffer::PaletteSkinFlag) { vba.setPaletteSkin(id, corn->Palette); for(i=0;iWeights[i]); } } } // optimize triangles order of all render pass. void optimizeTriangleOrder(); // Some runtime not serialized compilation void compileRunTime(); /// \name Skinning // @{ enum TSkinType {SkinPosOnly=0, SkinWithNormal, SkinWithTgSpace}; // build skinning. void buildSkin(CMesh::CMeshBuild &m, std::vector &tmpFaces); // Build bone Usage information for serialized mesh <= version 3. void buildBoneUsageVer3 (); // bkup from VBuffer into _OriginalSkin* void bkupOriginalSkinVertices(); // restore from _OriginalSkin* to VBuffer. set _OriginalSkinRestored to true void restoreOriginalSkinVertices(); // apply Skin to all vertices from _OriginalSkin* to _VBuffer. void applySkin(CSkeletonModel *skeleton); void flagSkinVerticesForMatrixBlock(uint8 *skinFlags, CMatrixBlock &mb); void computeSkinMatrixes(CSkeletonModel *skeleton, CMatrix3x4 *matrixes, CMatrixBlock *prevBlock, CMatrixBlock &curBlock); void computeSoftwarePointSkinning(CMatrix3x4 *matrixes, CVector *srcVector, CPaletteSkin *srcPal, float *srcWgt, CVector *dstVector); void computeSoftwareVectorSkinning(CMatrix3x4 *matrixes, CVector *srcVector, CPaletteSkin *srcPal, float *srcWgt, CVector *dstVector); // Shadow mapping and CMesh. NB: not serialized, but created at each load CShadowSkin _ShadowSkin; // build the shadow skin, from the VertexBuffer/IndexBuffer void buildShadowSkin(); // @} }; } // NL3D #endif // NL_MESH_H /* End of mesh.h */