// 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