// 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/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= 3); surf._Flags = 0; surf._Flags |= (surf._IsFloor) ? (1< &verts, sint32 left, sint32 right) { vector 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 converts; for (i=0; i::iterator next2s = converts.begin(), it2s, prev2s; prev2s = next2s; ++next2s; it2s = next2s; ++next2s; vector::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 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 chainFlags; chainFlags.resize(surface._Chains.size()); for (j=0; j 4.0e-2f)&& loopCloseDistance > 4.0e-2f) { nlwarning("in NLPACS::CLocalRetriever::computeTips()"); dumpSurface(i); for (j=0; j 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 &polygons) const { const CRetrievableSurface &surf = _Surfaces[surface]; uint i, j, k, l; for (i=0; i0; --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 &polygons) const { const CRetrievableSurface &surf = _Surfaces[surface]; uint i, j, k, l; for (i=0; i0; --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 surfacesStack; surfacesStack.push_back(surface); while (!surfacesStack.empty()) { CRetrievableSurface ¤t = _Surfaces[surfacesStack.back()]; surfacesStack.pop_back(); current._Topologies[character] = topology; uint i; for (i=0; i=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 ¤t = _Surfaces[surface]; for (character=0; 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= 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; i1.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 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 &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)= 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 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 &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 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::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 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::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 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::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 &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 intersections; uint i, j; sint32 surfaceId = A.Surface; const CRetrievableSurface &surface = _Surfaces[surfaceId]; for (i=0; i 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= (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;igetOrderedChains()[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 &oChainVertices= oChain.getVertices(); for(sint edge=ece.EdgeStart; edge &surfaceBBoxes) const { // resize dest, and init. vector 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 &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 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 converts; for (j=0; j::iterator next2s = converts.begin(), it2s, prev2s; prev2s = next2s; ++next2s; it2s = next2s; ++next2s; vector::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= _Surfaces.size()) return false; const CRetrievableSurface& surface = _Surfaces[surf]; uint nloops = (uint)surface.getLoops().size(); std::vector > edges; uint iloop; uint i, j, k; for (iloop=0; iloop(ochain[k], ochain[k+1])); } } } } bool success = true; for (i=0; i+1