khanat-opennel-code/code/nel/src/pacs/local_retriever.cpp

2309 lines
62 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 "stdpacs.h"
#include "nel/misc/plane.h"
#include "local_retriever.h"
#include "collision_desc.h"
#include "retriever_instance.h"
#include "nel/misc/hierarchical_timer.h"
using namespace std;
using namespace NLMISC;
/// The max distance allowed to merge tips.
const float NLPACS::CLocalRetriever::_TipThreshold = 0.1f;
const float NLPACS::CLocalRetriever::_EdgeTipThreshold = 0.1f;
/// The threshold distance to insure a position belongs to a surface
const float InsurePositionThreshold = 2.0e-2f;
//static float hybrid2dNorm(const CVector &v)
//{
// return (float)(sqrt(sqr(v.x)+sqr(v.y))+fabs(v.z)*0.1);
//}
NLPACS::CLocalRetriever::CLocalRetriever()
{
_Type = Landscape;
_Loaded = false;
LoadCheckFlag = false;
}
void NLPACS::CLocalRetriever::clear()
{
contReset(_OrderedChains);
contReset(_FullOrderedChains);
contReset(_Chains);
contReset(_Surfaces);
contReset(__Tips);
contReset(_BorderChains);
uint i;
for (i=0; i<NumMaxCreatureModels; ++i)
contReset(_Topologies[i]);
_ChainQuad.clear();
_ExteriorMesh.clear();
contReset(_InteriorVertices);
contReset(_InteriorFaces);
_FaceGrid.clear();
_Id.resize(0);
_Loaded = false;
LoadCheckFlag = false;
}
const CVector &NLPACS::CLocalRetriever::getStartVector(uint32 chain) const
{
const COrderedChain3f &ochain = _FullOrderedChains[_Chains[chain].getSubChains().front()];
return (ochain.isForward()) ? ochain.getVertices().front() : ochain.getVertices().back();
}
const CVector &NLPACS::CLocalRetriever::getStopVector(uint32 chain) const
{
const COrderedChain3f &ochain = _FullOrderedChains[_Chains[chain].getSubChains().back()];
return (ochain.isForward()) ? ochain.getVertices().back() : ochain.getVertices().front();
}
const CVector &NLPACS::CLocalRetriever::getStartVector(uint32 chain, sint32 surface) const
{
bool onLeft = _Chains[chain].getLeft() == surface;
const COrderedChain3f &ochain = onLeft ? _FullOrderedChains[_Chains[chain].getSubChains().front()] :
_FullOrderedChains[_Chains[chain].getSubChains().back()];
if (ochain.isForward() == onLeft)
return ochain.getVertices().front();
else
return ochain.getVertices().back();
}
const CVector &NLPACS::CLocalRetriever::getStopVector(uint32 chain, sint32 surface) const
{
bool onLeft = _Chains[chain].getLeft() == surface;
const COrderedChain3f &ochain = onLeft ? _FullOrderedChains[_Chains[chain].getSubChains().back()] :
_FullOrderedChains[_Chains[chain].getSubChains().front()];
if (ochain.isForward() == onLeft)
return ochain.getVertices().back();
else
return ochain.getVertices().front();
}
/*
uint16 NLPACS::CLocalRetriever::getStartTip(uint32 chain, sint32 surface) const
{
return (_Chains[chain].getLeft() == surface) ? _Chains[chain].getStartTip() : _Chains[chain].getStopTip();
}
uint16 NLPACS::CLocalRetriever::getStopTip(uint32 chain, sint32 surface) const
{
return (_Chains[chain].getLeft() == surface) ? _Chains[chain].getStopTip() : _Chains[chain].getStartTip();
}
void NLPACS::CLocalRetriever::setStartTip(uint32 chain, sint32 surface, uint16 startTip)
{
if (_Chains[chain].getLeft() == surface)
_Chains[chain]._StartTip = startTip;
else
_Chains[chain]._StopTip = startTip;
}
void NLPACS::CLocalRetriever::setStopTip(uint32 chain, sint32 surface, uint16 stopTip)
{
if (_Chains[chain].getLeft() == surface)
_Chains[chain]._StopTip = stopTip;
else
_Chains[chain]._StartTip = stopTip;
}
*/
uint32 NLPACS::CLocalRetriever::getPreviousChain(uint32 chain, sint32 surface) const
{
uint loop;
uint loopIndex;
if (_Chains[chain].getLeft() == surface)
{
loop = _Chains[chain]._LeftLoop;
loopIndex = _Chains[chain]._LeftLoopIndex;
}
else
{
loop = _Chains[chain]._RightLoop;
loopIndex = _Chains[chain]._RightLoopIndex;
}
const CRetrievableSurface &surf = _Surfaces[surface];
const CRetrievableSurface::TLoop &sLoop = surf._Loops[loop];
return surf._Chains[sLoop[(loopIndex+sLoop.size()-1)%sLoop.size()]].Chain;
}
uint32 NLPACS::CLocalRetriever::getNextChain(uint32 chain, sint32 surface) const
{
uint loop;
uint loopIndex;
if (_Chains[chain].getLeft() == surface)
{
loop = _Chains[chain]._LeftLoop;
loopIndex = _Chains[chain]._LeftLoopIndex;
}
else
{
loop = _Chains[chain]._RightLoop;
loopIndex = _Chains[chain]._RightLoopIndex;
}
const CRetrievableSurface &surf = _Surfaces[surface];
const CRetrievableSurface::TLoop &sLoop = surf._Loops[loop];
return surf._Chains[sLoop[(loopIndex+1)%sLoop.size()]].Chain;
}
void NLPACS::CLocalRetriever::unify()
{
/*
uint i, j;
for (i=0; i<_Chains.size(); ++i)
_Chains[i].unify(_OrderedChains);
for (i=0; i<_Tips.size(); ++i)
{
NLPACS::CLocalRetriever::CTip &tip = _Tips[i];
CVector2s ptip = tip.Point;
for (j=0; j<tip.Chains.size(); ++j)
{
if (tip.Chains[j].Start)
{
if (_Chains[tip.Chains[j].Chain].getStartVector(_OrderedChains) != ptip)
nlwarning("chain %d is not stuck to tip %d", tip.Chains[j].Chain, i);
_Chains[tip.Chains[j].Chain].setStartVector(ptip, _OrderedChains);
}
else
{
if (_Chains[tip.Chains[j].Chain].getStopVector(_OrderedChains) != ptip)
nlwarning("chain %d is not stuck to tip %d", tip.Chains[j].Chain, i);
_Chains[tip.Chains[j].Chain].setStopVector(ptip, _OrderedChains);
}
}
}
_FullOrderedChains.resize(_OrderedChains.size());
for (i=0; i<_OrderedChains.size(); ++i)
_FullOrderedChains[i].unpack(_OrderedChains[i]);
*/
}
void NLPACS::CLocalRetriever::dumpSurface(uint surf, const CVector &vect) const
{
const CRetrievableSurface &surface = _Surfaces[surf];
nlinfo("dump surf %d", surf);
nlinfo("%d chains, %d loops", surface._Chains.size(), surface._Loops.size());
uint i, j, k;
for (i=0; i<surface._Chains.size(); ++i)
{
uint chainId = surface._Chains[i].Chain;
const CChain &chain = _Chains[chainId];
nlinfo("-- chain %d[%d]: %d sub left=%d right=%d start=%d stop=%d", i, chainId, chain.getSubChains().size(), chain.getLeft(), chain.getRight(), chain.getStartTip(), chain.getStopTip());
for (j=0; j<chain.getSubChains().size(); ++j)
{
const COrderedChain3f &ochain = _FullOrderedChains[chain.getSubChain(j)];
const COrderedChain &ochains = _OrderedChains[chain.getSubChain(j)];
nlinfo(" subchain %d[%d]: fwd=%d parent=%d idx=%d", j, chain.getSubChain(j), ochain.isForward(), ochain.getParentId(), ochain.getIndexInParent());
for (k=0; k<ochain.getVertices().size(); ++k)
nlinfo(" v[%d]=(%.3f,%.3f,%.3f) (%d,%d)", k, ochain.getVertices()[k].x+vect.x, ochain.getVertices()[k].y+vect.y, ochain.getVertices()[k].z+vect.z, ochains.getVertices()[k].x, ochains.getVertices()[k].y);
}
}
for (i=0; i<surface._Loops.size(); ++i)
{
const CRetrievableSurface::TLoop &loop = surface._Loops[i];
nlinfo("-- loop %d: %d chains length=%.2f", i, loop.size(), loop.Length);
static char wbuffer[256];
static char buffer[10240];
sprintf(buffer, " chains:");
for (j=0; j<loop.size(); ++j)
{
sprintf(wbuffer, " %d[%d]", loop[j], surface._Chains[loop[j]].Chain);
strcat(buffer, wbuffer);
}
nlinfo("%s", buffer);
}
}
float NLPACS::CLocalRetriever::distanceToBorder(const ULocalPosition &pos) const
{
if (!isLoaded())
return 0.0f;
const CRetrievableSurface &surf = _Surfaces[pos.Surface];
uint i, j;
float minDist = 1.0e10f, dist;
for (i=0; i<surf._Chains.size(); ++i)
{
const CChain &chain = _Chains[surf._Chains[i].Chain];
for (j=0; j<chain.getSubChains().size(); ++j)
{
dist = _OrderedChains[chain.getSubChain(j)].distance(pos.Estimation);
if (dist < minDist)
{
minDist = dist;
}
}
}
return minDist;
}
sint32 NLPACS::CLocalRetriever::addSurface(uint8 normalq, uint8 orientationq,
uint8 mat, uint8 charact, uint8 level,
bool isUnderWater, float waterHeight,
bool clusterHint,
const CVector &center,
const NLPACS::CSurfaceQuadTree &quad,
sint8 quantHeight)
{
// creates a new surface...
sint32 newId = (sint32)_Surfaces.size();
_Surfaces.resize(newId+1);
CRetrievableSurface &surf = _Surfaces.back();
// ... and fills it
surf._NormalQuanta = normalq;
surf._OrientationQuanta = orientationq;
surf._Material = mat;
surf._Character = charact;
surf._Level = level;
surf._Quad = quad;
surf._Center = center;
surf._QuantHeight = quantHeight;
// WARNING!! MODIFY THESE IF QUANTAS VALUES CHANGE !!
surf._IsFloor = (surf._NormalQuanta <= 1);
surf._IsCeiling = (surf._NormalQuanta >= 3);
surf._Flags = 0;
surf._Flags |= (surf._IsFloor) ? (1<<CRetrievableSurface::IsFloorBit) : 0;
surf._Flags |= (surf._IsCeiling) ? (1<<CRetrievableSurface::IsCeilingBit) : 0;
surf._Flags |= (!surf._IsFloor && !surf._IsCeiling) ? (1<<CRetrievableSurface::IsSlantBit) : 0;
surf._Flags |= clusterHint ? (1<<CRetrievableSurface::ClusterHintBit) : 0;
surf._Flags |= (isUnderWater) ? (1<<CRetrievableSurface::IsUnderWaterBit) : 0;
surf._WaterHeight = waterHeight;
surf._Flags |= ((0xffffffff<<(CRetrievableSurface::NormalQuantasStartBit)) & CRetrievableSurface::NormalQuantasBitMask);
return newId;
}
sint32 NLPACS::CLocalRetriever::addChain(const vector<CVector> &verts,
sint32 left, sint32 right)
{
vector<CVector> vertices = verts;
uint i;
if (vertices.size() < 2)
{
nlwarning("in NLPACS::CLocalRetriever::addChain()");
nlwarning("The chain has less than 2 vertices");
return -1;
}
// Remove doubled vertices due to CVector2s snapping
vector<CVector2s> converts;
for (i=0; i<vertices.size(); ++i)
converts.push_back(CVector2s(vertices[i]));
vector<CVector2s>::iterator next2s = converts.begin(), it2s, prev2s;
prev2s = next2s; ++next2s;
it2s = next2s; ++next2s;
vector<CVector>::iterator it3f = vertices.begin();
CVector prev3f = *it3f;
++it3f;
for (; it2s != converts.end() && next2s != converts.end(); )
{
// if the next point is equal to the previous
if (*it2s == *prev2s || *it2s == *next2s)
{
// then remove the next point
it2s = converts.erase(it2s);
it3f = vertices.erase(it3f);
prev2s = it2s;
--prev2s;
next2s = it2s;
++next2s;
}
else
{
// else remember the next point, and step to the next...
++prev2s;
++it2s;
++next2s;
++it3f;
prev3f = *it3f;
}
}
if (vertices.size() < 2)
{
nlwarning("in NLPACS::CLocalRetriever::addChain()");
nlwarning("The chain was snapped to a single point");
return -1;
}
sint32 newId = (sint32)_Chains.size();
_Chains.resize(newId+1);
CChain &chain = _Chains.back();
if (left>(sint)_Surfaces.size())
nlerror ("left surface id MUST be id<%d (id=%d)", _Surfaces.size(), left);
if (right>(sint)_Surfaces.size())
nlerror ("right surface id MUST be id<%d (id=%d)", _Surfaces.size(), right);
// checks if we can build the chain.
if (newId > 65535)
nlerror("in NLPACS::CLocalRetriever::addChain(): reached the maximum number of chains");
CRetrievableSurface *leftSurface = (left>=0) ? &(_Surfaces[left]) : NULL;
CRetrievableSurface *rightSurface = (right>=0) ? &(_Surfaces[right]) : NULL;
// adds the chain and the link to the surface links vector.
if (leftSurface != NULL)
leftSurface->_Chains.push_back(CRetrievableSurface::CSurfaceLink(newId, right));
if (rightSurface != NULL)
rightSurface->_Chains.push_back(CRetrievableSurface::CSurfaceLink(newId, left));
chain._StartTip = 0xffff;
chain._StopTip = 0xffff;
// make the chain and its subchains.
vector<uint> empty;
chain.make(vertices, left, right, _OrderedChains, (uint16)newId, _FullOrderedChains, empty);
return newId;
}
void NLPACS::CLocalRetriever::computeLoopsAndTips()
{
// for each surface,
// examine each chain tip to match another tip inside the surface tips
// if there is no matching tip, then creates a new one
uint i, j;
for (i=0; i<_Surfaces.size(); ++i)
{
CRetrievableSurface &surface = _Surfaces[i];
vector<bool> chainFlags;
chainFlags.resize(surface._Chains.size());
for (j=0; j<chainFlags.size(); ++j)
chainFlags[j] = false;
uint totalAdded = 0;
for(;;)
{
for (j=0; j<chainFlags.size() && chainFlags[j]; ++j)
;
if (j == chainFlags.size())
break;
uint32 loopId = (uint32)surface._Loops.size();
surface._Loops.push_back(CRetrievableSurface::TLoop());
CRetrievableSurface::TLoop &loop = surface._Loops.back();
CVector loopStart = getStartVector(surface._Chains[j].Chain, i);
CVector currentEnd = getStopVector(surface._Chains[j].Chain, i);
_Chains[surface._Chains[j].Chain].setLoopIndexes(i, loopId, (uint)loop.size());
loop.push_back(uint16(j));
chainFlags[j] = true;
float loopCloseDistance;
for(;;)
{
// loopCloseDistance = hybrid2dNorm(loopStart-currentEnd);
loopCloseDistance = (loopStart-currentEnd).norm();
// choose the best matching start vector
sint bestChain = -1;
float best = 1.0e10f;
CVector thisStart;
for (j=0; j<chainFlags.size(); ++j)
{
if (chainFlags[j])
continue;
thisStart = getStartVector(surface._Chains[j].Chain, i);
// float d = hybrid2dNorm(thisStart-currentEnd);
float d = (thisStart-currentEnd).norm();
if (d < best)
{
best = d;
bestChain = j;
}
}
if ((bestChain == -1 || best > 4.0e-2f)&& loopCloseDistance > 4.0e-2f)
{
nlwarning("in NLPACS::CLocalRetriever::computeTips()");
dumpSurface(i);
for (j=0; j<surface._Chains.size(); ++j)
{
CVector start = getStartVector(surface._Chains[j].Chain, i);
CVector end = getStopVector(surface._Chains[j].Chain, i);
nlinfo("surf=%d chain=%d", i, surface._Chains[j].Chain);
nlinfo("start=(%f,%f,%f)", start.x, start.y, start.z);
nlinfo("end=(%f,%f,%f)", end.x, end.y, end.z);
}
nlwarning("bestChain=%d best=%f", bestChain, best);
nlwarning("loopCloseDistance=%f", loopCloseDistance);
nlerror("Couldn't close loop on surface=%d", i);
}
else if ((best > 1.0e0f && loopCloseDistance < 3.0e-2f) ||
loopCloseDistance < 1.0e-3f)
{
break;
}
currentEnd = getStopVector(surface._Chains[bestChain].Chain, i);
_Chains[surface._Chains[bestChain].Chain].setLoopIndexes(i, loopId, (uint)loop.size());
loop.push_back(uint16(bestChain));
chainFlags[bestChain] = true;
++totalAdded;
}
}
}
/*
dumpSurface(9);
dumpSurface(10);
for (i=0; i<_Chains.size(); ++i)
{
if (i == 127)
nlinfo("");
uint whichTip;
// for both tips (start and stop)
for (whichTip=0; whichTip<=1; ++whichTip)
{
// get the tip id
uint thisTip = (whichTip) ? _Chains[i].getStopTip() : _Chains[i].getStartTip();
if (thisTip != 0xffff && thisTip >= _Tips.size())
{
nlwarning("in NLPACS::CLocalRetriever::computeLoopsAndTips()");
nlerror("checked a tip that doesn't exist on chain %d (tipId=%d)", i, thisTip);
}
// if it is unaffected yet creates an new tip and affect it to the common chains
if (thisTip == 0xffff)
{
uint turn;
uint tipId = _Tips.size();
if (tipId == 62)
nlinfo("");
_Tips.resize(tipId+1);
CTip &tip = _Tips[tipId];
tip.Point = (whichTip) ? getStopVector(i) : getStartVector(i);
for (turn=0; turn<=1; ++turn)
{
uint chain = i;
//
if (whichTip)
_Chains[chain]._StopTip = tipId;
else
_Chains[chain]._StartTip = tipId;
sint32 surf = (!turn && !whichTip || turn && whichTip) ? _Chains[chain].getLeft() : _Chains[chain].getRight();
while (surf >= 0)
{
CChain &nextChain = (turn) ? _Chains[chain = getNextChain(chain, surf)] : _Chains[chain = getPreviousChain(chain, surf)];
bool isForward = (nextChain.getLeft() == surf); // tells if the left surf is the current surf
bool selectTip = isForward && !turn || !isForward && turn;
uint16 &tipRef = selectTip ? nextChain._StopTip : nextChain._StartTip;
surf = (isForward) ? nextChain.getRight() : nextChain.getLeft();
if (tipRef != 0xffff && tipRef != tipId)
{
nlwarning("in NLPACS::CLocalRetriever::computeLoopsAndTips()");
nlerror("Trying to setup a already created tip (tipId=%d, previous=%d)", tipId, tipRef);
}
else if (tipRef != 0xffff)
{
break;
}
tipRef = tipId;
}
}
}
}
}
for (i=0; i<_Chains.size(); ++i)
{
uint startTip = _Chains[i].getStartTip(),
stopTip = _Chains[i].getStopTip();
// if (_Chains[i].getEdge() >= 0 && startTip == stopTip)
// {
// nlwarning("NLPACS::CLocalRetriever::computeLoopsAndTips(): chain %d on edge %d has same StartTip and StopTip", i, _Chains[i].getEdge(), startTip, stopTip);
// }
_Tips[startTip].Chains.push_back(CTip::CChainTip(i, true));
_Tips[stopTip].Chains.push_back(CTip::CChainTip(i, false));
}
*/
for (i=0; i<_Surfaces.size(); ++i)
{
for (j=0; j<_Surfaces[i]._Loops.size(); ++j)
{
_Surfaces[i]._Loops[j].Length = 0.0f;
uint k;
for (k=0; k<_Surfaces[i]._Loops[j].size(); ++k)
_Surfaces[i]._Loops[j].Length += _Chains[_Surfaces[i]._Chains[_Surfaces[i]._Loops[j][k]].Chain].getLength();
}
}
}
//
void NLPACS::CLocalRetriever::buildSurfacePolygons(uint32 surface, list<CPolygon> &polygons) const
{
const CRetrievableSurface &surf = _Surfaces[surface];
uint i, j, k, l;
for (i=0; i<surf._Loops.size(); ++i)
{
polygons.push_back(CPolygon());
CPolygon &poly = polygons.back();
for (j=0; j<surf._Loops[i].size(); ++j)
{
const CChain &chain = _Chains[surf._Loops[i][j]];
bool chainforward = ((uint32)chain._Left == surface);
if (chainforward)
{
for (k=0; k<chain._SubChains.size(); ++k)
{
const COrderedChain &ochain = _OrderedChains[chain._SubChains[k]];
bool ochainforward = ochain.isForward();
if (ochainforward)
{
for (l=0; l<ochain.getVertices().size()-1; ++l)
poly.Vertices.push_back(ochain[l].unpack3f());
}
else
{
for (l=(uint)ochain.getVertices().size()-1; l>0; --l)
poly.Vertices.push_back(ochain[l].unpack3f());
}
}
}
else
{
for (k=(uint)chain._SubChains.size(); (sint)k>0; --k)
{
const COrderedChain &ochain = _OrderedChains[chain._SubChains[k]];
bool ochainforward = ochain.isForward();
if (ochainforward)
{
for (l=(uint)ochain.getVertices().size()-1; (sint)l>0; --l)
poly.Vertices.push_back(ochain[l].unpack3f());
}
else
{
for (l=0; l<ochain.getVertices().size()-1; ++l)
poly.Vertices.push_back(ochain[l].unpack3f());
}
}
}
}
}
}
//
void NLPACS::CLocalRetriever::build3dSurfacePolygons(uint32 surface, list<CPolygon> &polygons) const
{
const CRetrievableSurface &surf = _Surfaces[surface];
uint i, j, k, l;
for (i=0; i<surf._Loops.size(); ++i)
{
polygons.push_back(CPolygon());
CPolygon &poly = polygons.back();
for (j=0; j<surf._Loops[i].size(); ++j)
{
const CRetrievableSurface::TLoop &loop = surf._Loops[i];
const CChain &chain = _Chains[surf._Chains[loop[j]].Chain];
bool chainforward = ((uint32)chain._Left == surface);
if (chainforward)
{
for (k=0; k<chain._SubChains.size(); ++k)
{
const COrderedChain3f &ochain = _FullOrderedChains[chain._SubChains[k]];
bool ochainforward = ochain.isForward();
if (ochainforward)
{
for (l=0; l<ochain.getVertices().size()-1; ++l)
poly.Vertices.push_back(ochain[l]);
}
else
{
for (l=(uint)ochain.getVertices().size()-1; l>0; --l)
poly.Vertices.push_back(ochain[l]);
}
}
}
else
{
for (k=(uint)chain._SubChains.size()-1; (sint)k>=0; --k)
{
const COrderedChain3f &ochain = _FullOrderedChains[chain._SubChains[k]];
bool ochainforward = ochain.isForward();
if (ochainforward)
{
for (l=(uint)ochain.getVertices().size()-1; (sint)l>0; --l)
poly.Vertices.push_back(ochain[l]);
}
else
{
for (l=0; l<ochain.getVertices().size()-1; ++l)
poly.Vertices.push_back(ochain[l]);
}
}
}
}
}
}
// not implemented...
void NLPACS::CLocalRetriever::sortTips()
{
}
void NLPACS::CLocalRetriever::findBorderChains()
{
uint chain;
// for each chain, if it belongs to an edge of the
// local retriever, then adds it to the _BorderChains.
for (chain=0; chain<_Chains.size(); ++chain)
if (_Chains[chain].isBorderChain())
{
sint32 index = (sint32)_BorderChains.size();
_BorderChains.push_back(uint16(chain));
_Chains[chain].setBorderChainIndex(index);
}
}
void NLPACS::CLocalRetriever::updateChainIds()
{
uint surf, link;
for (surf=0; surf<_Surfaces.size(); ++surf)
{
CRetrievableSurface &surface = _Surfaces[surf];
for (link=0; link<surface._Chains.size(); ++link)
{
sint32 chain = surface._Chains[link].Chain;
if (_Chains[chain]._Left == (sint32)surf)
surface._Chains[link].Surface = _Chains[chain]._Right;
else if (_Chains[chain]._Right == (sint32)surf)
surface._Chains[link].Surface = _Chains[chain]._Left;
else
{
nlwarning("in NLPACS::CLocalRetriever::updateEdgesOnSurfaces()");
nlerror("Can't find back point to surface %d on chain %d", surf, chain);
}
}
}
}
void NLPACS::CLocalRetriever::computeTopologies()
{
//nlinfo("compute topologies");
// Find topologies out...
uint character;
for (character=0; character<NumCreatureModels; ++character)
{
// for each type of creature, flood fill surfaces...
sint32 surface;
uint topology = 0;
for (surface=0; surface<(sint)_Surfaces.size(); ++surface)
{
if (_Surfaces[surface]._Topologies[character] == -1 &&
_Surfaces[surface]._Character == character)
{
vector<sint32> surfacesStack;
surfacesStack.push_back(surface);
while (!surfacesStack.empty())
{
CRetrievableSurface &current = _Surfaces[surfacesStack.back()];
surfacesStack.pop_back();
current._Topologies[character] = topology;
uint i;
for (i=0; i<current._Chains.size(); ++i)
{
CChain &chain = _Chains[current._Chains[i].Chain];
sint32 link = (chain.getLeft() == surface) ? chain.getRight() : chain.getLeft();
if (link>=0 && link<(sint)_Surfaces.size() &&
_Surfaces[link]._Topologies[character] == -1 &&
_Surfaces[link]._Character >= character)
{
surfacesStack.push_back(link);
_Surfaces[link]._Topologies[character] = topology;
}
}
}
++topology;
}
}
_Topologies[character].resize(topology);
//nlinfo("generated %d topologies for character %d", topology, character);
}
uint surface;
for (surface=0; surface<_Surfaces.size(); ++surface)
{
CRetrievableSurface &current = _Surfaces[surface];
for (character=0; character<NumCreatureModels; ++character)
if (current._Topologies[character] >= 0)
_Topologies[character][current._Topologies[character]].push_back(surface);
}
}
void NLPACS::CLocalRetriever::translate(const NLMISC::CVector &translation)
{
uint i;
for (i=0; i<_OrderedChains.size(); ++i)
_OrderedChains[i].translate(translation);
for (i=0; i<_Surfaces.size(); ++i)
_Surfaces[i].translate(translation);
/*
for (i=0; i<_Tips.size(); ++i)
_Tips[i].translate(translation);
*/
}
void NLPACS::CLocalRetriever::serial(NLMISC::IStream &f)
{
/*
Version 0:
- base version (with collision info).
Version 1:
- interior vertices and faces, for interior ground snapping
Version 2:
- face grid added.
Version 3:
- identifier added.
Version 4:
- topologies no more in stream (obsolete)
*/
sint ver= f.serialVersion(4);
if (ver < 4)
throw EOlderStream();
uint i;
f.serialCont(_Chains);
f.serialCont(_OrderedChains);
f.serialCont(_FullOrderedChains);
f.serialCont(_Surfaces);
f.serialCont(__Tips);
f.serialCont(_BorderChains);
if (ver < 4)
{
for (i=0; i<NumMaxCreatureModels; ++i)
f.serialCont(_Topologies[i]);
}
f.serial(_ChainQuad);
f.serial(_BBox);
f.serialEnum(_Type);
f.serial(_ExteriorMesh);
// a fix for old versions (with wrong _Type value)
if (_Type != CLocalRetriever::Interior) _Type = CLocalRetriever::Landscape;
if (ver >= 1)
{
f.serialCont(_InteriorVertices);
f.serialCont(_InteriorFaces);
}
if (ver >= 2)
{
f.serial(_FaceGrid);
}
if (ver >= 3)
{
f.serial(_Id);
}
_Loaded = true;
LoadCheckFlag = false;
}
bool NLPACS::CLocalRetriever::insurePosition(NLPACS::ULocalPosition &local) const
{
if (!_Loaded)
return false;
if (local.Surface < 0 || local.Surface >= (sint)_Surfaces.size())
{
nlwarning("PACS: can't insure position to inexistant surface %d", local.Surface);
return false;
}
// the surface
const NLPACS::CRetrievableSurface &surface = _Surfaces[local.Surface];
uint i, j, k;
CVector2f M = CVector2f(local.Estimation);
bool moved = false;
// for each chain and each subchain of the surface,
// check if point is located on the good side of the border (and far enough to avoid accuracy issues)
for (i=0; i<surface.getChains().size(); ++i)
{
uint ichain = surface.getChain(i).Chain;
const NLPACS::CChain &chain = _Chains[ichain];
for (j=0; j<chain.getSubChains().size(); ++j)
{
uint iochain = chain.getSubChain(j);
const NLPACS::COrderedChain &ochain = _OrderedChains[iochain];
uint isAtLeft = ((chain.getLeft() == local.Surface) ? 1 : 0);
uint isForward = (ochain.isForward() ? 1 : 0);
bool shouldBeUpper = !((isAtLeft ^ isForward) != 0); // shouldBeAtLeft for vertical segment
for (k=0; (sint)k<(sint)(ochain.getVertices().size()-1); ++k)
{
CVector2f A = ochain[k].unpack();
CVector2f B = ochain[k+1].unpack();
CVector2f AB = B-A;
float lambda = ((M-A)*AB)/AB.sqrnorm();
if (lambda<0.0f || lambda>1.0f)
continue;
CVector2f n = (shouldBeUpper ? CVector2f(-AB.y, AB.x) : CVector2f(AB.y, -AB.x)).normed();
float d = (M-A)*n;
// if point is too close of the border or on the wrong side
// move it far enough
if (d < InsurePositionThreshold && d > -InsurePositionThreshold)
{
M += (InsurePositionThreshold*1.1f-d)*n;
moved = true;
}
}
}
}
NLPACS::CRetrieverInstance::snapVector(M);
local.Estimation.x = M.x;
local.Estimation.y = M.y;
{
float fx1024 = local.Estimation.x * 1024.0f;
float fy1024 = local.Estimation.x * 1024.0f;
sint32 ix1024 = (sint32)floor(fx1024);
sint32 iy1024 = (sint32)floor(fy1024);
nlassert ((float)ix1024 == fx1024);
nlassert ((float)iy1024 == fy1024);
}
return moved;
}
bool NLPACS::CLocalRetriever::testPosition(NLPACS::ULocalPosition &local, CCollisionSurfaceTemp &cst) const
{
if (!_Loaded)
return false;
if (local.Surface < 0 || local.Surface >= (sint)_Surfaces.size())
{
nlwarning("PACS: can't test inexistant surface %d", local.Surface);
return false;
}
if (fabs(local.Estimation.x) >= 256.0 || fabs(local.Estimation.y) >= 256.0)
return false;
retrievePosition(local.Estimation, cst);
bool result = (cst.SurfaceLUT[local.Surface].Counter == 2 || cst.SurfaceLUT[local.Surface].OnVerticalEdge);
uint i;
for (i=0; i<cst.PossibleSurfaces.size(); ++i)
cst.SurfaceLUT[cst.PossibleSurfaces[i]].reset();
return result;
}
void NLPACS::CLocalRetriever::retrievePosition(CVector estimated, CCollisionSurfaceTemp &cst) const
{
if (!_Loaded)
return;
CAABBox box;
const double BorderThreshold = 2.0e-2f;
box.setMinMax(CVector(estimated.x-(float)BorderThreshold, _BBox.getMin().y, 0.0f), CVector(estimated.x+(float)BorderThreshold, _BBox.getMax().y, 0.0f));
uint numEdges = _ChainQuad.selectEdges(box, cst);
uint ochain, i;
CVector2s estim = CVector2s(estimated);
cst.PossibleSurfaces.clear();
// WARNING!!
// cst.SurfaceLUT is assumed to be 0 filled !!
//nldebug("estim=(%d,%d)", estim.x, estim.y);
// for each ordered chain, checks if the estimated position is between the min and max.
for (i=0; i<numEdges; ++i)
{
ochain = cst.EdgeChainEntries[i].OChainId;
const COrderedChain &sub = _OrderedChains[ochain];
const CVector2s &min = sub.getMin(),
&max = sub.getMax();
// checks the position against the min and max of the chain
if (estim.x < min.x || estim.x > max.x)
continue;
bool isUpper;
bool isOnBorder = false;
sint32 left = _Chains[sub.getParentId()].getLeft(),
right = _Chains[sub.getParentId()].getRight();
if (estim.y < min.y)
{
if (estim.x == max.x)
continue;
isUpper = false;
// nlinfo("Box: min(%d,%d) max(%d,%d) forward=%d left=%d right=%d upper=false", min.x, min.y, max.x, max.y, sub.isForward(), left, right);
}
else if (estim.y > max.y)
{
if (estim.x == max.x)
continue;
isUpper = true;
// nlinfo("Box: min(%d,%d) max(%d,%d) forward=%d left=%d right=%d upper=true", min.x, min.y, max.x, max.y, sub.isForward(), left, right);
}
else
{
const vector<CVector2s> &vertices = sub.getVertices();
uint start = 0, stop = (uint)vertices.size()-1;
// then finds the smallest segment of the chain that includes the estimated position.
while (stop-start > 1)
{
uint mid = (start+stop)/2;
if (vertices[mid].x > estim.x)
stop = mid;
else
start = mid;
}
// if a vertical edge
if (vertices[start].x == vertices[stop].x)
{
// look for maximal bounds
while (start > 0 && vertices[start].x == vertices[start-1].x)
--start;
while (stop < vertices.size()-1 && vertices[stop].x == vertices[stop+1].x)
++stop;
// if upper or lower the bounds, do nothing
if ((estim.y > vertices[start].y && estim.y > vertices[stop].y) ||
(estim.y < vertices[start].y && estim.y < vertices[stop].y))
continue;
isOnBorder = true;
if (left >= 0)
{
cst.SurfaceLUT[left].FoundCloseEdge = true;
cst.SurfaceLUT[left].OnVerticalEdge = true;
}
if (right >= 0)
{
cst.SurfaceLUT[right].FoundCloseEdge = true;
cst.SurfaceLUT[right].OnVerticalEdge = true;
}
// nlinfo("Edge: start(%d,%d) stop(%d,%d) forward=%d left=%d right=%d border=true", vertices[start].x, vertices[start].y, vertices[stop].x, vertices[stop].y, sub.isForward(), left, right);
continue;
}
else if (vertices[stop].x == estim.x)
{
// if (yes)
continue;
}
// and then checks if the estimated position is up or down the chain.
// first trivial case (up both tips)
if (estim.y > vertices[start].y && estim.y > vertices[stop].y)
{
isUpper = true;
// nlinfo("Edge: start(%d,%d) stop(%d,%d) forward=%d left=%d right=%d upper=true", vertices[start].x, vertices[start].y, vertices[stop].x, vertices[stop].y, sub.isForward(), left, right);
}
// second trivial case (down both tips)
else if (estim.y < vertices[start].y && estim.y < vertices[stop].y)
{
isUpper = false;
// nlinfo("Edge: start(%d,%d) stop(%d,%d) forward=%d left=%d right=%d upper=false", vertices[start].x, vertices[start].y, vertices[stop].x, vertices[stop].y, sub.isForward(), left, right);
}
// full test...
else
{
const CVector2s &vstart = vertices[start],
&vstop = vertices[stop];
sint16 intersect = vstart.y + (vstop.y-vstart.y)*(estim.x-vstart.x)/(vstop.x-vstart.x);
isUpper = estim.y > intersect;
//isOnBorder = (fabs(estim.y - intersect)<BorderThreshold*Vector2sAccuracy);
isOnBorder = (fabs((double)(estim.y - intersect))<(double)(BorderThreshold*Vector2sAccuracy));
// nlinfo("Edge: start(%d,%d) stop(%d,%d) forward=%d left=%d right=%d upper=%s border=%s", vertices[start].x, vertices[start].y, vertices[stop].x, vertices[stop].y, sub.isForward(), left, right, isUpper ? "true":"false", isOnBorder ? "true":"false");
}
}
if (isOnBorder)
{
cst.incSurface(left);
cst.incSurface(right);
if (left >= 0) cst.SurfaceLUT[left].FoundCloseEdge = true;
if (right >= 0) cst.SurfaceLUT[right].FoundCloseEdge = true;
continue;
}
// Depending on the chain is forward, up the position, increase/decrease the surface table...
if (sub.isForward())
{
if (isUpper)
{
cst.incSurface(left);
cst.decSurface(right);
}
else
{
cst.decSurface(left);
cst.incSurface(right);
}
}
else
{
if (isUpper)
{
cst.decSurface(left);
cst.incSurface(right);
}
else
{
cst.incSurface(left);
cst.decSurface(right);
}
}
}
}
void NLPACS::CLocalRetriever::retrieveAccuratePosition(CVector2s estim, CCollisionSurfaceTemp &cst, bool &onBorder) const
{
if (!_Loaded)
return;
CAABBox box;
CVector estimated = estim.unpack3f();
const double BorderThreshold = 2.0e-2f;
box.setMinMax(CVector(estimated.x-(float)BorderThreshold, _BBox.getMin().y, 0.0f),
CVector(estimated.x+(float)BorderThreshold, _BBox.getMax().y, 0.0f));
uint numEdges = _ChainQuad.selectEdges(box, cst);
uint ochain, i;
onBorder = false;
cst.PossibleSurfaces.clear();
// WARNING!!
// cst.SurfaceLUT is assumed to be 0 filled !!
//nldebug("estim=(%d,%d)", estim.x, estim.y);
// for each ordered chain, checks if the estimated position is between the min and max.
for (i=0; i<numEdges; ++i)
{
ochain = cst.EdgeChainEntries[i].OChainId;
const COrderedChain &sub = _OrderedChains[ochain];
const CVector2s &min = sub.getMin(),
&max = sub.getMax();
// checks the position against the min and max of the chain
if (estim.x < min.x || estim.x > max.x)
continue;
bool isUpper;
//bool isOnBorder = false;
sint32 left = _Chains[sub.getParentId()].getLeft(),
right = _Chains[sub.getParentId()].getRight();
if (estim.y < min.y)
{
if (estim.x == max.x)
continue;
isUpper = false;
}
else if (estim.y > max.y)
{
if (estim.x == max.x)
continue;
isUpper = true;
}
else
{
const vector<CVector2s> &vertices = sub.getVertices();
uint start = 0, stop = (uint)vertices.size()-1;
// then finds the smallest segment of the chain that includes the estimated position.
while (stop-start > 1)
{
uint mid = (start+stop)/2;
if (vertices[mid].x > estim.x)
stop = mid;
else
start = mid;
}
// if a vertical edge
if (vertices[start].x == vertices[stop].x)
{
// look for maximal bounds
while (start > 0 && vertices[start].x == vertices[start-1].x)
--start;
while (stop < vertices.size()-1 && vertices[stop].x == vertices[stop+1].x)
++stop;
// if upper or lower the bounds, do nothing
if ((estim.y > vertices[start].y && estim.y > vertices[stop].y) ||
(estim.y < vertices[start].y && estim.y < vertices[stop].y))
continue;
onBorder = true;
continue;
}
else if (vertices[stop].x == estim.x)
{
// if (yes)
continue;
}
// and then checks if the estimated position is up or down the chain.
// first trivial case (up both tips)
if (estim.y > vertices[start].y && estim.y > vertices[stop].y)
{
isUpper = true;
}
// second trivial case (down both tips)
else if (estim.y < vertices[start].y && estim.y < vertices[stop].y)
{
isUpper = false;
}
// full test...
else
{
const CVector2s &vstart = vertices[start],
&vstop = vertices[stop];
// this test is somewhat more accurate
// no division performed
sint32 det = (estim.y-vstart.y)*(vstop.x-vstart.x) - (estim.x-vstart.x)*(vstop.y-vstart.y);
isUpper = (det > 0);
if (det == 0)
onBorder = true;
}
}
// Depending on the chain is forward, up the position, increase/decrease the surface table...
if (sub.isForward())
{
if (isUpper)
{
cst.incSurface(left);
cst.decSurface(right);
}
else
{
cst.decSurface(left);
cst.incSurface(right);
}
}
else
{
if (isUpper)
{
cst.decSurface(left);
cst.incSurface(right);
}
else
{
cst.incSurface(left);
cst.decSurface(right);
}
}
}
}
void NLPACS::CLocalRetriever::initFaceGrid()
{
CFaceGrid::CFaceGridBuild fgb;
fgb.init(64, 4.0f);
uint i;
for (i=0; i<_InteriorFaces.size(); ++i)
{
CAABBox box;
CInteriorFace &f = _InteriorFaces[i];
box.setCenter(_InteriorVertices[f.Verts[0]]);
box.extend(_InteriorVertices[f.Verts[1]]);
box.extend(_InteriorVertices[f.Verts[2]]);
fgb.insert(box.getMin(), box.getMax(), i);
}
_FaceGrid.create(fgb);
}
void NLPACS::CLocalRetriever::snapToInteriorGround(NLPACS::ULocalPosition &position, bool &snapped) const
{
if (!_Loaded)
return;
// first preselect faces around the (x, y) position (CQuadGrid ?)
vector<uint32> selection;
_FaceGrid.select(position.Estimation, selection);
// from the preselect faces, look for the only face that belongs to the surface
// and that contains the position
CVector pos = position.Estimation;
CVector posh = pos+CVector(0.0f, 0.0f, 1.0f);
CVector2f pos2d = position.Estimation;
float bestDist = 1.0e10f;
CVector best(0.0f, 0.0f, 0.0f);
vector<uint32>::iterator it;
snapped = false;
for (it=selection.begin(); it!=selection.end(); ++it)
{
const CInteriorFace &f = _InteriorFaces[*it];
if (f.Surface == (uint32)position.Surface)
{
CVector v[3];
v[0] = _InteriorVertices[f.Verts[0]];
v[1] = _InteriorVertices[f.Verts[1]];
v[2] = _InteriorVertices[f.Verts[2]];
CVector2f n;
float c; // 2D cartesian coefficients of line in plane X/Y.
// Line p0-p1.
n = CVector2f(-(v[1].y-v[0].y), (v[1].x-v[0].x)).normed();
c = -(v[0].x*n.x + v[0].y*n.y);
if (n*pos2d + c < -1.0f/Vector2sAccuracy) continue;
// Line p1-p2.
n = CVector2f(-(v[2].y-v[1].y), (v[2].x-v[1].x)).normed();
c = -(v[1].x*n.x + v[1].y*n.y);
if (n*pos2d + c < -1.0f/Vector2sAccuracy) continue;
// Line p2-p0.
n = CVector2f(-(v[0].y-v[2].y), (v[0].x-v[2].x)).normed();
c = -(v[2].x*n.x + v[2].y*n.y);
if (n*pos2d + c < -1.0f/Vector2sAccuracy) continue;
CPlane p;
p.make(v[0], v[1], v[2]);
CVector i = p.intersect(pos, posh);
float d = (float)fabs(pos.z-i.z);
if (d < bestDist)
{
bestDist = d;
best = i;
}
}
}
// and computes the real position on this face
if (bestDist < 400.0f)
{
snapped = true;
position.Estimation = best;
}
}
// ***************************************************************************
float NLPACS::CLocalRetriever::getHeight(const NLPACS::ULocalPosition &position) const
{
if (!_Loaded)
return 0.0f;
if (_Type == Interior)
{
// first preselect faces around the (x, y) position (CQuadGrid ?)
vector<uint32> selection;
_FaceGrid.select(position.Estimation, selection);
// from the preselect faces, look for the only face that belongs to the surface
// and that contains the position
CVector pos = position.Estimation;
CVector posh = pos+CVector(0.0f, 0.0f, 1.0f);
float bestDist = 1.0e10f;
CVector best(0.0f, 0.0f, 0.0f);
vector<uint32>::iterator it;
for (it=selection.begin(); it!=selection.end(); ++it)
{
const CInteriorFace &f = _InteriorFaces[*it];
if (f.Surface == (uint32)position.Surface)
{
CVector v[3];
v[0] = _InteriorVertices[f.Verts[0]];
v[1] = _InteriorVertices[f.Verts[1]];
v[2] = _InteriorVertices[f.Verts[2]];
float a,b,c; // 2D cartesian coefficients of line in plane X/Y.
// Line p0-p1.
a = -(v[1].y-v[0].y);
b = (v[1].x-v[0].x);
c = -(v[0].x*a + v[0].y*b);
if (a*pos.x + b*pos.y + c < 0) continue;
// Line p1-p2.
a = -(v[2].y-v[1].y);
b = (v[2].x-v[1].x);
c = -(v[1].x*a + v[1].y*b);
if (a*pos.x + b*pos.y + c < 0) continue;
// Line p2-p0.
a = -(v[0].y-v[2].y);
b = (v[0].x-v[2].x);
c = -(v[2].x*a + v[2].y*b);
if (a*pos.x + b*pos.y + c < 0) continue;
CPlane p;
p.make(v[0], v[1], v[2]);
CVector i = p.intersect(pos, posh);
float d = (float)fabs(pos.z-i.z);
if (d < bestDist)
{
bestDist = d;
best = i;
}
}
}
// and computes the real position on this face
return (bestDist < 200.0f) ? best.z : position.Estimation.z;
}
else
{
if (_Surfaces[position.Surface].getQuadTree().getRoot() != NULL)
{
// find quad leaf.
const CQuadLeaf *leaf = _Surfaces[position.Surface].getQuadTree().getLeaf(position.Estimation);
// if there is no acceptable leaf, just give up
if (leaf == NULL)
{
//nlinfo("COL: quadtree: don't find the quadLeaf!");
return position.Estimation.z;
}
else
{
// else return mean height.
float meanHeight = (leaf->getMinHeight()+leaf->getMaxHeight())*0.5f;
return meanHeight;
}
}
else if (_Surfaces[position.Surface].isUnderWater())
{
return _Surfaces[position.Surface].getWaterHeight();
}
else
{
sint8 qh = _Surfaces[position.Surface].getQuantHeight();
return qh*2.0f + 1.0f;
}
}
}
// ***************************************************************************
float NLPACS::CLocalRetriever::getInteriorHeightAround(const ULocalPosition &position, float outsideTolerance) const
{
if (!_Loaded)
return 0.0f;
if (_Type == Interior)
{
// first preselect faces around the (x, y) position (CQuadGrid ?)
vector<uint32> selection;
_FaceGrid.select(position.Estimation, selection);
// from the preselect faces, look for the only face that belongs to the surface
// and that contains the position
CVector pos = position.Estimation;
CVector posh = pos+CVector(0.0f, 0.0f, 1.0f);
float bestDist = 1.0e10f;
CVector best(0.0f, 0.0f, 0.0f);
vector<uint32>::iterator it;
for (it=selection.begin(); it!=selection.end(); ++it)
{
const CInteriorFace &f = _InteriorFaces[*it];
if (f.Surface == (uint32)position.Surface)
{
CVector v[3];
v[0] = _InteriorVertices[f.Verts[0]];
v[1] = _InteriorVertices[f.Verts[1]];
v[2] = _InteriorVertices[f.Verts[2]];
// Test if out of this triangle (+ tolerance)
float a,b; // 2D cartesian coefficients of line in plane X/Y.
float len;
// Line p0-p1.
a = -(v[1].y-v[0].y);
b = (v[1].x-v[0].x);
len= sqrtf(a*a+b*b); // norm of the normal vector
if (a*(pos.x-v[0].x) + b*(pos.y-v[0].y) < -len*outsideTolerance) continue;
// Line p1-p2.
a = -(v[2].y-v[1].y);
b = (v[2].x-v[1].x);
len= sqrtf(a*a+b*b); // norm of the normal vector
if (a*(pos.x-v[1].x) + b*(pos.y-v[1].y) < -len*outsideTolerance) continue;
// Line p2-p0.
a = -(v[0].y-v[2].y);
b = (v[0].x-v[2].x);
len= sqrtf(a*a+b*b); // norm of the normal vector
if (a*(pos.x-v[2].x) + b*(pos.y-v[2].y) < -len*outsideTolerance) continue;
// Ok IN => compute z and keep nearest to wanted one
CPlane p;
p.make(v[0], v[1], v[2]);
CVector i = p.intersect(pos, posh);
float d = (float)fabs(pos.z-i.z);
if (d < bestDist)
{
bestDist = d;
best = i;
}
}
}
// and computes the real position on this face
return (bestDist < 200.0f) ? best.z : position.Estimation.z;
}
return 0.f;
}
// ***************************************************************************
#ifdef NL_OS_WINDOWS
//#pragma optimize( "", off )
#endif // NL_OS_WINDOWS
void NLPACS::CLocalRetriever::findPath(const NLPACS::CLocalRetriever::CLocalPosition &A,
const NLPACS::CLocalRetriever::CLocalPosition &B,
std::vector<NLPACS::CVector2s> &path,
NLPACS::CCollisionSurfaceTemp &cst) const
{
if (A.Surface != B.Surface)
{
nlwarning("in NLPACS::CLocalRetriever::findPath()");
nlerror("Try to find a path between 2 points that are not in the same surface (A=%d, B=%d)", A.Surface, B.Surface);
}
CVector a = A.Estimation,
b = B.Estimation,
n = CVector(a.y-b.y, b.x-a.x, 0.0f);
_ChainQuad.selectEdges(a, b, cst);
vector<CIntersectionMarker> intersections;
uint i, j;
sint32 surfaceId = A.Surface;
const CRetrievableSurface &surface = _Surfaces[surfaceId];
for (i=0; i<cst.EdgeChainEntries.size(); ++i)
{
CEdgeChainEntry &entry = cst.EdgeChainEntries[i];
const COrderedChain &chain = _OrderedChains[entry.OChainId];
if (_Chains[chain.getParentId()].getLeft() != surfaceId &&
_Chains[chain.getParentId()].getRight() != surfaceId)
continue;
for (j=entry.EdgeStart; j<entry.EdgeEnd; ++j)
{
// here the edge collision test
CVector p0 = chain[j].unpack3f(),
p1 = chain[j+1].unpack3f();
float vp0 = (p0-a)*n,
vp1 = (p1-a)*n;
if (vp0*vp1 <= 0.0f)
{
CVector np = CVector(p0.y-p1.y, p1.x-p0.x, 0.0f);
float va = (a-p0)*np,
vb = (b-p0)*np;
// here we have an intersection
if (va*vb <= 0.0f)
{
const CChain &parent = _Chains[chain.getParentId()];
bool isIn = (va-vb < 0.0f) ^ (parent.getLeft() == surfaceId) ^ chain.isForward();
intersections.push_back(CIntersectionMarker(va/(va-vb), entry.OChainId, uint16(j), isIn));
}
}
}
}
sort(intersections.begin(), intersections.end());
uint intersStart = 0;
uint intersEnd = (uint)intersections.size();
if (intersEnd > 0)
{
while (intersStart < intersections.size() &&
intersections[intersStart].In && intersections[intersStart].Position < 1.0e-4f)
++intersStart;
while (intersStart < intersEnd &&
!intersections[intersEnd-1].In && intersections[intersEnd-1].Position > 1.0f-1.0e-4f)
--intersEnd;
// Check intersections have a valid order
if ((intersEnd-intersStart) & 1)
{
nlwarning("in NLPACS::CLocalRetriever::findPath()");
nlerror("Found an odd (%d) number of intersections", intersections.size());
}
for (i=intersStart; i<intersEnd; )
{
uint exitLoop, enterLoop;
const CChain &exitChain = _Chains[_OrderedChains[intersections[i].OChain].getParentId()];
exitLoop = (exitChain.getLeft() == surfaceId) ? exitChain.getLeftLoop() : exitChain.getRightLoop();
if (intersections[i++].In)
{
nlwarning("in NLPACS::CLocalRetriever::findPath()");
nlerror("Entered the surface before exited", intersections.size());
}
const CChain &enterChain = _Chains[_OrderedChains[intersections[i].OChain].getParentId()];
enterLoop = (enterChain.getLeft() == surfaceId) ? enterChain.getLeftLoop() : enterChain.getRightLoop();
if (!intersections[i++].In)
{
nlwarning("in NLPACS::CLocalRetriever::findPath()");
nlerror("Exited twice the surface", intersections.size());
}
if (exitLoop != enterLoop)
{
nlwarning("in NLPACS::CLocalRetriever::findPath()");
nlerror("Exited and rentered by a different loop");
}
}
}
// dumpSurface(surfaceId);
path.push_back(CVector2s(A.Estimation));
for (i=intersStart; i<intersEnd; )
{
uint exitChainId = _OrderedChains[intersections[i].OChain].getParentId(),
enterChainId = _OrderedChains[intersections[i+1].OChain].getParentId();
const CChain &exitChain = _Chains[exitChainId],
&enterChain = _Chains[enterChainId];
uint loopId, exitLoopIndex, enterLoopIndex;
if (exitChain.getLeft() == surfaceId)
{
loopId = exitChain.getLeftLoop();
exitLoopIndex = exitChain.getLeftLoopIndex();
}
else
{
loopId = exitChain.getRightLoop();
exitLoopIndex = exitChain.getRightLoopIndex();
}
const CRetrievableSurface::TLoop &loop = surface._Loops[loopId];
if (enterChain.getLeft() == surfaceId)
enterLoopIndex = enterChain.getLeftLoopIndex();
else
enterLoopIndex = enterChain.getRightLoopIndex();
float forwardLength = (exitChain.getLength()+enterChain.getLength())*0.5f;
sint loopIndex = exitLoopIndex;
uint thisChainId = exitChainId;
bool thisChainForward = (enterChain.getLeft() == surfaceId);
uint thisOChainId = intersections[i].OChain;
sint thisOChainIndex = _OrderedChains[thisOChainId].getIndexInParent();
bool forward;
if (exitChainId != enterChainId)
{
for (j=(exitLoopIndex+1)%loop.size(); j!=enterLoopIndex; j=(j+1)%loop.size())
forwardLength += _Chains[surface._Chains[loop[j]].Chain].getLength();
forward = (forwardLength <= loop.Length-forwardLength);
}
else
{
forward = !thisChainForward ^ (_OrderedChains[intersections[i].OChain].getIndexInParent() < _OrderedChains[intersections[i+1].OChain].getIndexInParent());
}
path.push_back(CVector2s(A.Estimation+intersections[i].Position*(B.Estimation-A.Estimation)));
for(;;)
{
sint from = (thisOChainId == intersections[i].OChain) ? intersections[i].Edge : -1,
to = (thisOChainId == intersections[i+1].OChain) ? intersections[i+1].Edge : -1;
bool oforward = thisChainForward ^ forward ^ _OrderedChains[thisOChainId].isForward();
if (from != -1 && to != -1)
oforward = (intersections[i].Edge < intersections[i+1].Edge);
_OrderedChains[thisOChainId].traverse(from, to, oforward, path);
if (thisOChainId == intersections[i+1].OChain)
break;
thisOChainIndex = (thisChainForward ^ forward) ? thisOChainIndex-1 : thisOChainIndex+1;
if (thisOChainIndex < 0 || thisOChainIndex >= (sint)_Chains[thisChainId]._SubChains.size())
{
if (forward)
{
loopIndex++;
if (loopIndex == (sint)loop.size())
loopIndex = 0;
}
else
{
loopIndex--;
if (loopIndex < 0)
loopIndex = (sint)loop.size()-1;
}
thisChainId = surface._Chains[loop[loopIndex]].Chain;
thisChainForward = (_Chains[thisChainId].getLeft() == surfaceId);
thisOChainIndex = (thisChainForward == forward) ?
0 : (sint)_Chains[thisChainId]._SubChains.size()-1;
}
thisOChainId = _Chains[thisChainId]._SubChains[thisOChainIndex];
}
path.push_back(CVector2s(A.Estimation+intersections[i+1].Position*(B.Estimation-A.Estimation)));
i += 2;
}
path.push_back(CVector2s(B.Estimation));
}
#ifdef NL_OS_WINDOWS
//#pragma optimize( "", on )
#endif // NL_OS_WINDOWS
// ***************************************************************************
// ***************************************************************************
// Collisions part.
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
void NLPACS::CLocalRetriever::computeCollisionChainQuad()
{
_ChainQuad.build(_OrderedChains);
}
// ***************************************************************************
void NLPACS::CLocalRetriever::testCollision(CCollisionSurfaceTemp &cst, const CAABBox &bboxMove, const CVector2f &transBase) const
{
if (!_Loaded)
return;
// H_AUTO(PACS_LR_testCollision);
sint i;
// 0. select ordered chains in the chainquad.
//=====================================
// H_BEFORE(PACS_LR_testCol_selEdges);
sint nEce= _ChainQuad.selectEdges(bboxMove, cst);
// H_AFTER(PACS_LR_testCol_selEdges);
// NB: cst.OChainLUT is assured to be full of 0xFFFF after this call (if was right before).
// 1. regroup them in chains. build cst.CollisionChains
//=====================================
// NB: use cst.OChainLUT to look if a Chain has been inserted before.
uint16 *chainLUT= cst.OChainLUT;
// bkup where we begin to add chains.
uint firstChainAdded= (uint)cst.CollisionChains.size();
// For all edgechain entry.
for(i=0;i<nEce;i++)
{
CEdgeChainEntry &ece= cst.EdgeChainEntries[i];
// this is the ordered chain in the retriever.
const COrderedChain &oChain= this->getOrderedChains()[ece.OChainId];
// this is the id of the chain is the local retriever.
uint16 chainId= oChain.getParentId();
// test if edge is interior and points to another instance
if (_Type == Interior && CChain::isBorderChainId(this->getChains()[chainId].getRight()))
{
// then look for a door that match this edge
uint l;
for (l=0; l<_ExteriorMesh.getLinks().size() && _ExteriorMesh.getLink(l).ChainId != chainId; ++l)
;
// if found a door, then leave the edge as is
if (l < _ExteriorMesh.getLinks().size())
continue;
}
// add/retrieve the id in cst.CollisionChains.
//=================================
uint ccId;
// if never added.
if(chainLUT[chainId]==0xFFFF)
{
// H_AUTO(PACS_LR_testCol_addToLUT);
// add a new CCollisionChain.
ccId= (uint)cst.CollisionChains.size();
cst.CollisionChains.push_back(CCollisionChain());
// Fill it with default.
cst.CollisionChains[ccId].Tested= false;
cst.CollisionChains[ccId].ExteriorEdge = false;
cst.CollisionChains[ccId].FirstEdgeCollide= 0xFFFFFFFF;
cst.CollisionChains[ccId].ChainId= chainId;
// Fill Left right info.
cst.CollisionChains[ccId].LeftSurface.SurfaceId= this->getChains()[chainId].getLeft();
cst.CollisionChains[ccId].RightSurface.SurfaceId= this->getChains()[chainId].getRight();
// NB: cst.CollisionChains[ccId].*Surface.RetrieverInstanceId is not filled here because we don't have
// this info at this level.
// store this Id in the LUT of chains.
chainLUT[chainId]= uint16(ccId);
}
else
{
// get the id of this collision chain.
ccId= chainLUT[chainId];
}
// add edge collide to the list.
//=================================
// H_BEFORE(PACS_LR_testCol_addToList);
CCollisionChain &colChain= cst.CollisionChains[ccId];
const std::vector<CVector2s> &oChainVertices= oChain.getVertices();
for(sint edge=ece.EdgeStart; edge<ece.EdgeEnd; edge++)
{
CVector2f p0= oChainVertices[edge].unpack();
CVector2f p1= oChainVertices[edge+1].unpack();
// alloc a new edgeCollide.
uint32 ecnId= cst.allocEdgeCollideNode();
CEdgeCollideNode &ecn= cst.getEdgeCollideNode(ecnId);
// append to the front of the list.
ecn.Next= colChain.FirstEdgeCollide;
colChain.FirstEdgeCollide= ecnId;
// build this edge.
p0+= transBase;
p1+= transBase;
ecn.make(p0, p1);
}
// H_AFTER(PACS_LR_testCol_addToList);
}
// 2. Reset LUT to 0xFFFF.
//=====================================
// H_BEFORE(PACS_LR_testCol_resetLUT);
// for all collisions chains inserted (starting from firstChainAdded), reset LUT.
for(i=firstChainAdded; i<(sint)cst.CollisionChains.size(); i++)
{
uint ccId= cst.CollisionChains[i].ChainId;
chainLUT[ccId]= 0xFFFF;
}
// H_AFTER(PACS_LR_testCol_resetLUT);
}
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
void NLPACS::CLocalRetriever::buildInteriorSurfaceBBoxes(std::vector<NLMISC::CAABBox> &surfaceBBoxes) const
{
// resize dest, and init.
vector<bool> firstTriangle;
surfaceBBoxes.clear();
surfaceBBoxes.resize(_Surfaces.size());
firstTriangle.resize(_Surfaces.size(), true);
// For all _InteriorFaces.
for(uint iIntFace=0; iIntFace<_InteriorFaces.size(); iIntFace++)
{
const CInteriorFace &intFace= _InteriorFaces[iIntFace];
// Extend the surface of this face with her 3 points.
// check good id.
if(intFace.Surface==(uint)-1)
continue;
nlassert(intFace.Surface<_Surfaces.size());
// If first time we extend the bbox of this surface
if(firstTriangle[intFace.Surface])
{
surfaceBBoxes[intFace.Surface].setCenter(_InteriorVertices[intFace.Verts[0]] );
firstTriangle[intFace.Surface]= false;
}
else
surfaceBBoxes[intFace.Surface].extend(_InteriorVertices[intFace.Verts[0]] );
// extend with other 2 points
surfaceBBoxes[intFace.Surface].extend(_InteriorVertices[intFace.Verts[1]] );
surfaceBBoxes[intFace.Surface].extend(_InteriorVertices[intFace.Verts[2]] );
}
}
// ***************************************************************************
void NLPACS::CLocalRetriever::replaceChain(uint32 chainId, const std::vector<NLPACS::CLocalRetriever::CChainReplacement> &replacement)
{
// free subchains
uint i, j;
for (i=0; i<_Chains[chainId]._SubChains.size(); ++i)
{
FreeOChains.push_back(_Chains[chainId]._SubChains[i]);
_OrderedChains[_Chains[chainId]._SubChains[i]] = COrderedChain();
_FullOrderedChains[_Chains[chainId]._SubChains[i]] = COrderedChain3f();
}
// create new chains in replacement of this chain
for (i=0; i<replacement.size(); ++i)
{
vector<CVector> vertices = replacement[i].Vertices;
sint left = replacement[i].Left;
sint right = replacement[i].Right;
if (CChain::isBorderChainId(right))
{
// check border already exists for this particular chain
sint32 border = CChain::convertBorderChainId(right);
if (border < (sint)_BorderChains.size() && (chainId != _BorderChains[border] || chainId != replacement[i].Chain))
{
nlwarning("replaceChain(): replacement of a border is forced whereas this border is already used and not replaced!");
}
if (border >= (sint)_BorderChains.size())
{
if (border > (sint)_BorderChains.size())
{
nlwarning("replaceChain(): _BorderChains size increased of more than 1 step, holes may result!");
}
_BorderChains.resize(border+1, 0xffff);
}
_BorderChains[border] = uint16(replacement[i].Chain);
}
nlassert(vertices.size() >= 2);
// Remove doubled vertices due to CVector2s snapping
vector<CVector2s> converts;
for (j=0; j<vertices.size(); ++j)
converts.push_back(CVector2s(vertices[j]));
vector<CVector2s>::iterator next2s = converts.begin(), it2s, prev2s;
prev2s = next2s; ++next2s;
it2s = next2s; ++next2s;
vector<CVector>::iterator it3f = vertices.begin();
CVector prev3f = *it3f;
++it3f;
for (; it2s != converts.end() && next2s != converts.end(); )
{
// if the next point is equal to the previous
if (*it2s == *prev2s || *it2s == *next2s)
{
// then remove the next point
it2s = converts.erase(it2s);
it3f = vertices.erase(it3f);
prev2s = it2s;
--prev2s;
next2s = it2s;
++next2s;
}
else
{
// else remember the next point, and step to the next...
++prev2s;
++it2s;
++next2s;
++it3f;
prev3f = *it3f;
}
}
nlassert(vertices.size() >= 2);
sint32 newId = replacement[i].Chain;
if (newId >= (sint)_Chains.size())
_Chains.resize(newId+1);
//CChain &nchain = _Chains[newId];
if (left>(sint)_Surfaces.size())
nlerror ("left surface id MUST be id<%d (id=%d)", _Surfaces.size(), left);
if (right>(sint)_Surfaces.size())
nlerror ("right surface id MUST be id<%d (id=%d)", _Surfaces.size(), right);
// checks if we can build the chain.
if (newId > 65535)
nlerror("in NLPACS::CLocalRetriever::addChain(): reached the maximum number of chains");
//CRetrievableSurface *leftSurface = (left>=0) ? &(_Surfaces[left]) : NULL;
//CRetrievableSurface *rightSurface = (right>=0) ? &(_Surfaces[right]) : NULL;
CChain &chain = _Chains[newId];
chain._StartTip = 0xffff;
chain._StopTip = 0xffff;
// make the chain and its subchains.
chain.make(vertices, left, right, _OrderedChains, (uint16)newId, _FullOrderedChains, FreeOChains);
}
for (i=0; i<_Surfaces.size(); ++i)
{
// remove old chain and replace by new chains in surface links
for (j=0; j<_Surfaces[i]._Chains.size(); ++j)
{
if (_Surfaces[i]._Chains[j].Chain == (sint)chainId)
{
_Surfaces[i]._Chains.erase(_Surfaces[i]._Chains.begin()+j);
uint k;
for (k=0; k<replacement.size(); ++k)
{
CRetrievableSurface::CSurfaceLink link;
link.Chain = replacement[k].Chain;
link.Surface = (replacement[k].Left == (sint)i ? replacement[k].Right : replacement[k].Left);
_Surfaces[i]._Chains.push_back(link);
}
break;
}
}
// remove old chain and replace by new chains in surface loops
for (j=0; j<_Surfaces[i]._Loops.size(); ++j)
{
uint k;
for (k=0; k<_Surfaces[i]._Loops[j].size(); ++k)
{
if (_Surfaces[i]._Loops[j][k] == chainId)
{
_Surfaces[i]._Loops[j].erase(_Surfaces[i]._Loops[j].begin()+k);
uint m;
for (m=0; m<replacement.size(); ++m)
_Surfaces[i]._Loops[j].insert(_Surfaces[i]._Loops[j].begin()+k+m, uint16(replacement[m].Chain));
break;
}
}
}
}
}
/*
* Check surface integrity
*/
bool NLPACS::CLocalRetriever::checkSurfacesIntegrity(NLMISC::CVector translation, bool verbose) const
{
bool success = true;
uint surf;
for (surf=0; surf<_Surfaces.size(); ++surf)
{
if (!checkSurfaceIntegrity(surf, translation, verbose))
{
success = false;
if (verbose)
{
dumpSurface(surf, translation);
}
}
}
return success;
}
/**
* Check surface integrity
*/
bool NLPACS::CLocalRetriever::checkSurfaceIntegrity(uint surf, NLMISC::CVector translation, bool verbose) const
{
if (surf >= _Surfaces.size())
return false;
const CRetrievableSurface& surface = _Surfaces[surf];
uint nloops = (uint)surface.getLoops().size();
std::vector<std::pair<CVector2s, CVector2s> > edges;
uint iloop;
uint i, j, k;
for (iloop=0; iloop<nloops; ++iloop)
{
const CRetrievableSurface::TLoop& loop = surface.getLoop(iloop);
for (i=0; i<loop.size(); ++i)
{
// loop[i] is the index in the surface list of chains
// so surface._Chains[loop[i]].Chain is really the chain id !!!
const CChain& chain = _Chains[ surface._Chains[loop[i]].Chain ];
for (j=0; j<chain.getSubChains().size(); ++j)
{
const COrderedChain& ochain = _OrderedChains[chain.getSubChain(j)];
for (k=0; k+1<ochain.getVertices().size(); ++k)
{
edges.push_back(make_pair<CVector2s, CVector2s>(ochain[k], ochain[k+1]));
}
}
}
}
bool success = true;
for (i=0; i+1<edges.size(); ++i)
{
for (j=i+1; j<edges.size(); ++j)
{
CVector2s a0 = edges[i].first,
a1 = edges[i].second;
CVector2s b0 = edges[j].first,
b1 = edges[j].second;
double a, b;
bool inters = CVector2s::intersect(a0, a1, b0, b1, &a, &b);
if (!inters)
continue;
double da = (a1-a0).norm();
double db = (b1-b0).norm();
bool tipa = (fabs(a)*da < 4.0e-2 || fabs(1.0-a)*da < 4.0e-2);
bool tipb = (fabs(b)*db < 4.0e-2 || fabs(1.0-b)*db < 4.0e-2);
if (tipa && tipb)
continue;
CVector2s ip = a0 + (a1-a0)*(float)a;
CVector p = ip.unpack3f() - translation;
nlwarning("Surface %d has issue at position (%f,%f)", surf, p.x, p.y);
if (verbose)
{
NLMISC::CVector v;
v = a0.unpack3f() - translation;
nlwarning(" -- a0: (%f,%f)", v.x, v.y);
v = a1.unpack3f() - translation;
nlwarning(" -- a1: (%f,%f)", v.x, v.y);
v = b0.unpack3f() - translation;
nlwarning(" -- b0: (%f,%f)", v.x, v.y);
v = b1.unpack3f() - translation;
nlwarning(" -- b1: (%f,%f)", v.x, v.y);
}
success = false;
}
}
return success;
}