khanat-opennel-code/code/nel/tools/pacs/build_indoor_rbank/mouline.cpp
2014-02-13 22:02:25 +01:00

486 lines
12 KiB
C++

// 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/pacs/collision_mesh_build.h"
#include "nel/pacs/local_retriever.h"
#include "nel/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);
}