// 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 "stdpacs.h" #include "nel/pacs/build_indoor.h" #include "nel/pacs/collision_mesh_build.h" #include "nel/pacs/local_retriever.h" #include "nel/pacs/exterior_mesh.h" using namespace std; using namespace NLMISC; namespace NLPACS { /** * The interior surface class. Intermediate to compute real retriever surfaces * \author Benjamin Legros * \author Nevrax France * \date 2001 */ class CInteriorSurface { public: /// The collision mesh root object CCollisionMeshBuild *CollisionMeshBuild; /// The faces that compose the surface std::vector Faces; /// The Id of the surface sint32 Id; /// The center of the surface NLMISC::CVector Center; /// The material of the surface sint32 Material; public: CCollisionFace &getFace(uint face) { return CollisionMeshBuild->Faces[Faces[face]]; } CCollisionFace &getNeighbor(uint face, uint edge) { return CollisionMeshBuild->Faces[getFace(face).Edge[edge]]; } }; /** * The border of interior surfaces. * \author Benjamin Legros * \author Nevrax France * \date 2001 */ class CInteriorBorder { public: /// The vertices that compose the border std::vector Vertices; /// The left and right surfaces sint32 Left, Right; public: }; // how to build interior snapping data void buildSnapping(CCollisionMeshBuild &cmb, CLocalRetriever &lr); // how to build surfaces void buildSurfaces(CCollisionMeshBuild &cmb, CLocalRetriever &lr); // // functions to build interior surfaces and borders from mesh // // how to generate connex surfaces void floodFillSurfaces(CCollisionMeshBuild &cmb, vector &surfaces) { sint32 currentId = 0; uint i; for (i=0; i stack; stack.push_back(sint32(i)); face.InternalSurface = currentId; surfaces.resize(surfaces.size()+1); surfaces.back().Id = currentId; surfaces.back().CollisionMeshBuild = &cmb; surfaces.back().Material = face.Material; while (!stack.empty()) { uint pop = stack.back(); stack.pop_back(); surfaces.back().Faces.push_back(pop); CCollisionFace &popFace = cmb.Faces[pop]; sint32 edge, neighb; for (edge=0; edge<3; ++edge) { if ((neighb = popFace.Edge[edge]) != -1 && cmb.Faces[neighb].InternalSurface == -1 && cmb.Faces[neighb].Surface == popFace.Surface) { cmb.Faces[neighb].InternalSurface = currentId; stack.push_back(neighb); } } } ++currentId; } } // reset the edge flags of the whole collision mesh build void resetEdgeFlags(CCollisionMeshBuild &cmb) { uint face, edge; for (face=0; face &vstore, bool &loop) { CCollisionFace *current = &surface.getFace(first); CCollisionFace *next = (current->Edge[edge] == -1) ? NULL : &surface.CollisionMeshBuild->Faces[current->Edge[edge]]; current->EdgeFlags[edge] = true; sint32 currentFace = surface.Faces[first]; const sint32 currentSurfId = current->InternalSurface; const sint32 oppositeSurfId = (next != NULL) ? next->InternalSurface : (current->Visibility[edge] ? -1 : -2); sint oedge; sint pivot = (edge+sens)%3; sint nextEdge = edge; bool allowThis = true; // adds the pivot to the border and its normal vstore.push_back(surface.CollisionMeshBuild->Vertices[current->V[pivot]]); for(;;) { loop = false; // -1 means no neighbor at all, -2 means a neighbor that is not available yet sint32 thisOpposite = (next != NULL) ? next->InternalSurface : (current->Visibility[nextEdge] ? -1 : -2); if ((thisOpposite != currentSurfId && thisOpposite != oppositeSurfId) || (loop = (current->EdgeFlags[nextEdge] && !allowThis))) { // if reaches the end of the border, then quits. break; } else if (thisOpposite == oppositeSurfId) { // if the next edge belongs to the border, then go on the same element current->EdgeFlags[nextEdge] = true; if (oppositeSurfId >= 0) { for (oedge=0; oedge<3 && next->Edge[oedge]!=currentFace; ++oedge) ; nlassert(oedge != 3); nlassert(allowThis || !next->EdgeFlags[oedge]); next->EdgeFlags[oedge] = true; } pivot = (pivot+sens)%3; nextEdge = (nextEdge+sens)%3; next = (current->Edge[nextEdge] == -1) ? NULL : &surface.CollisionMeshBuild->Faces[current->Edge[nextEdge]]; vstore.push_back(surface.CollisionMeshBuild->Vertices[current->V[pivot]]); } else { // if the next element is inside the surface, then go to the next element nlassert(next->InternalSurface == currentSurfId); for (oedge=0; oedge<3 && next->Edge[oedge]!=currentFace; ++oedge) ; nlassert(oedge != 3); currentFace = current->Edge[nextEdge]; current = next; pivot = (oedge+3-sens)%3; nextEdge = (oedge+sens)%3; next = (current->Edge[nextEdge] == -1) ? NULL : &surface.CollisionMeshBuild->Faces[current->Edge[nextEdge]]; } allowThis = false; } } void computeSurfaceBorders(CInteriorSurface &surface, vector &borders) { uint face, edge; for (face=0; faceFaces[cf.Edge[edge]].InternalSurface; } nldebug("generate border %d (%d-%d)", borders.size()-1, border.Left, border.Right); bool loop; vector bwdVerts; vector &fwdVerts = border.Vertices; followBorder(surface, face, edge, 2, bwdVerts, loop); sint i; fwdVerts.reserve(bwdVerts.size()); fwdVerts.clear(); for (i=(sint)(bwdVerts.size()-1); i>=0; --i) { fwdVerts.push_back(bwdVerts[i]); } if (loop) { fwdVerts.push_back(fwdVerts.front()); } else { fwdVerts.resize(fwdVerts.size()-2); followBorder(surface, face, edge, 1, fwdVerts, loop); } } } } } void computeSurfaceCenter(CInteriorSurface &surface) { CCollisionMeshBuild &cmb = *(surface.CollisionMeshBuild); CVector center = CVector::Null; float totalWeight = 0.0f; uint i, j; for (i=0; iVertices[surface.CollisionMeshBuild->Faces[surface.Faces[i]].V[j]]; if (first) box.setCenter(v), first=false; else box.extend(v); } } quad.clear(); quad.init(4.0f, 6, box.getCenter(), std::max(box.getHalfSize().x, box.getHalfSize().y)); for (i=0; iVertices[surface.CollisionMeshBuild->Faces[surface.Faces[i]].V[j]]; quad.addVertex(v); } } quad.compile(); } void buildSurfaces(CCollisionMeshBuild &cmb, CLocalRetriever &lr) { vector surfaces; vector borders; floodFillSurfaces(cmb, surfaces); resetEdgeFlags(cmb); uint surf, bord; for (surf=0; surf=(sint)surfaces.size() || right>(sint)surfaces.size()) nlstop; lr.addChain(borders[bord].Vertices, left, right); } } // void buildSnapping(CCollisionMeshBuild &cmb, CLocalRetriever &lr) { // copy the vertices lr.getInteriorVertices() = cmb.Vertices; // create the faces uint i; vector &faces = lr.getInteriorFaces(); for (i=0; i edges; uint numLink = 0; for (i=0; i edges = em.getEdges(); vector links; const vector &chains = lr.getChains(); const vector &ochains = lr.getFullOrderedChains(); const vector &bchains = lr.getBorderChains(); { uint i; for (i=0; i= (sint)links.size()) links.resize(edges[edge].Link+1); // if the link already exists, warning if (links[edges[edge].Link].BorderChainId != 0xFFFF || links[edges[edge].Link].ChainId != 0xFFFF || links[edges[edge].Link].SurfaceId != 0xFFFF) { nlwarning("in linkInteriorToExterior():"); nlwarning("link %d already set!!", edges[edge].Link); } // setup the link links[edges[edge].Link] = link; } em.setEdges(edges); em.setLinks(links); lr.setExteriorMesh(em); } // bool computeRetriever(CCollisionMeshBuild &cmb, CLocalRetriever &lr, CVector &translation, string &error, bool useCmbTrivialTranslation) { // set the retriever lr.setType(CLocalRetriever::Interior); // if should use the own cmb bbox, then compute it if (useCmbTrivialTranslation) { translation = cmb.computeTrivialTranslation(); // snap the translation vector to a meter wide grid translation.x = (float)ceil(translation.x); translation.y = (float)ceil(translation.y); translation.z = 0.0f; } vector errors; cmb.link(false, errors); cmb.link(true, errors); if (!errors.empty()) { nlwarning("Edge issues reported !!"); uint i; error.clear(); for (i=0; i