////////////////////////////////////////////////////////////////////// // // CryEngine Source code // // File:StencilShadowConnectivity.h // Declaration of class CStencilShadowConnectivity // // History: // -:Created by Sergiy Migdalskiy // 08/30/2002 include .cpp in header files, move to CryCommon, use in Cry3DEngine (Martin Mittring) // ////////////////////////////////////////////////////////////////////// // class CStencilShadowConnectivity // // This is pre-computed data for a model that's gonna cast stencil // shadows, and whose vertices may change position ****, but the topology // cannot change. // // Ok, to put it clear: for each model, you need one instance of this // class initialized once. The model is allowed to deform, but is not // allowed to change topology (add or remove faces as well as re-assign // face vertices or even change face orientation). // // NOTE: The class is optimized for use with manifold geometry. // This means you have to provide a model with unified normals and non- // exploded triangles. // // // IMPLEMENTATION NOTES should be in // StencilShadowConnectivity.cpp // #pragma once #ifndef _STENCIL_SHADOW_CONNECTIVITY_HDR_ #define _STENCIL_SHADOW_CONNECTIVITY_HDR_ #include // IStencilShadowConnectivity // the empty connectivity info: class CStencilShadowConnectivityNull:public IStencilShadowConnectivity { public: //! don't forget to call Release for freeing the memory resources virtual void Release( void ) {delete this;} //! to keep the interface small and the access fast //! /return pointer to the internal memory representation (only used within module 3DEngine) const virtual CStencilShadowConnectivity *GetInternalRepresentation( void ) const {return NULL;} //! for debugging and profiling //! /param outVertexCount //! /param outTriangleCount virtual void GetStats( DWORD &outVertexCount, DWORD &outTriangleCount ) {outVertexCount = outTriangleCount = 0;} //! Memorize the vertex buffer in this connectivity object. This is needed if a static object doesn't need this info virtual void SetVertices(const Vec3d* pVertices, unsigned numVertices) {assert(0);} //! Serializes this object to the given memory block; if it's NULL, only returns //! the number of bytes required. If it's not NULL, returns the number of bytes written //! (0 means error, insufficient space) virtual unsigned Serialize (bool bSave, void* pStream, unsigned nSize, IMiniLog* pWarningLog = NULL) { return 0; } //! Calculates the size of this object virtual void GetMemoryUsage (ICrySizer* pSizer) {} #ifdef WIN32 //! /param szFilename filename with path (or relative) and extension //! /return true=success, false otherwise virtual bool DebugConnectivityInfo( const char *szFilename ){} #endif }; #ifdef WIN64 #pragma warning( push ) //AMD Port #pragma warning( disable : 4267 ) #endif class CStencilShadowConnectivity : public IStencilShadowConnectivity { public: //! number of vertices in the array referenced by the mesh //! /return vertex count unsigned numVertices() const { return m_numVertices; } //! number of edges in the array of edges //! /return edge count unsigned numEdges() const { return m_numEdges; } //! number of orphaned (open) edges //! /return orphaned (open) count virtual unsigned numOrphanEdges() const { return m_numOrphanEdges; } //! total number of faces //! /return face count unsigned numFaces ()const { return m_numFaces; } // ----------------- // This is the basic edge representation: two vertex indices. // This is capable of being sorted. struct BasicEdge { //! default constructor BasicEdge () {} //! constructor //! /param nV0 start vertex index //! /param nV1 end vertex index BasicEdge (vindex nV0, vindex nV1) { m_nVertex[0] = nV0; m_nVertex[1] = nV1; } //! start and end vertex index of the edge vindex m_nVertex[2]; // alphabet sorting implementation //! /param rEdge right side of the operator term inline bool operator < (const BasicEdge& rEdge)const { if (m_nVertex[0] < rEdge.m_nVertex[0]) return true; else if (m_nVertex[0] == rEdge.m_nVertex[0]) return m_nVertex[1] < rEdge.m_nVertex[1]; else return false; } //! get teh start or end vertex index //! /param nIndex 0/1 inline unsigned operator [] (int nIndex)const { assert(nIndex==0 || nIndex==1); return m_nVertex[nIndex]; } //! copy the basic edge, designed for ancestors //! /param beEdge void setBasicEdge (const BasicEdge& beEdge) { m_nVertex[0] = beEdge.m_nVertex[0]; m_nVertex[1] = beEdge.m_nVertex[1]; } }; // ----------------- // connection of the edge to the face: may contain, depending on the implementation, // the face index, pointer to it, vertex, pointer to it struct EdgeFace { //! default constructor EdgeFace (){} //! constructor //! /param nFace face index //! /param nVertex vertex index EdgeFace (vindex nFace, vindex nVertex): m_nFace(nFace), m_nVertex(nVertex) {} //! /return face index inline vindex getFaceIndex ()const {return m_nFace;} //! /return vertex index inline vindex getVertexIndex() const {return m_nVertex;} vindex m_nFace; //!< face index vindex m_nVertex; //!< face vertex }; // ----------------- // This is a primary connectivity edge data structure: // the basic edge (start and end vertices, inherited from BasicEdge) // and the opposite face edges class Edge: public BasicEdge { //! this and opposite face 3rd vertex. //! in "this" face, the edge goes CCW (natural) direction [0], //! and in "opposite" face it goes CW (reverse) direction [1] EdgeFace m_Face[2]; public: //! default constructor Edge () {} //! constructor //! /param be edge datastructure //! /param ef0 in this triangle the edge goes CCW (natural) direction //! /param ef1 in this triangle the edge goes CW (reverse) direction Edge (const BasicEdge& be, const EdgeFace& ef0, const EdgeFace& ef1): BasicEdge(be) { m_Face[0] = ef0; m_Face[1] = ef1; } //! /param nFace 0/1 //! /return reference to the face data structure const EdgeFace& getFace(int nFace) const { assert(nFace==0 || nFace==1); return m_Face[nFace]; } //! /param nFace 0/1 //! /param ef reference to the edge datastructure void setFace (int nFace, const EdgeFace& ef) { assert(nFace==0 || nFace==1); m_Face[nFace] = ef; } }; // ----------------- // orphan edge - an edge with only one face attached to it (boundary) class OrphanEdge: public BasicEdge { EdgeFace m_Face; //!< public: //! default constructor OrphanEdge () {} // constructor OrphanEdge (const BasicEdge& be, const EdgeFace& ef): BasicEdge (be), m_Face (ef) {} const EdgeFace& getFace()const {return m_Face;} void setFace (const EdgeFace& ef) {m_Face = ef;} }; // ----------------- // face - 3 vertices class Face { public: vindex m_nVertex[3]; //!< //! default constructur Face () {} //! constructor //! /param nV0 first vertex index //! /param nV1 second vertex index //! /param nV2 thired vertex index Face (vindex nV0, vindex nV1, vindex nV2) { m_nVertex[0] = nV0; m_nVertex[1] = nV1; m_nVertex[2] = nV2; } //! get the index of one triangle vertex //! /param nVtx 0/1/2 //! /return index of that triangle vertex const vindex getVertex (int nVtx) const { assert(nVtx==0 || nVtx==1 || nVtx==2); return m_nVertex[nVtx]; } //! set the index of one triangle vertex //! /param nVtx 0/1/2 //! /param nIndex index of the triangle vertex no void setVertex (int nVtx, vindex nIndex) { assert(nVtx==0 || nVtx==1 || nVtx==2); m_nVertex[nVtx] = nIndex; } }; // ----------------- //! /aparam nEdge 0..m_numEdges-1 //! /return the nEdge'th edge const Edge& getEdge (int nEdge)const { assert(m_pEdges); assert(nEdge>=0); assert((unsigned)nEdge=0); assert((unsigned)nEdge=0); assert((unsigned)nFace& arrFaces) { assert (!m_pFaces && m_numFaces <= arrFaces.size()); if (!arrFaces.empty()) { m_pFaces = new Face[m_numFaces = arrFaces.size()]; for (unsigned i = 0; i < m_numFaces; ++i) m_pFaces[i] = arrFaces[i]; } } // destruct the object (it's only constructed by the Builder) // the virtual ensures that the function releases the object in the same module where it was allocated: // because the VTable was also allocated in the same module virtual void Release () { assert(this); // this is a counterpart to the new that's called within the // CStencilShadowConnectivityBuilder::ConstructConnectivity() method delete this; } const virtual CStencilShadowConnectivity *GetInternalRepresentation( void ) const { return(this); } // from IStencilShadowConnectivity virtual bool DebugConnectivityInfo( const char *szFilename ) { // used only for debugging FILE *out=fopen(szFilename,"w"); if(!out)return(false); fprintf(out,"%d Edges:\n",m_numEdges); for(unsigned i=0;iAddObject(this,Serialize (true, NULL,0)); } protected: //! constructor is only called within 3DEngine //! /param pEdges - array of nNumEdges edges //! /param nNumEdges CStencilShadowConnectivity( const std::vector& arrEdges) { m_pOrphanEdges=NULL; m_pFaces=NULL; m_numOrphanEdges=0; // find the max vertex index to put it into m_nVertices m_numVertices = 0; m_numFaces = 0; for (std::vector::const_iterator it = arrEdges.begin(); it != arrEdges.end(); ++it) { UseBasicEdge (*it); UseEdgeFace (it->getFace(0)); UseEdgeFace (it->getFace(1)); } m_numEdges = arrEdges.size(); if(m_numEdges) { m_pEdges = new Edge[m_numEdges]; for (unsigned i = 0; i < m_numEdges; ++i) m_pEdges[i] = arrEdges[i]; } else m_pEdges=0; m_pPlanes = NULL; m_pVertices = NULL; } void SetPlanes (const Plane* pPlanes, unsigned numPlanes) { if (m_pPlanes) delete [] m_pPlanes; if (pPlanes) { assert (numPlanes >= m_numFaces); m_pPlanes = new Plane[m_numFaces]; memcpy (m_pPlanes, pPlanes, sizeof(Plane) * m_numFaces); } else m_pPlanes = NULL; } void SetVertices(const Vec3d* pVertices, unsigned numVertices) { if (m_pVertices) delete[]m_pVertices; if (pVertices) { assert (numVertices >= m_numVertices); m_pVertices = new Vec3d[m_numVertices]; memcpy (m_pVertices, pVertices, sizeof(Vec3d)*m_numVertices); } else m_pVertices = NULL; } // remaps all vertex indices and memorizes the passed "new" vertex array (in new indexation) void SetRemapVertices (const vindex*pMap, unsigned numMapEntries, const Vec3d* pNewVertices, unsigned numNewVertices) { if (m_pVertices) delete[]m_pVertices; assert (pNewVertices); // we collected information about the vertices before . The number of used vertices is in // m_numVertices. We assume the passed new vertex map can not make this number larger, because // its sole purpose is optimization of number of vertex indices used (for storing the vertex info inside) assert (numNewVertices <= m_numVertices); m_numVertices = numNewVertices; m_pVertices = new Vec3d[numNewVertices]; memcpy (m_pVertices, pNewVertices, sizeof(Vec3d)*numNewVertices); remapVertexIndices (pMap, numMapEntries); } // can be destructed only from within Release() virtual ~CStencilShadowConnectivity(void) { if (m_pEdges) delete[] m_pEdges; if (m_pOrphanEdges) delete[] m_pOrphanEdges; if (m_pFaces) delete[] m_pFaces; if (m_pPlanes) delete[] m_pPlanes; if (m_pVertices) delete[] m_pVertices; } // makes sure that the given vertex index fits into the range 0..m_nVertices // by expanding (increasing) m_nVertices void UseVertex(vindex nVertex) { if (nVertex >= m_numVertices) m_numVertices = nVertex + 1; } // makes sure that the given face index fits into the range 0..m_nFaces // by expanding (increasing) m_nFaces void UseFace (vindex nFace) { if (nFace >= m_numFaces) m_numFaces = nFace + 1; } // copies the list of orphaned edges into internal array // this is specially optimized for faster cooperation with the Builder // DO NOT call twice, this is only designed to be called once after construction typedef std::map OrphanEdgeMap; // copies the list of orphaned edges into internal array // this is specially optimized for faster cooperation with the Builder // DO NOT call twice, this is only designed to be called once after construction void SetOrphanEdges (const OrphanEdgeMap& mapOrphanEdge) { m_numOrphanEdges = mapOrphanEdge.size(); m_pOrphanEdges = new OrphanEdge[m_numOrphanEdges]; OrphanEdge* pEdge = m_pOrphanEdges; // copy the content of the orphan edge map into the array for (OrphanEdgeMap::const_iterator it = mapOrphanEdge.begin(); it != mapOrphanEdge.end(); ++it) { pEdge->setBasicEdge (it->first); pEdge->setFace (it->second); UseBasicEdge (*pEdge); UseEdgeFace (pEdge->getFace()); ++pEdge; } } // makes sure that the vertices/faces referenced by the edge are in account by the m_numFaces and m_numVertices void UseBasicEdge (const BasicEdge& beEdge) { UseVertex(beEdge[0]); UseVertex(beEdge[1]); } // makes sure that the vertices/faces referenced by the edge are in account by the m_numFaces and m_numVertices void UseEdgeFace (const EdgeFace& efEdge) { UseFace (efEdge.getFaceIndex()); UseVertex(efEdge.getVertexIndex()); } //! for debugging and profiling //! /param outVertexCount //! /param outTriangleCount virtual void GetStats( DWORD &outVertexCount, DWORD &outTriangleCount ) { outVertexCount=m_numVertices; outTriangleCount=m_numFaces; } public: // the version of the stream enum {g_nSerialVersion = 2}; //! Deserializes this object. Returns the number of bytes read. 0 means error //! pWarningLog is used to put warnings about deserialized connectivity unsigned Deserialize (void *pStream, unsigned nSize, IMiniLog* pWarningLog = NULL) { unsigned* pHeader = (unsigned*)pStream; if (nSize < 5 * sizeof(unsigned)) return 0; // we should have the header in place if (*(pHeader++) != g_nSerialVersion) // version is incompatible return 0; m_numEdges = *(pHeader++); m_numOrphanEdges = *(pHeader++); m_numVertices = *(pHeader++); m_numFaces = *(pHeader++); unsigned numPlanes = *(pHeader++); assert (numPlanes == 0 || numPlanes == m_numFaces); unsigned numVertices = *(pHeader++); assert (numVertices == 0 || numVertices == m_numVertices); unsigned nRequiredSize = sizeof(unsigned) + sizeof(m_numEdges) + m_numEdges * sizeof(Edge) + sizeof(m_numOrphanEdges) + m_numOrphanEdges * sizeof(OrphanEdge) + sizeof(m_numVertices) + sizeof(m_numFaces) + m_numFaces * sizeof(Face) + sizeof(numPlanes) + numPlanes * sizeof(Plane)+ sizeof(numVertices) + numVertices * sizeof(Vec3d); if(m_pEdges) delete[] m_pEdges; if(m_pOrphanEdges) delete[] m_pOrphanEdges; if(m_pFaces) delete[] m_pFaces; if(m_pPlanes) delete[] m_pPlanes; if (m_pVertices) delete []m_pVertices; if (nSize < nRequiredSize) { m_pEdges = NULL; m_pOrphanEdges = NULL; m_pFaces = NULL; m_numEdges = 0; m_numOrphanEdges = 0; m_numVertices = 0; m_numFaces = 0; // incompatible stream return 0; } m_pEdges = new Edge [m_numEdges]; m_pOrphanEdges = new OrphanEdge [m_numOrphanEdges]; m_pFaces = new Face [m_numFaces]; m_pPlanes = numPlanes?new Plane[numPlanes]:NULL; m_pVertices = numVertices?new Vec3d[numVertices]:NULL; Edge* pEdgeData = (Edge*)pHeader; memcpy (m_pEdges, pEdgeData, m_numEdges*sizeof(Edge)); OrphanEdge* pOrphanEdgeData = (OrphanEdge*)(pEdgeData + m_numEdges); memcpy (m_pOrphanEdges, pOrphanEdgeData, m_numOrphanEdges*sizeof(OrphanEdge)); Face* pFaceData = (Face*)(pOrphanEdgeData + m_numOrphanEdges); memcpy (m_pFaces, pFaceData, m_numFaces * sizeof(Face)); Plane* pPlanes = (Plane*)(pFaceData+m_numFaces); if (numPlanes) memcpy (m_pPlanes, pPlanes, numPlanes* sizeof(Plane)); Vec3d* pVertices = (Vec3d*)(pPlanes + numPlanes); if (numVertices) memcpy (m_pVertices, pVertices, sizeof(Vec3d)*numVertices); #if !defined(LINUX) if (m_numOrphanEdges && pWarningLog) pWarningLog->LogWarning("Connectivity warning: there are %d open edges (%d closed, %d faces, %d planes, %d vertices)", m_numOrphanEdges, m_numEdges, m_numFaces, numPlanes, numVertices); #endif return nRequiredSize; } //! Serializes this object to the given memory block; if it's NULL, only returns //! the number of bytes required. If it's not NULL, returns the number of bytes written //! (0 means error, insufficient space) unsigned Serialize (bool bSave, void* pStream, unsigned nSize, IMiniLog* pWarningLog = NULL) { if (!bSave) return Deserialize (pStream, nSize, pWarningLog); // calculate the required size of the buffer unsigned numPlanes = m_pPlanes?m_numFaces:0, numVertices = m_pVertices?m_numVertices:0, nRequiredSize = sizeof(unsigned) + sizeof(m_numEdges) + m_numEdges * sizeof(Edge) + sizeof(m_numOrphanEdges) + m_numOrphanEdges * sizeof(OrphanEdge) + sizeof(m_numVertices) + sizeof(m_numFaces) + m_numFaces * sizeof(Face) + sizeof(numPlanes) + numPlanes * sizeof(Plane)+ sizeof(numVertices) + numVertices * sizeof(Vec3d); if (pStream) { if (nSize < nRequiredSize) return 0; // insufficient space in the pStream buffer unsigned* pHeader = (unsigned*)pStream; *(pHeader++) = g_nSerialVersion; // version of the stream *(pHeader++) = m_numEdges; *(pHeader++) = m_numOrphanEdges; *(pHeader++) = m_numVertices; *(pHeader++) = m_numFaces; *(pHeader++) = numPlanes; *(pHeader++) = numVertices; Edge* pEdgeData = (Edge*)pHeader; memcpy (pEdgeData, m_pEdges, m_numEdges*sizeof(Edge)); OrphanEdge* pOrphanEdgeData = (OrphanEdge*)(pEdgeData + m_numEdges); memcpy (pOrphanEdgeData, m_pOrphanEdges, m_numOrphanEdges*sizeof(OrphanEdge)); Face* pFaceData = (Face*)(pOrphanEdgeData + m_numOrphanEdges); memcpy (pFaceData, m_pFaces, m_numFaces * sizeof(Face)); Plane* pPlanes = (Plane*)(pFaceData+m_numFaces); if (m_pPlanes) memcpy (pPlanes, m_pPlanes, numPlanes * sizeof(Plane)); Vec3d* pVertices = (Vec3d*)(pPlanes+numPlanes); if (m_pVertices) memcpy (pVertices, m_pVertices, numVertices * sizeof(Vec3d)); } return nRequiredSize; } // the plane equation, x*n+d == 0 bool hasPlanes() const {return m_pPlanes != NULL;} const Plane&getPlane (unsigned i)const {assert(i