// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/> // 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 <http://www.gnu.org/licenses/>. #include <map> #include <vector> #include "nel/../../src/pacs/collision_mesh_build.h" #include "nel/../../src/pacs/local_retriever.h" #include "nel/../../src/pacs/exterior_mesh.h" #include "mouline.h" #include "build_surfaces.h" using namespace std; using namespace NLMISC; using namespace NLPACS; /* // a reference on an edge struct CEdgeKey { uint32 V0; uint32 V1; CEdgeKey() {} CEdgeKey(uint32 v0, uint32 v1) : V0(v0), V1(v1) {} bool operator() (const CEdgeKey &a, const CEdgeKey &b) { return a.V0 < b.V0 || (a.V0 == b.V0 && a.V1 < b.V1); } }; // the info on an edge struct CEdgeInfo { sint32 Left, LeftEdge; sint32 Right, RightEdge; CEdgeInfo(sint32 left=-1, sint32 leftEdge=-1, sint32 right=-1, sint32 rightEdge=-1) : Left(left), LeftEdge(leftEdge), Right(right), RightEdge(rightEdge) {} }; typedef map<CEdgeKey, CEdgeInfo, CEdgeKey> TLinkRelloc; typedef TLinkRelloc::iterator ItTLinkRelloc; // void linkMesh(CCollisionMeshBuild &cmb, bool linkInterior) { uint i, j; TLinkRelloc relloc; // check each edge of each face for (i=0; i<cmb.Faces.size(); ++i) { if (cmb.Faces[i].Surface == CCollisionFace::ExteriorSurface && linkInterior || cmb.Faces[i].Surface >= CCollisionFace::InteriorSurfaceFirst && !linkInterior) continue; for (j=0; j<3; ++j) { cmb.Faces[i].Edge[j] = -1; uint edge = (j+2)%3; uint32 va = cmb.Faces[i].V[j], vb = cmb.Faces[i].V[(j+1)%3]; ItTLinkRelloc it; if ((it = relloc.find(CEdgeKey(va, vb))) != relloc.end()) { // in this case, the left triangle of the edge has already been found. // should throw an error nlerror("On face %d, edge %d: left side of edge (%d,%d) already linked to face %d", i, edge, va, vb, (*it).second.Left); } else if ((it = relloc.find(CEdgeKey(vb, va))) != relloc.end()) { // in this case, we must check the right face has been set yet if ((*it).second.Right != -1) { nlerror("On face %d, edge %d: right side of edge (%d,%d) already linked to face %d", i, edge, vb, va, (*it).second.Right); } (*it).second.Right = i; (*it).second.RightEdge = edge; } else { // if the edge wasn't present yet, create it and set it up. relloc.insert(make_pair(CEdgeKey(va, vb), CEdgeInfo(i, edge, -1, -1))); } } } // for each checked edge, update the edge info inside the faces ItTLinkRelloc it; for (it=relloc.begin(); it!=relloc.end(); ++it) { sint32 left, leftEdge; sint32 right, rightEdge; // get the link info on the edge left = (*it).second.Left; leftEdge = (*it).second.LeftEdge; right = (*it).second.Right; rightEdge = (*it).second.RightEdge; // update both faces if (left != -1) cmb.Faces[left].Edge[leftEdge] = right; if (right != -1) cmb.Faces[right].Edge[rightEdge] = left; } } */ void buildExteriorMesh(CCollisionMeshBuild &cmb, CExteriorMesh &em) { uint startFace = 0; vector<CExteriorMesh::CEdge> edges; uint i; for (i=0; i<cmb.Faces.size(); ++i) { cmb.Faces[i].EdgeFlags[0] = false; cmb.Faces[i].EdgeFlags[1] = false; cmb.Faces[i].EdgeFlags[2] = false; } while (true) { // find the first non interior face uint i, edge; bool found = false; for (i=startFace; i<cmb.Faces.size() && !found; ++i) { if (cmb.Faces[i].Surface != CCollisionFace::ExteriorSurface) continue; for (edge=0; edge<3 && !found; ++edge) if (cmb.Faces[i].Edge[edge] == -1 && !cmb.Faces[i].EdgeFlags[edge]) { // nlassert(cmb.Faces[i].Material != 0xdeadbeef); found = true; break; } if (found) break; } // if (!found) break; // cmb.Faces[i].Material = 0xdeadbeef; startFace = i+1; sint32 current = i; sint32 next = cmb.Faces[current].Edge[edge]; sint oedge; sint pivot = (edge+1)%3; sint nextEdge = edge; bool allowThis = true; uint numLink = 0; uint firstEdge = (uint)edges.size(); vector<CExteriorMesh::CEdge> loop; while (true) { if (cmb.Faces[current].EdgeFlags[nextEdge]) { // if reaches the end of the border, then quits. break; } else if (next == -1) { // if the next edge belongs to the border, then go on the same element cmb.Faces[current].EdgeFlags[nextEdge] = true; /// \todo get the real edge link sint link = (cmb.Faces[current].Visibility[nextEdge]) ? -1 : 0; //(numLink++); //edges.push_back(CExteriorMesh::CEdge(cmb.Vertices[cmb.Faces[current].V[pivot]], link)); loop.push_back(CExteriorMesh::CEdge(cmb.Vertices[cmb.Faces[current].V[pivot]], link)); pivot = (pivot+1)%3; nextEdge = (nextEdge+1)%3; next = cmb.Faces[current].Edge[nextEdge]; } else { // if the next element is inside the surface, then go to the next element for (oedge=0; oedge<3 && cmb.Faces[next].Edge[oedge]!=current; ++oedge) ; nlassert(oedge != 3); current = next; pivot = (oedge+2)%3; nextEdge = (oedge+1)%3; next = cmb.Faces[current].Edge[nextEdge]; } } // mark the end of a ext mesh block // this way, collisions won't be checked in the pacs engine if (loop.size() >= 3) { uint n = (uint)loop.size(); while (loop.front().Link >= 0 && loop.back().Link >= 0 && n > 0) { loop.push_back(loop.front()); loop.erase(loop.begin()); --n; } } loop.push_back(loop.front()); loop.back().Link = -2; edges.insert(edges.end(), loop.begin(), loop.end()); //edges.push_back(edges[firstEdge]); //edges.back().Link = -2; } bool previousWasLink = false; sint previousLink = -1; for (i=0; i<edges.size(); ++i) { // nldebug("ext-mesh: vertex=%d (%.2f,%.2f,%.2f) link=%d", i, edges[i].Start.x, edges[i].Start.y, edges[i].Start.z, edges[i].Link); if (edges[i].Link >= 0) { if (!previousWasLink) ++previousLink; edges[i].Link = previousLink; previousWasLink = true; } else { previousWasLink = false; } } em.setEdges(edges); } // void linkExteriorToInterior(CLocalRetriever &lr) { CExteriorMesh em = lr.getExteriorMesh(); vector<CExteriorMesh::CEdge> edges = em.getEdges(); vector<CExteriorMesh::CLink> links; const vector<CChain> &chains = lr.getChains(); const vector<COrderedChain3f> &ochains = lr.getFullOrderedChains(); const vector<uint16> &bchains = lr.getBorderChains(); { uint i; nlinfo("Border chains (to be linked) for this retriever:"); for (i=0; i<bchains.size(); ++i) { static char buf[512], w[256]; const CChain &chain = chains[bchains[i]]; sprintf(buf, "Border chain %d: chain=%d ", i, bchains[i]); uint och; for (och=0; och<chain.getSubChains().size(); ++och) { const COrderedChain3f &ochain = ochains[chain.getSubChain(och)]; sprintf(w, "subchain=%d", chain.getSubChain(och)); strcat(buf, w); uint v; for (v=0; v<ochain.getVertices().size(); ++v) { sprintf(w, " (%.2f,%.2f)", ochain[v].x, ochain[v].y); strcat(buf, w); } } nlinfo("%s", buf); } } uint edge, ch; for (edge=0; edge+1<edges.size(); ) { if (edges[edge].Link == -1 || edges[edge].Link == -2) { ++edge; continue; } uint startedge = edge; uint stopedge; for (stopedge=edge; stopedge+1<edges.size() && edges[stopedge+1].Link == edges[startedge].Link; ++stopedge) ; edge = stopedge+1; CVector start = edges[startedge].Start, stop = edges[stopedge+1].Start; bool found = false; for (ch=0; ch<bchains.size() && !found; ++ch) { // get the border chain. //const CChain &chain = chains[bchains[ch]]; const CVector &cstart = lr.getStartVector(bchains[ch]), &cstop = lr.getStopVector(bchains[ch]); float d = (start-cstart).norm()+(stop-cstop).norm(); if (d < 1.0e-1f) { found = true; break; } } // create a link CExteriorMesh::CLink link; if (!found) { nlwarning("in linkInteriorToExterior():"); nlwarning("couldn't find any link to the exterior edge %d-%d!!", startedge, stopedge); } else { // set it up to point on the chain and surface link.BorderChainId = ch; link.ChainId = bchains[ch]; link.SurfaceId = (uint16)chains[link.ChainId].getLeft(); } // enlarge the links if (edges[startedge].Link >= (sint)links.size()) links.resize(edges[startedge].Link+1); // if the link already exists, warning if (links[edges[startedge].Link].BorderChainId != 0xFFFF || links[edges[startedge].Link].ChainId != 0xFFFF || links[edges[startedge].Link].SurfaceId != 0xFFFF) { nlwarning("in linkInteriorToExterior():"); nlwarning("link %d already set!!", edges[startedge].Link); } // setup the link links[edges[startedge].Link] = link; } // em.setEdges(edges); em.setLinks(links); lr.setExteriorMesh(em); } // void computeRetriever(CCollisionMeshBuild &cmb, CLocalRetriever &lr, CVector &translation, 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; } uint i, j; for (i=0; i<cmb.Faces.size(); ++i) { CVector normal = ((cmb.Vertices[cmb.Faces[i].V[1]]-cmb.Vertices[cmb.Faces[i].V[0]])^(cmb.Vertices[cmb.Faces[i].V[2]]-cmb.Vertices[cmb.Faces[i].V[0]])).normed(); if (normal.z < 0.0f) { nlwarning("Face %d in cmb (%s) has negative normal! -- face is flipped", i, cmb.Faces[i].Surface == CCollisionFace::InteriorSurfaceFirst ? "interior" : "exterior"); /* std::swap(cmb.Faces[i].V[1], cmb.Faces[i].V[2]); std::swap(cmb.Faces[i].Visibility[1], cmb.Faces[i].Visibility[2]); */ } } // first link faces /* linkMesh(cmb, false); linkMesh(cmb, true); */ vector<string> errors; cmb.link(false, errors); cmb.link(true, errors); if (!errors.empty()) { nlwarning("Edge issues reported !!"); uint i; for (i=0; i<errors.size(); ++i) nlwarning("%s", errors[i].c_str()); nlerror("Can't continue."); } // translate the meshbuild to the local axis cmb.translate(translation); // find the exterior mesh border CExteriorMesh extMesh; buildExteriorMesh(cmb, extMesh); lr.setExteriorMesh(extMesh); // build the surfaces in the local retriever buildSurfaces(cmb, lr); // create the snapping faces and vertices // after the build surfaces because the InternalSurfaceId is filled within buildSurfaces()... buildSnapping(cmb, lr); // lr.computeLoopsAndTips(); lr.findBorderChains(); lr.updateChainIds(); lr.computeTopologies(); lr.unify(); lr.computeCollisionChainQuad(); /* // for (i=0; i<lr.getSurfaces().size(); ++i) lr.dumpSurface(i); */ // linkExteriorToInterior(lr); // compute the bbox of the retriever CAABBox bbox; bool first = true; for (i=0; i<extMesh.getEdges().size(); ++i) if (!first) bbox.extend(extMesh.getEdge(i).Start); else bbox.setCenter(extMesh.getEdge(i).Start), first=false; for (i=0; i<lr.getOrderedChains().size(); ++i) for (j=0; j<lr.getOrderedChain(i).getVertices().size(); ++j) if (!first) bbox.extend(lr.getOrderedChain(i)[j].unpack3f()); else bbox.setCenter(lr.getOrderedChain(i)[j].unpack3f()), first=false; CVector bboxhs = bbox.getHalfSize(); bboxhs.z = 10000.0f; bbox.setHalfSize(bboxhs); lr.setBBox(bbox); }