#include "stdafx.h" // precompiled header #include "StencilShadowConnectivity.h" #include "StencilShadowConnectivityBuilder.h" #ifdef WIN64 #pragma warning( push ) //AMD Port #pragma warning( disable : 4267 ) #endif // creates an empty mesh connectivity CStencilShadowConnectivityBuilder::CStencilShadowConnectivityBuilder( void ) #ifdef DEBUG_STD_CONTAINERS :m_mapSingleEdges("CStencilShadowConnectivityBuilder.SingleEdges") ,m_vDoubleEdges ("CStencilShadowConnectivityBuilder.DoubleEdges") ,m_vFaces("CStencilShadowConnectivityBuilder.Faces") ,m_mVertexWelder("CStencilShadowConnectivityBuilder.VertexWelder") #endif { Reinit(); } CStencilShadowConnectivityBuilder::~CStencilShadowConnectivityBuilder( void ) { } void CStencilShadowConnectivityBuilder::Reinit( void ) { m_mapSingleEdges.clear(); m_vDoubleEdges.clear(); m_vFaces.clear(); m_mVertexWelder.clear(); m_dwNumUncompressedVertices=0; // m_fWeldTolerance = 0; } /////////////////////////////////////////////////////////////////////////////////// // adds a single triangle to the mesh // the tirangle is defined by three vertices, in counter-clockwise order // these vertex indices will be used later when accessing the array of // deformed character/model vertices to determine the shadow volume boundary /////////////////////////////////////////////////////////////////////////////////// void CStencilShadowConnectivityBuilder::AddTriangle( unsigned short nV0, unsigned short nV1, unsigned short nV2 ) { unsigned nNumFaces = m_vFaces.size(); AddNewEdge (BasicEdge(nV0, nV1), EdgeFace(nNumFaces, nV2)); AddNewEdge (BasicEdge(nV1, nV2), EdgeFace(nNumFaces, nV0)); AddNewEdge (BasicEdge(nV2, nV0), EdgeFace(nNumFaces, nV1)); m_vFaces.push_back(Face (nV0, nV1, nV2)); } // this will try to find a close match to the given vertex, and // if found, return its index (the actual index of the vertex that's very close or // coincide with v in space). Otherwise, creates a new vertex reference in the map // and returns the index nNewVertex unsigned CStencilShadowConnectivityBuilder::WeldVertex (const Vec3d& v, unsigned nNewVertex) { // The easiest way: just find it directly VertexWelderMap::iterator it; it = m_mVertexWelder.find(v); if (it != m_mVertexWelder.end()) return it->second; else { /* // scan and find some very close vertex if (m_fWeldTolerance > 0) for (it = m_mVertexWelder.begin(); it != m_mVertexWelder.end(); ++it) { if ((it->first-v).len2() < m_fWeldTolerance) return it->second; } */ m_mVertexWelder.insert (VertexWelderMap::value_type(v, nNewVertex)); return nNewVertex; } } /////////////////////////////////////////////////////////////////////////////////// // Adds the triangle with the given vertex indices and vertex coordinates // to the list of triangles that will cast shadows. Welds the vertices based on their // coordinates void CStencilShadowConnectivityBuilder::AddTriangleWelded( unsigned short nV0, unsigned short nV1, unsigned short nV2, const Vec3d &vV0, const Vec3d &vV1, const Vec3d &vV2 ) { nV0=WeldVertex(vV0,nV0); nV1=WeldVertex(vV1,nV1); nV2=WeldVertex(vV2,nV2); AddTriangle(nV0,nV1,nV2); } ////////////////////////////////////////////////////////////////////////////////////// // add a new edge, if there is no complementary single edge; // otherwise, withdraw the edge from the list of single edges and add to double edges // PARAMETERS: // edge - start and end vertices of the edge going CCW along the face // efFace - the opposite vertex/face the edge belongs to ////////////////////////////////////////////////////////////////////////////////////// void CStencilShadowConnectivityBuilder::AddNewEdge (BasicEdge eEdge, EdgeFace efFace) { // first, try to find the complementary BasicEdge eComplementary (eEdge[1], eEdge[0]); SingleEdgeMap::iterator itComplementary = m_mapSingleEdges.find (eComplementary); if (itComplementary == m_mapSingleEdges.end()) { // complementeary edge not found. Add a new single edge m_mapSingleEdges.insert (SingleEdgeMap::value_type(eEdge, efFace)); // nVEdgeG for nFaceVertex[1] is unknown and doesn't matter at the moment } else { // we found the complementary edge Edge edgeNewDouble (eComplementary, itComplementary->second, efFace); m_vDoubleEdges.push_back (edgeNewDouble); m_mapSingleEdges.erase (itComplementary); } } /////////////////////////////////////////////////////////////////////////////////// // constructs/compiles the optimum representation of the connectivity // to be used in run-time // WARNING: use Release method to dispose the connectivity object /////////////////////////////////////////////////////////////////////////////////// IStencilShadowConnectivity *CStencilShadowConnectivityBuilder::ConstructConnectivity( void ) { CStencilShadowConnectivity* pConnectivity = new CStencilShadowConnectivity(m_vDoubleEdges); assert(pConnectivity); // low memeory? pConnectivity->SetOrphanEdges(m_mapSingleEdges); pConnectivity->SetFaces(m_vFaces); // find all the used vertices FaceArray::const_iterator pFace = m_vFaces.begin(); DWORD nFaceCount = m_vFaces.size(); #if 0 // test for serialization function unsigned nBufSize = pConnectivity->Serialize(true, NULL, 0); void* pBuf = malloc (nBufSize); unsigned nSaved = pConnectivity->Serialize(true, pBuf, nBufSize); assert (nSaved == nBufSize); pConnectivity->Release(); pConnectivity = new CStencilShadowConnectivity (); unsigned nLoaded = pConnectivity->Serialize(false, pBuf, nBufSize); assert (nLoaded == nBufSize); free (pBuf); #endif return((IStencilShadowConnectivity *)pConnectivity); } // returns the number of single (with no pair faces found) or orphaned edges unsigned CStencilShadowConnectivityBuilder::numOrphanedEdges ()const { return(m_mapSingleEdges.size()); } //! Returns the list of faces for orphaned edges into the given buffer; //! For each orphaned edge, one face will be returned; some faces may be duplicated void CStencilShadowConnectivityBuilder::getOrphanedEdgeFaces (unsigned* pBuffer) { for (SingleEdgeMap::iterator it = m_mapSingleEdges.begin(); it != m_mapSingleEdges.end(); ++it) *(pBuffer++) = it->second.m_nFace; } // reserves space for the given number of triangles that are to be added void CStencilShadowConnectivityBuilder::ReserveForTriangles( unsigned nNumTriangles, unsigned innNumVertices ) { m_vDoubleEdges.reserve(nNumTriangles*3/2); m_vFaces.reserve(nNumTriangles); m_dwNumUncompressedVertices=innNumVertices; } CStencilShadowStaticConnectivityBuilder::CStencilShadowStaticConnectivityBuilder() #ifdef DEBUG_STD_CONTAINERS : m_vPlanes("CStencilShadowStaticConnectivityBuilder.Planes") #endif { } CStencilShadowStaticConnectivityBuilder::~CStencilShadowStaticConnectivityBuilder() { } // reserves space for the given number of triangles that are to be added void CStencilShadowStaticConnectivityBuilder::ReserveForTriangles( unsigned nNumTriangles, unsigned innNumVertices ) { CStencilShadowConnectivityBuilder::ReserveForTriangles (nNumTriangles, innNumVertices); m_vPlanes.reserve (nNumTriangles); } //! return to the state right after construction void CStencilShadowStaticConnectivityBuilder::Reinit( void ) { CStencilShadowConnectivityBuilder::Reinit(); m_vPlanes.clear(); } ////////////////////////////////////////////////////////////////////////// // Adds the triangle with the given vertex indices and vertex coordinates // to the list of triangles that will cast shadows. Welds the vertices based on their // coordinates. Calculates and remembers the normals for each triangle void CStencilShadowStaticConnectivityBuilder::AddTriangleWelded( vindex nV0, vindex nV1, vindex nV2, const Vec3d &vV0, const Vec3d &vV1, const Vec3d &vV2 ) { CStencilShadowConnectivityBuilder::AddTriangleWelded (nV0, nV1, nV2, vV0, vV1, vV2); m_vPlanes.push_back(Plane (vV0,vV1,vV2)); } // constructs/compiles the optimum representation of the connectivity // to be used in run-time // WARNING: use Release method to dispose the connectivity object //! /param inpVertexBuf vertex position buffer to check for solvable open edges (2 vertices with same position) //! /return interface pointer, could be 0 class IStencilShadowConnectivity *CStencilShadowStaticConnectivityBuilder::ConstructConnectivity () { if (m_vPlanes.empty()) return new CStencilShadowConnectivity(); CStencilShadowConnectivity * pConnectivity = (CStencilShadowConnectivity *)CStencilShadowConnectivityBuilder::ConstructConnectivity()->GetInternalRepresentation(); pConnectivity->SetPlanes(&m_vPlanes[0], m_vPlanes.size()); SetVertices (pConnectivity); return pConnectivity; }; ////////////////////////////////////////////////////////////////////////// // WARNING: This is only to be called when the whole construction process // is finished, to modify already created connectivity // Goes through all vertices used by the connectivity and constructs a continuous // array out of them; reindexes the vertices in the connectivity object // to use these vertices; puts the new vertex array into the connectivity object void CStencilShadowStaticConnectivityBuilder::SetVertices (CStencilShadowConnectivity * pConnectivity) { // scan through all used vertices and put them into continuous array std::vector arrNewVertices; // the vertices array in new indexation std::vector arrOldToNewMap; // the mapping old->new indexation arrNewVertices.reserve (pConnectivity->numVertices()); arrOldToNewMap.resize (pConnectivity->numVertices(),-1); for (VertexWelderMap::const_iterator it = m_mVertexWelder.begin(); it != m_mVertexWelder.end(); ++it) { #if 1 // this will remap the vertex indices arrOldToNewMap[it->second] = arrNewVertices.size(); arrNewVertices.push_back(it->first); #else // this will not change indexation arrOldToNewMap[it->second] = it->second; if (arrNewVertices.size() <= it->second) arrNewVertices.resize (it->second+1, Vec3d(0,0,0)); arrNewVertices[it->second] = it->first; #endif } // now renumber the vertices pConnectivity->SetRemapVertices(&arrOldToNewMap[0], arrOldToNewMap.size(), &arrNewVertices[0], arrNewVertices.size()); } #ifdef WIN64 #pragma warning( pop ) //AMD Port #endif