// 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 #include #include "nel/pacs/collision_mesh_build.h" #include "nel/pacs/local_retriever.h" #include "nel/pacs/exterior_mesh.h" #include "nel/pacs/surface_quad.h" #include "nel/pacs/chain.h" #include "build_surfaces.h" using namespace std; using namespace NLMISC; using namespace NLPACS; // how to generate connex surfaces void floodFillSurfaces(CCollisionMeshBuild &cmb, vector &surfaces) { sint32 currentId = 0; uint i; for (i=0; i stack; stack.push_back(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]; int 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]]); while (true) { 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; /// \todo compute real surface center and quadtree 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