Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / nel / src / pacs / global_retriever.cpp
blob73691e19de878d799ffaa12005ff237cebfef272
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "stdpacs.h"
19 #include "nel/pacs/global_retriever.h"
20 #include "nel/pacs/retriever_bank.h"
22 #include "nel/misc/async_file_manager.h"
23 #include "nel/misc/common.h"
24 #include "nel/misc/hierarchical_timer.h"
25 #include "nel/misc/line.h"
26 #include "nel/misc/path.h"
27 #include "nel/misc/time_nl.h"
28 #include "nel/misc/variable.h"
30 NLMISC::TTicks AStarTicks;
31 NLMISC::TTicks PathTicks;
32 NLMISC::TTicks ChainTicks;
33 NLMISC::TTicks SurfTicks;
34 NLMISC::TTicks ThisAStarTicks;
35 NLMISC::TTicks ThisPathTicks;
36 NLMISC::TTicks ThisChainTicks;
37 NLMISC::TTicks ThisSurfTicks;
39 uint PacsRetrieveVerbose = 0;
41 using namespace std;
42 using namespace NLMISC;
44 const float InsureSurfaceThreshold = 0.5f; // the threshold distance between 2 surfaces below which we insure the retrieved position to be inside the surface
46 H_AUTO_DECL ( NLPACS_Refresh_LR_Around )
47 H_AUTO_DECL ( NLPACS_Retrieve_Position )
49 #define NLPACS_HAUTO_REFRESH_LR_AROUND H_AUTO_USE ( NLPACS_Refresh_LR_Around )
50 #define NLPACS_HAUTO_RETRIEVE_POSITION H_AUTO_USE ( NLPACS_Retrieve_Position )
52 // CGlobalRetriever methods implementation
54 NLPACS::CGlobalRetriever::~CGlobalRetriever()
56 // must be sure all current async loading is ended
57 waitEndOfAsyncLoading();
61 void NLPACS::CGlobalRetriever::init()
63 _BBox.setCenter(CVector::Null);
64 _BBox.setHalfSize(CVector::Null);
66 _InstanceGrid.create(128, 160.0f);
69 void NLPACS::CGlobalRetriever::initQuadGrid()
71 _InstanceGrid.clear();
72 _InstanceGrid.create(128, 160.0f);
74 uint i;
75 for (i=0; i<_Instances.size(); ++i)
76 _InstanceGrid.insert(_Instances[i].getBBox().getMin(), _Instances[i].getBBox().getMax(), i);
79 void NLPACS::CGlobalRetriever::initRetrieveTable()
81 uint i;
82 uint size = 0;
84 for (i=0; i<_Instances.size(); ++i)
86 if (_Instances[i].getInstanceId() != -1 && _Instances[i].getRetrieverId() != -1)
88 const CLocalRetriever &retriever = getRetriever(_Instances[i].getRetrieverId());
89 size = std::max((uint)retriever.getSurfaces().size(), size);
93 _RetrieveTable.resize(size);
94 for (i=0; i<size; ++i)
95 _RetrieveTable[i] = 0;
100 bool NLPACS::CGlobalRetriever::selectInstances(const NLMISC::CAABBox &bbox, CCollisionSurfaceTemp &cst, UGlobalPosition::TType type) const
102 _InstanceGrid.select(bbox.getMin(), bbox.getMax());
103 cst.CollisionInstances.clear();
105 bool allLoaded = true;
107 NLPACS::CQuadGrid<uint32>::CIterator it;
108 for (it=_InstanceGrid.begin(); it!=_InstanceGrid.end(); ++it)
110 if ((type == UGlobalPosition::Landscape && _Instances[*it].getType() == CLocalRetriever::Interior) ||
111 (type == UGlobalPosition::Interior && _Instances[*it].getType() == CLocalRetriever::Landscape))
112 continue;
114 if (_Instances[*it].getBBox().intersect(bbox))
116 if (!_RetrieverBank->isLoaded(_Instances[*it].getRetrieverId()))
117 allLoaded = false;
118 cst.CollisionInstances.push_back(*it);
122 return allLoaded;
127 void NLPACS::CGlobalRetriever::serial(NLMISC::IStream &f)
130 Version 0:
131 - base version.
133 (void)f.serialVersion(0);
135 f.serialCont(_Instances);
136 f.serial(_BBox);
138 if (f.isReading())
139 initAll(false);
144 void NLPACS::CGlobalRetriever::check() const
146 uint i, j, k;
148 for (i=0; i<_Instances.size(); ++i)
150 if (_Instances[i].getInstanceId() == -1)
152 nlwarning("Uninitialized instance %d", i);
153 continue;
156 if (_Instances[i].getInstanceId() != (sint)i)
157 nlwarning("InstanceId for instance %d is not correctly initialized", i);
159 if (_Instances[i].getRetrieverId() == -1)
161 nlwarning("No retriever at instance %d", i);
162 continue;
165 const CRetrieverInstance &instance = _Instances[i];
167 if (instance.getRetrieverId()<0 || instance.getRetrieverId()>=(sint)_RetrieverBank->getRetrievers().size())
169 nlwarning("Instance %d has wrong retriever reference", i);
170 continue;
173 const CLocalRetriever &retriever = _RetrieverBank->getRetriever(instance.getRetrieverId());
175 for (j=0; j<retriever.getChains().size(); ++j)
177 const CChain &chain = retriever.getChain(j);
178 for (k=0; k<chain.getSubChains().size(); ++k)
180 if (chain.getSubChain(k) >= retriever.getOrderedChains().size())
182 nlwarning("retriever %d, chain %d: subchain %d reference is not valid", instance.getRetrieverId(), j, k);
183 continue;
186 if (retriever.getOrderedChain(chain.getSubChain(k)).getParentId() != j)
188 nlwarning("retriever %d, ochain %d: reference on parent is not valid", instance.getRetrieverId(), chain.getSubChain(k));
189 continue;
192 if (retriever.getOrderedChain(chain.getSubChain(k)).getIndexInParent() != k)
194 nlwarning("retriever %d, ochain %d: index on parent is not valid", instance.getRetrieverId(), chain.getSubChain(k));
195 continue;
199 if (chain.getLeft()<0 || chain.getLeft()>=(sint)retriever.getSurfaces().size())
201 nlwarning("retriever %d, chain %d: reference on left surface is not valid", instance.getRetrieverId(), j);
204 if (chain.getRight()>=(sint)retriever.getSurfaces().size() ||
205 (chain.getRight()<=CChain::getDummyBorderChainId() && !CChain::isBorderChainId(chain.getRight())))
207 nlwarning("retriever %d, chain %d: reference on right surface is not valid", instance.getRetrieverId(), j);
210 if (CChain::isBorderChainId(chain.getRight()))
212 sint link = chain.getBorderChainIndex();
214 if (link<0 || link>=(sint)instance.getBorderChainLinks().size())
216 nlwarning("retriever %d, instance %d, chain %d: reference on right link is not valid", instance.getRetrieverId(), instance.getInstanceId(), j);
218 else
220 CRetrieverInstance::CLink lnk = instance.getBorderChainLink(link);
222 if (lnk.Instance != 0xFFFF || lnk.SurfaceId != 0xFFFF ||
223 lnk.ChainId != 0xFFFF || lnk.BorderChainId != 0xFFFF)
225 if (lnk.Instance >= _Instances.size() ||
226 _Instances[lnk.Instance].getRetrieverId()<0 ||
227 _Instances[lnk.Instance].getRetrieverId()>(sint)_RetrieverBank->getRetrievers().size() ||
228 lnk.SurfaceId >= getRetriever(_Instances[lnk.Instance].getRetrieverId()).getSurfaces().size() ||
229 ((lnk.ChainId >= getRetriever(_Instances[lnk.Instance].getRetrieverId()).getChains().size() ||
230 lnk.BorderChainId >= getRetriever(_Instances[lnk.Instance].getRetrieverId()).getBorderChains().size()) && instance.getType() != CLocalRetriever::Interior ))
232 nlwarning("retriever %d, instance %d, link %d: reference on instance may be not valid [Inst=%d, Surf=%d, Chain=%d, BorderChain=%d]", instance.getRetrieverId(), instance.getInstanceId(), link, lnk.Instance, lnk.SurfaceId, lnk.ChainId, lnk.BorderChainId);
243 float NLPACS::CGlobalRetriever::distanceToBorder(const UGlobalPosition &pos) const
245 if (pos.InstanceId < 0 || pos.InstanceId > (sint)_Instances.size())
246 return 0.0f;
248 return getRetriever(_Instances[pos.InstanceId].getRetrieverId()).distanceToBorder(pos.LocalPosition);
251 void NLPACS::CGlobalRetriever::getBorders(const UGlobalPosition &pos, std::vector<std::pair<NLMISC::CLine, uint8> > &edges)
253 edges.clear();
255 if (pos.InstanceId < 0)
256 return;
258 CVectorD gpos = getDoubleGlobalPosition(pos);
259 CAABBox sbox;
260 sbox.setCenter(gpos);
261 sbox.setHalfSize(CVector(50.0f, 50.0f, 100.0f));
263 getBorders(sbox, edges);
266 void NLPACS::CGlobalRetriever::getBorders(const CAABBox &sbox, std::vector<std::pair<NLMISC::CLine, uint8> > &edges)
268 edges.clear();
270 selectInstances(sbox, _InternalCST);
272 uint inst;
273 for (inst=0; inst<_InternalCST.CollisionInstances.size(); ++inst)
275 CRetrieverInstance &instance = _Instances[_InternalCST.CollisionInstances[inst]];
276 CLocalRetriever &retriever = const_cast<CLocalRetriever &>(getRetriever(instance.getRetrieverId()));
277 if (!retriever.isLoaded())
278 continue;
280 CChainQuad &chainquad = retriever.getChainQuad();
282 CAABBox box;
283 CVector origin = instance.getOrigin();
284 box.setCenter(sbox.getCenter()-origin);
285 box.setHalfSize(sbox.getHalfSize());
286 chainquad.selectEdges(box, _InternalCST);
288 uint ece;
290 CVector dz(0.0f, 0.0f, 0.5f);
291 float zp = (float)sbox.getCenter().z;
292 for (ece=0; ece<_InternalCST.EdgeChainEntries.size(); ++ece)
294 CEdgeChainEntry &entry = _InternalCST.EdgeChainEntries[ece];
297 const CChain &fchain = retriever.getChain(retriever.getOrderedChain(entry.OChainId).getParentId());
298 uint8 chainType = (fchain.getRight() >= 0 ? 1 : (fchain.isBorderChain() ? 2 : 0));
301 if (chainType == 1)
303 uint left = fchain.getLeft();
304 uint right = fchain.getRight();
306 const CRetrievableSurface &lsurface = retriever.getSurface(left);
307 const CRetrievableSurface &rsurface = retriever.getSurface(right);
309 bool luw = (lsurface.getFlags() & (1 << CRetrievableSurface::IsUnderWaterBit)) != 0;
310 bool ruw = (rsurface.getFlags() & (1 << CRetrievableSurface::IsUnderWaterBit)) != 0;
312 if (luw != ruw)
313 chainType = 3;
316 if (!retriever.getFullOrderedChains().empty())
318 const COrderedChain3f &ochain = retriever.getFullOrderedChain(entry.OChainId);
320 uint edge;
321 for (edge=entry.EdgeStart; edge<entry.EdgeEnd; ++edge)
323 edges.push_back(make_pair(CLine(), chainType));
324 edges.back().first.V0 = ochain[edge] + origin;
325 edges.back().first.V1 = ochain[edge+1] + origin;
327 edges.push_back(make_pair(CLine(), chainType));
328 edges.back().first.V0 = ochain[edge] + origin;
329 edges.back().first.V1 = ochain[edge] + origin +dz;
331 edges.push_back(make_pair(CLine(), chainType));
332 edges.back().first.V0 = ochain[edge+1] + origin;
333 edges.back().first.V1 = ochain[edge+1] + origin +dz;
337 else
339 const COrderedChain &ochain = retriever.getOrderedChain(entry.OChainId);
341 uint edge;
342 for (edge=entry.EdgeStart; edge<entry.EdgeEnd; ++edge)
344 edges.push_back(make_pair(CLine(), chainType));
345 edges.back().first.V0 = ochain[edge].unpack3f() + origin;
346 edges.back().first.V0.z = zp;
347 edges.back().first.V1 = ochain[edge+1].unpack3f() + origin;
348 edges.back().first.V1.z = zp;
352 // Bind edges for exterior mesh
353 const CExteriorMesh &em = retriever.getExteriorMesh();
354 const CExteriorMesh::CEdge *previousEdge = NULL;
355 for(uint k = 0; k < em.getEdges().size(); ++k)
357 if (previousEdge)
359 edges.push_back(make_pair(CLine(), previousEdge->Link < 0 ? 4 : 5));
360 edges.back().first.V0 = previousEdge->Start + origin;
361 edges.back().first.V1 = em.getEdges()[k].Start + origin;
363 previousEdge = em.getEdges()[k].Link != -2 ? &em.getEdges()[k] : NULL;
371 void NLPACS::CGlobalRetriever::makeLinks(uint n)
373 CRetrieverInstance &instance = _Instances[n];
375 selectInstances(instance.getBBox(), _InternalCST);
377 uint i;
378 for (i=0; i<_InternalCST.CollisionInstances.size(); ++i)
380 CRetrieverInstance &neighbor = _Instances[_InternalCST.CollisionInstances[i]];
382 if (neighbor.getInstanceId() == instance.getInstanceId())
383 continue;
387 instance.link(neighbor, _RetrieverBank->getRetrievers());
388 neighbor.link(instance, _RetrieverBank->getRetrievers());
390 catch (const Exception &e)
392 nlwarning("in NLPACS::CGlobalRetriever::makeLinks()");
393 nlwarning("caught an exception during linkage of %d and %d: %s", instance.getInstanceId(), neighbor.getInstanceId(), e.what());
397 if (getRetriever(instance.getRetrieverId()).getType() == CLocalRetriever::Interior)
398 instance.linkEdgeQuad(*this);
401 void NLPACS::CGlobalRetriever::resetAllLinks()
403 uint n;
404 for (n=0; n<_Instances.size(); ++n)
405 _Instances[n].unlink(_Instances);
409 void NLPACS::CGlobalRetriever::makeAllLinks()
411 resetAllLinks();
413 uint n;
414 for (n=0; n<_Instances.size(); ++n)
415 makeLinks(n);
418 void NLPACS::CGlobalRetriever::initAll(bool initInstances)
420 if (initInstances)
422 uint n;
423 for (n=0; n<_Instances.size(); ++n)
424 if (_Instances[n].getInstanceId() != -1 && _Instances[n].getRetrieverId() != -1)
425 _Instances[n].init(_RetrieverBank->getRetriever(_Instances[n].getRetrieverId()));
428 initQuadGrid();
429 initRetrieveTable();
434 const NLPACS::CRetrieverInstance &NLPACS::CGlobalRetriever::makeInstance(uint32 retrieverId, uint8 orientation, const CVector &origin)
436 uint id;
437 for (id=0; id<_Instances.size() && _Instances[id].getInstanceId()!=-1; ++id)
440 if (id == _Instances.size())
441 _Instances.resize(id+1);
443 CRetrieverInstance &instance = _Instances[id];
444 const CLocalRetriever &retriever = getRetriever(retrieverId);
446 if (_RetrieveTable.size() < retriever.getSurfaces().size())
447 _RetrieveTable.resize(retriever.getSurfaces().size(), 0);
449 instance.make(id, retrieverId, retriever, orientation, origin);
451 CVector hsize = instance.getBBox().getHalfSize();
452 hsize.z = 0.0f;
453 if (hsize != CVector::Null)
455 if (_BBox.getHalfSize() == CVector::Null)
457 _BBox = instance.getBBox();
459 else
461 _BBox.extend(instance.getBBox().getMin());
462 _BBox.extend(instance.getBBox().getMax());
465 if (getRetriever(instance.getRetrieverId()).getType() == CLocalRetriever::Interior)
466 instance.initEdgeQuad(*this);
468 _InstanceGrid.insert(instance.getBBox().getMin(), instance.getBBox().getMax(), instance.getInstanceId());
471 return instance;
476 NLPACS::UGlobalPosition NLPACS::CGlobalRetriever::retrievePosition(const CVector &estimated, float threshold) const
478 return retrievePosition(CVectorD(estimated), (double)threshold, UGlobalPosition::Unspecified);
481 NLPACS::UGlobalPosition NLPACS::CGlobalRetriever::retrievePosition(const CVectorD &estimated, double threshold) const
483 return retrievePosition(estimated, threshold, UGlobalPosition::Unspecified);
486 NLPACS::UGlobalPosition NLPACS::CGlobalRetriever::retrievePosition(const CVector &estimated) const
488 return retrievePosition(estimated, 1.0e10f, UGlobalPosition::Unspecified);
491 NLPACS::UGlobalPosition NLPACS::CGlobalRetriever::retrievePosition(const CVectorD &estimated) const
493 return retrievePosition(estimated, 1.0e10, UGlobalPosition::Unspecified);
496 // Retrieves the position of an estimated point in the global retriever (double instead.)
497 NLPACS::UGlobalPosition NLPACS::CGlobalRetriever::retrievePosition(const CVectorD &estimated, double /* threshold */, NLPACS::UGlobalPosition::TType retrieveSpec) const
499 NLPACS_HAUTO_RETRIEVE_POSITION
501 // the retrieved position
502 CGlobalPosition result = CGlobalPosition(-1, CLocalRetriever::CLocalPosition(-1, estimated));
504 if (!_BBox.include(CVector((float)estimated.x, (float)estimated.y, (float)estimated.z)))
506 _ForbiddenInstances.clear();
507 return result;
511 // get the best matching instances
512 CAABBox bbpos;
513 bbpos.setCenter(estimated);
514 bbpos.setHalfSize(CVector(0.5f, 0.5f, 0.5f));
515 if (!selectInstances(bbpos, _InternalCST, retrieveSpec))
517 return result;
520 uint i;
522 _InternalCST.SortedSurfaces.clear();
524 // for each instance, try to retrieve the position
525 for (i=0; i<_InternalCST.CollisionInstances.size(); ++i)
527 uint32 id = _InternalCST.CollisionInstances[i];
528 const CRetrieverInstance &instance = _Instances[id];
529 const CLocalRetriever &retriever = _RetrieverBank->getRetriever(instance.getRetrieverId());
531 uint j;
532 for (j=0; j<_ForbiddenInstances.size(); ++j)
533 if (_ForbiddenInstances[j] == (sint32)id)
534 break;
536 if (j<_ForbiddenInstances.size() || !retriever.isLoaded())
537 continue;
539 instance.retrievePosition(estimated, retriever, _InternalCST);
542 _ForbiddenInstances.clear();
544 if (!_InternalCST.SortedSurfaces.empty())
546 // if there are some selected surfaces, sort them
547 std::sort(_InternalCST.SortedSurfaces.begin(), _InternalCST.SortedSurfaces.end(), CCollisionSurfaceTemp::CDistanceSurface());
549 uint selInstance;
550 float bestDist = 1.0e10f;
551 for (selInstance=0; selInstance<_InternalCST.SortedSurfaces.size(); ++selInstance)
553 uint32 id = _InternalCST.SortedSurfaces[selInstance].Instance;
554 const CRetrieverInstance &instance = _Instances[id];
556 if (instance.getType() == CLocalRetriever::Interior && _InternalCST.SortedSurfaces[selInstance].Distance < bestDist+6.0f)
557 break;
559 if (selInstance == 0)
560 bestDist = _InternalCST.SortedSurfaces[0].Distance;
563 if (selInstance >= _InternalCST.SortedSurfaces.size())
564 selInstance = 0;
566 uint32 id = _InternalCST.SortedSurfaces[selInstance].Instance;
567 const CRetrieverInstance &instance = _Instances[id];
568 const CLocalRetriever &retriever = _RetrieverBank->getRetriever(instance.getRetrieverId());
570 // get the UGlobalPosition of the estimation for this surface
571 result.InstanceId = id;
572 result.LocalPosition.Surface = _InternalCST.SortedSurfaces[selInstance].Surface;
573 result.LocalPosition.Estimation = instance.getLocalPosition(estimated);
575 CRetrieverInstance::snapVector(result.LocalPosition.Estimation);
577 // if there are more than 1 one possible (and best matching) surface, insure the position within the surface (by moving the point)
578 // if (_InternalCST.SortedSurfaces.size() >= 2 &&
579 // _InternalCST.SortedSurfaces[1].Distance-_InternalCST.SortedSurfaces[0].Distance < InsureSurfaceThreshold)
580 if (_InternalCST.SortedSurfaces[selInstance].FoundCloseEdge)
582 bool moved;
583 uint numMove = 0;
586 moved = retriever.insurePosition(result.LocalPosition);
587 ++numMove;
589 while (moved && numMove < 100);
590 // the algo won't loop infinitely
592 if (numMove > 1)
594 nldebug("PACS: insured position inside surface (%d,%d)-(%f,%f,%f), %d moves needed", result.InstanceId, result.LocalPosition.Surface, estimated.x, estimated.y, estimated.z, numMove-1);
597 if (moved)
599 nlwarning ("PACS: couldn't insure position (%.f,%.f) within the surface (surf=%d,inst=%d) after 100 retries", result.LocalPosition.Estimation.x, result.LocalPosition.Estimation.y, result.LocalPosition.Surface, result.InstanceId);
603 // and after selecting the best surface (and some replacement) snap the point to the surface
604 instance.snap(result.LocalPosition, retriever);
607 if (PacsRetrieveVerbose)
608 nlinfo("retrievePosition(%f,%f,%f) -> %d/%d/(%f,%f,%f) - %s/%s",
609 estimated.x, estimated.y, estimated.z,
610 result.InstanceId, result.LocalPosition.Surface,
611 result.LocalPosition.Estimation.x, result.LocalPosition.Estimation.y, result.LocalPosition.Estimation.z,
612 retriever.getIdentifier().c_str(),
613 retriever.getType() == CLocalRetriever::Interior ? "Interior" : "Landscape");
615 else
617 if (PacsRetrieveVerbose)
618 nlwarning("PACS: unable to retrieve correct position (%f,%f,%f)", estimated.x, estimated.y, estimated.z);
619 // nlSleep(1);
622 return result;
627 // Retrieves the position of an estimated point in the global retriever using layer hint
628 NLPACS::UGlobalPosition NLPACS::CGlobalRetriever::retrievePosition(const CVectorD &estimated, uint h, sint &res) const
630 // the retrieved position
631 CGlobalPosition result = CGlobalPosition(-1, CLocalRetriever::CLocalPosition(-1, estimated));
633 if (!_BBox.include(CVector((float)estimated.x, (float)estimated.y, (float)estimated.z)))
635 _ForbiddenInstances.clear();
636 res = Failed;
637 return result;
641 // get the best matching instances
642 CAABBox bbpos;
643 bbpos.setCenter(estimated);
644 bbpos.setHalfSize(CVector(0.5f, 0.5f, 0.5f));
645 bool canGet = selectInstances(bbpos, _InternalCST);
647 if (!canGet)
649 res = MissingLr;
650 return result;
653 uint i;
655 _InternalCST.SortedSurfaces.clear();
657 // for each instance, try to retrieve the position
658 for (i=0; i<_InternalCST.CollisionInstances.size(); ++i)
660 uint32 id = _InternalCST.CollisionInstances[i];
661 const CRetrieverInstance &instance = _Instances[id];
662 const CLocalRetriever &retriever = _RetrieverBank->getRetriever(instance.getRetrieverId());
664 uint j;
665 for (j=0; j<_ForbiddenInstances.size(); ++j)
666 if (_ForbiddenInstances[j] == (sint32)id)
667 break;
669 if (j<_ForbiddenInstances.size() || !retriever.isLoaded())
670 continue;
672 instance.retrievePosition(estimated, retriever, _InternalCST, false);
675 _ForbiddenInstances.clear();
677 if (!_InternalCST.SortedSurfaces.empty())
679 // if there are some selected surfaces, sort them
680 std::sort(_InternalCST.SortedSurfaces.begin(), _InternalCST.SortedSurfaces.end(), CCollisionSurfaceTemp::CDistanceSurface());
682 if (h >= _InternalCST.SortedSurfaces.size())
684 // found less surfaces than expected, abort
685 res = Failed;
686 return result;
689 uint32 id = _InternalCST.SortedSurfaces[h].Instance;
690 const CRetrieverInstance &instance = _Instances[id];
691 const CLocalRetriever &retriever = _RetrieverBank->getRetriever(instance.getRetrieverId());
693 // get the UGlobalPosition of the estimation for this surface
694 result.InstanceId = id;
695 result.LocalPosition.Surface = _InternalCST.SortedSurfaces[h].Surface;
696 result.LocalPosition.Estimation = instance.getLocalPosition(estimated);
698 CRetrieverInstance::snapVector(result.LocalPosition.Estimation);
700 // if there are more than 1 one possible (and best matching) surface, insure the position within the surface (by moving the point)
701 // if (_InternalCST.SortedSurfaces.size() >= 2 &&
702 // _InternalCST.SortedSurfaces[1].Distance-_InternalCST.SortedSurfaces[0].Distance < InsureSurfaceThreshold)
703 if (_InternalCST.SortedSurfaces[h].FoundCloseEdge)
705 bool moved;
706 uint numMove = 0;
709 moved = retriever.insurePosition(result.LocalPosition);
710 ++numMove;
712 while (moved && numMove < 100);
713 // the algo won't loop infinitely
715 if (numMove > 1)
717 nldebug("PACS: insured position inside surface (%d,%d)-(%f,%f,%f), %d moves needed", result.InstanceId, result.LocalPosition.Surface, estimated.x, estimated.y, estimated.z, numMove-1);
720 if (moved)
722 nlwarning ("PACS: couldn't insure position (%.f,%.f) within the surface (surf=%d,inst=%d) after 100 retries", result.LocalPosition.Estimation.x, result.LocalPosition.Estimation.y, result.LocalPosition.Surface, result.InstanceId);
726 // and after selecting the best surface (and some replacement) snap the point to the surface
727 instance.snap(result.LocalPosition, retriever);
729 else
731 res = Failed;
732 // nlwarning("PACS: unable to retrieve correct position (%f,%f,%f)", estimated.x, estimated.y, estimated.z);
733 // nlSleep(1);
736 res = Success;
737 return result;
743 sint32 NLPACS::CGlobalRetriever::getIdentifier(const string &id) const
745 sint32 i;
746 for (i=0; i<(sint32)(_RetrieverBank->getRetrievers().size()); ++i)
747 if (getRetriever(i).getIdentifier() == id)
748 return i;
750 return -1;
753 const string &NLPACS::CGlobalRetriever::getIdentifier(const NLPACS::UGlobalPosition &position) const
755 static const string nullString;
757 if (position.InstanceId == -1)
758 return nullString;
760 return getRetriever(_Instances[position.InstanceId].getRetrieverId()).getIdentifier();
763 sint32 NLPACS::CGlobalRetriever::getLocalRetrieverId(const NLPACS::UGlobalPosition &position) const
765 if (position.InstanceId == -1)
766 return -1;
768 return _Instances[position.InstanceId].getRetrieverId();
773 bool NLPACS::CGlobalRetriever::buildInstance(const string &id, const NLMISC::CVectorD &position, sint32 &instanceId)
776 sint32 retrieverId = getIdentifier(id);
778 instanceId = -1;
780 // check retriever exists
781 if (retrieverId < 0)
782 return false;
784 const CRetrieverInstance &instance = makeInstance(retrieverId, 0, CVector(position));
786 // check make instance success
787 if (instance.getInstanceId() == -1 || instance.getRetrieverId() != retrieverId)
788 return false;
790 // links new instance to its neighbors
791 makeLinks(instance.getInstanceId());
793 instanceId = instance.getInstanceId();
795 return true;
800 void NLPACS::CGlobalRetriever::removeInstance(sint32 instanceId)
802 if (instanceId < 0 || instanceId >= (sint32)_Instances.size() || _Instances[instanceId].getInstanceId() < 0)
804 nlwarning("CGlobalRetriever::removeInstance(): Can't unlink instance %d, doesn't exist", instanceId);
805 return;
808 // get instance
809 CRetrieverInstance &instance = _Instances[instanceId];
811 // unlink it from others
812 instance.unlink(_Instances);
821 void NLPACS::CGlobalRetriever::snapToInteriorGround(UGlobalPosition &position) const
823 const CRetrieverInstance &instance = _Instances[position.InstanceId];
824 if (instance.getType() != CLocalRetriever::Interior)
825 return;
827 const CLocalRetriever &retriever = getRetriever(instance.getRetrieverId());
828 instance.snapToInteriorGround(position.LocalPosition, retriever);
833 CVector NLPACS::CGlobalRetriever::getGlobalPosition(const UGlobalPosition &global) const
835 if (global.InstanceId >= 0)
837 return _Instances[global.InstanceId].getGlobalPosition(global.LocalPosition.Estimation);
839 else
841 // it should be an error here
842 return global.LocalPosition.Estimation;
846 CVectorD NLPACS::CGlobalRetriever::getDoubleGlobalPosition(const NLPACS::UGlobalPosition &global) const
848 if (global.InstanceId >= 0)
850 return _Instances[global.InstanceId].getDoubleGlobalPosition(global.LocalPosition.Estimation);
852 else
854 // it should be an error here
855 return CVectorD(global.LocalPosition.Estimation);
861 void NLPACS::CGlobalRetriever::findAStarPath(const NLPACS::UGlobalPosition &begin,
862 const NLPACS::UGlobalPosition &end,
863 vector<NLPACS::CRetrieverInstance::CAStarNodeAccess> &path,
864 uint32 forbidFlags) const
866 TTicks astarStart;
867 ThisAStarTicks = 0;
868 astarStart = CTime::getPerformanceTime();
870 // open and close lists
871 // TODO: Use a smart allocator to avoid huge alloc/free and memory fragmentation
872 // open is a priority queue (implemented as a stl multimap)
873 multimap<float, CRetrieverInstance::CAStarNodeAccess> open;
874 // close is a simple stl vector
875 vector<CRetrieverInstance::CAStarNodeAccess> close;
877 // inits start node and info
878 CRetrieverInstance::CAStarNodeAccess beginNode;
879 beginNode.InstanceId = begin.InstanceId;
880 beginNode.NodeId = (uint16)begin.LocalPosition.Surface;
881 CRetrieverInstance::CAStarNodeInfo &beginInfo = getNode(beginNode);
883 // inits end node and info.
884 CRetrieverInstance::CAStarNodeAccess endNode;
885 endNode.InstanceId = end.InstanceId;
886 endNode.NodeId = (uint16)end.LocalPosition.Surface;
887 CRetrieverInstance::CAStarNodeInfo &endInfo = getNode(endNode);
889 // set up first node...
890 CRetrieverInstance::CAStarNodeAccess node = beginNode;
891 beginInfo.Parent.InstanceId = -1;
892 beginInfo.Parent.NodeId = 0;
893 beginInfo.Parent.ThroughChain = 0;
894 beginInfo.Cost = 0;
895 beginInfo.F = (endInfo.Position-beginInfo.Position).norm();
897 // ... and inserts it in the open list.
898 open.insert(make_pair(beginInfo.F, node));
900 // TO DO: use a CVector2f instead
901 CVector2f endPosition = CVector2f(getGlobalPosition(end));
903 uint i;
905 path.clear();
907 for(;;)
909 if (open.empty())
911 // couldn't find a path
912 return;
915 multimap<float, CRetrieverInstance::CAStarNodeAccess>::iterator it;
917 it = open.begin();
918 node = it->second;
919 open.erase(it);
921 if (node == endNode)
923 // found a path
924 CRetrieverInstance::CAStarNodeAccess pathNode = node;
925 uint numNodes = 0;
926 while (pathNode.InstanceId != -1)
928 ++numNodes;
929 CRetrieverInstance &instance = _Instances[pathNode.InstanceId];
930 CRetrieverInstance::CAStarNodeInfo &pathInfo = instance._NodesInformation[pathNode.NodeId];
931 pathNode = pathInfo.Parent;
934 path.resize(numNodes);
935 pathNode = node;
936 while (pathNode.InstanceId != -1)
938 path[--numNodes] = pathNode;
939 CRetrieverInstance &instance = _Instances[pathNode.InstanceId];
940 CRetrieverInstance::CAStarNodeInfo &pathInfo = instance._NodesInformation[pathNode.NodeId];
941 pathNode = pathInfo.Parent;
944 ThisAStarTicks += (CTime::getPerformanceTime()-astarStart);
946 nlinfo("found a path");
947 for (i=0; i<path.size(); ++i)
949 CRetrieverInstance &instance = _Instances[path[i].InstanceId];
950 const CLocalRetriever &retriever = _RetrieverBank->getRetriever(instance.getRetrieverId());
951 nlinfo("pathNode %d = (Inst=%d, Node=%d, Through=%d)", i, path[i].InstanceId, path[i].NodeId, path[i].ThroughChain);
952 if (path[i].ThroughChain != 0xffff)
954 const CChain &chain = retriever.getChain(path[i].ThroughChain);
955 nlinfo(" chain: left=%d right=%d", chain.getLeft(), chain.getRight());
956 if (CChain::isBorderChainId(chain.getRight()))
958 CRetrieverInstance::CLink lnk = instance.getBorderChainLink(CChain::convertBorderChainId(chain.getRight()));
959 sint instanceid = lnk.Instance;
960 sint id = lnk.SurfaceId;
961 nlinfo(" right: instance=%d surf=%d", instanceid, id);
965 nlinfo("open.size()=%d", open.size());
966 nlinfo("close.size()=%d", close.size());
968 return;
971 // push successors of the current node
972 CRetrieverInstance &inst = _Instances[node.InstanceId];
973 const CLocalRetriever &retriever = _RetrieverBank->getRetriever(inst.getRetrieverId());
974 const CRetrievableSurface &surf = retriever.getSurface(node.NodeId);
975 const vector<CRetrievableSurface::CSurfaceLink> &chains = surf.getChains();
977 CRetrieverInstance *nextInstance;
978 const CLocalRetriever *nextRetriever;
979 const CRetrievableSurface *nextSurface;
981 nlinfo("examine node (instance=%d,surf=%d,cost=%g)", node.InstanceId, node.NodeId, inst._NodesInformation[node.NodeId].Cost);
983 for (i=0; i<chains.size(); ++i)
985 sint32 nextNodeId = chains[i].Surface;
986 CRetrieverInstance::CAStarNodeAccess nextNode;
988 if (CChain::isBorderChainId(nextNodeId))
990 // if the chain points to another retriever
992 // first get the edge on the retriever
993 CRetrieverInstance::CLink lnk = inst.getBorderChainLink(CChain::convertBorderChainId(nextNodeId));
994 nextNode.InstanceId = lnk.Instance;
996 if (nextNode.InstanceId < 0)
997 continue;
999 nextInstance = &_Instances[nextNode.InstanceId];
1000 nextRetriever = &(_RetrieverBank->getRetriever(nextInstance->getRetrieverId()));
1002 sint nodeId = lnk.SurfaceId;
1003 nlassert(nodeId >= 0);
1004 nextNode.NodeId = (uint16)nodeId;
1006 else if (nextNodeId >= 0)
1008 // if the chain points to the same instance
1009 nextNode.InstanceId = node.InstanceId;
1010 nextNode.NodeId = (uint16) nextNodeId;
1011 nextInstance = &inst;
1012 nextRetriever = &retriever;
1014 else
1016 // if the chain cannot be crossed
1017 continue;
1020 nextSurface = &(nextRetriever->getSurface(nextNode.NodeId));
1022 if (nextSurface->getFlags() & forbidFlags)
1023 continue;
1025 // compute new node value (heuristic and cost)
1027 CRetrieverInstance::CAStarNodeInfo &nextInfo = nextInstance->_NodesInformation[nextNode.NodeId];
1028 float stepCost = (nextInfo.Position-inst._NodesInformation[node.NodeId].Position).norm();
1029 float nextCost = inst._NodesInformation[node.NodeId].Cost+stepCost;
1030 float nextHeuristic = (nextInfo.Position-endPosition).norm();
1031 float nextF = nextCost+nextHeuristic;
1033 vector<CRetrieverInstance::CAStarNodeAccess>::iterator closeIt;
1034 for (closeIt=close.begin(); closeIt!=close.end() && *closeIt!=nextNode; ++closeIt)
1037 if (closeIt != close.end() && nextInfo.F < nextF)
1038 continue;
1040 multimap<float, CRetrieverInstance::CAStarNodeAccess>::iterator openIt;
1041 for (openIt=open.begin(); openIt!=open.end() && openIt->second!=nextNode; ++openIt)
1044 if (openIt != open.end() && nextInfo.F < nextF)
1045 continue;
1047 if (openIt != open.end())
1048 open.erase(openIt);
1050 if (closeIt != close.end())
1051 close.erase(closeIt);
1053 nextInfo.Parent = node;
1054 nextInfo.Parent.ThroughChain = (uint16)(chains[i].Chain);
1055 nextInfo.Cost = nextCost;
1056 nextInfo.F = nextF;
1058 nlinfo(" adding node (instance=%d,surf=%d) f=%g, through=%d", nextNode.InstanceId, nextNode.NodeId, nextInfo.F, i);
1060 open.insert(make_pair(nextInfo.F, nextNode));
1062 close.push_back(node);
1068 void NLPACS::CGlobalRetriever::findPath(const NLPACS::UGlobalPosition &begin,
1069 const NLPACS::UGlobalPosition &end,
1070 NLPACS::CGlobalRetriever::CGlobalPath &path,
1071 uint32 forbidFlags) const
1074 vector<CRetrieverInstance::CAStarNodeAccess> astarPath;
1075 findAStarPath(begin, end, astarPath, forbidFlags);
1077 TTicks surfStart;
1078 TTicks chainStart;
1080 ThisChainTicks = 0;
1081 ThisSurfTicks = 0;
1082 ThisPathTicks = 0;
1084 path.clear();
1085 path.resize(astarPath.size());
1087 uint i, j;
1088 for (i=0; i<astarPath.size(); ++i)
1090 chainStart = CTime::getPerformanceTime();
1091 CLocalPath &surf = path[i];
1092 surf.InstanceId = astarPath[i].InstanceId;
1093 const CLocalRetriever &retriever = _RetrieverBank->getRetriever(_Instances[surf.InstanceId].getRetrieverId());
1095 // computes start point
1096 if (i == 0)
1098 // if it is the first point, just copy the begin
1099 surf.Start.ULocalPosition::operator= (begin.LocalPosition);
1101 else
1103 // else, take the previous value and convert it in the current instance axis
1104 // TODO: avoid this if the instances are the same
1105 CVector prev = _Instances[path[i-1].InstanceId].getGlobalPosition(path[i-1].End.Estimation);
1106 CVector current = _Instances[surf.InstanceId].getLocalPosition(prev);
1107 surf.Start.Surface = astarPath[i].NodeId;
1108 surf.Start.Estimation = current;
1111 // computes end point
1112 if (i == astarPath.size()-1)
1114 surf.End.ULocalPosition::operator= (end.LocalPosition);
1116 else
1118 // get to the middle of the chain
1119 // first get the chain between the 2 surfaces
1120 const CChain &chain = retriever.getChain(astarPath[i].ThroughChain);
1121 float cumulLength = 0.0f, midLength=chain.getLength()*0.5f;
1122 for (j=0; j<chain.getSubChains().size() && cumulLength<=midLength; ++j)
1123 cumulLength += retriever.getOrderedChain(chain.getSubChain(j)).getLength();
1124 --j;
1125 const COrderedChain &ochain = retriever.getOrderedChain(chain.getSubChain(j));
1126 surf.End.Surface = astarPath[i].NodeId;
1128 if (ochain.getVertices().size() & 1)
1130 surf.End.Estimation = ochain[(uint)ochain.getVertices().size()/2].unpack3f();
1132 else
1134 surf.End.Estimation = (ochain[(uint)ochain.getVertices().size()/2].unpack3f()+
1135 ochain[(uint)ochain.getVertices().size()/2-1].unpack3f())*0.5f;
1139 ThisChainTicks += (CTime::getPerformanceTime()-chainStart);
1141 surfStart = CTime::getPerformanceTime();
1142 retriever.findPath(surf.Start, surf.End, surf.Path, _InternalCST);
1143 ThisSurfTicks += (CTime::getPerformanceTime()-surfStart);
1146 ThisPathTicks = ThisAStarTicks+ThisChainTicks+ThisSurfTicks;
1147 PathTicks += ThisPathTicks;
1148 SurfTicks += ThisSurfTicks;
1149 AStarTicks += ThisAStarTicks;
1150 ChainTicks += ThisChainTicks;
1154 // ***************************************************************************
1156 // ***************************************************************************
1157 // ***************************************************************************
1158 // Collisions part.
1159 // ***************************************************************************
1160 // ***************************************************************************
1164 // ***************************************************************************
1165 const NLPACS::CRetrievableSurface *NLPACS::CGlobalRetriever::getSurfaceById(const NLPACS::CSurfaceIdent &surfId) const
1167 if(surfId.RetrieverInstanceId>=0 && surfId.SurfaceId>=0)
1169 sint32 locRetId= this->getInstance(surfId.RetrieverInstanceId).getRetrieverId();
1170 const CLocalRetriever &retr = _RetrieverBank->getRetriever(locRetId);
1171 if (!retr.isLoaded() || surfId.SurfaceId >= (sint)retr.getSurfaces().size())
1172 return NULL;
1173 const CRetrievableSurface &surf= retr.getSurface(surfId.SurfaceId);
1174 return &surf;
1176 else
1177 return NULL;
1182 // ***************************************************************************
1183 void NLPACS::CGlobalRetriever::findCollisionChains(CCollisionSurfaceTemp &cst, const NLMISC::CAABBox &bboxMove, const NLMISC::CVector &origin) const
1185 // H_AUTO(PACS_GR_findCollisionChains);
1187 sint i,j;
1189 // 0. reset.
1190 //===========
1191 // reset possible chains.
1192 // H_BEFORE(PACS_GR_findCC_reset);
1193 cst.CollisionChains.clear();
1194 cst.resetEdgeCollideNodes();
1195 // H_AFTER(PACS_GR_findCC_reset);
1197 // 1. Find Instances which may hit this movement.
1198 //===========
1199 // H_BEFORE(PACS_GR_findCC_selectInstances);
1200 CAABBox bboxMoveGlobal= bboxMove;
1201 bboxMoveGlobal.setCenter(bboxMoveGlobal.getCenter()+origin);
1202 selectInstances(bboxMoveGlobal, cst);
1203 // H_AFTER(PACS_GR_findCC_selectInstances);
1205 // 2. Fill CollisionChains.
1206 //===========
1207 // For each possible surface mesh, test collision.
1208 for(i=0 ; i<(sint)cst.CollisionInstances.size(); i++)
1210 // H_BEFORE(PACS_GR_findCC_getAndComputeMove);
1211 // get retrieverInstance.
1212 sint32 curInstance= cst.CollisionInstances[i];
1213 const CRetrieverInstance &retrieverInstance= getInstance(curInstance);
1215 // Retrieve the localRetriever of this instance.
1216 sint32 localRetrieverId= retrieverInstance.getRetrieverId();
1217 // If invalid one (hole), continue.
1218 if(localRetrieverId<0)
1219 continue;
1220 const CLocalRetriever &localRetriever= _RetrieverBank->getRetriever(localRetrieverId);
1222 if (!localRetriever.isLoaded())
1224 nldebug("local retriever %d in %s not loaded, findCollisionChains in this retriever aborted", localRetrieverId, _RetrieverBank->getNamePrefix().c_str());
1225 continue;
1228 // get delta between startPos.instance and curInstance.
1229 CVector deltaOrigin;
1230 deltaOrigin= origin - retrieverInstance.getOrigin();
1232 // compute movement relative to this localRetriever.
1233 CAABBox bboxMoveLocal= bboxMove;
1234 bboxMoveLocal.setCenter(bboxMoveLocal.getCenter()+deltaOrigin);
1236 // add possible collision chains with movement.
1237 //================
1238 sint firstCollisionChain= (sint)cst.CollisionChains.size();
1239 CVector2f transBase(-deltaOrigin.x, -deltaOrigin.y);
1240 // H_AFTER(PACS_GR_findCC_getAndComputeMove);
1242 // H_BEFORE(PACS_GR_findCC_testCollision);
1243 // Go! fill collision chains that this movement intersect.
1244 localRetriever.testCollision(cst, bboxMoveLocal, transBase);
1245 // if an interior, also test for external collisions
1246 if (retrieverInstance.getType() == CLocalRetriever::Interior)
1247 retrieverInstance.testExteriorCollision(cst, bboxMoveLocal, transBase, localRetriever);
1249 // how many collision chains added? : nCollisionChain-firstCollisionChain.
1250 sint nCollisionChain= (sint)cst.CollisionChains.size();
1251 // H_AFTER(PACS_GR_findCC_testCollision);
1254 // For all collision chains added, fill good SurfaceIdent info.
1255 //================
1256 // H_BEFORE(PACS_GR_findCC_fillSurfIdent);
1257 for(j=firstCollisionChain; j<nCollisionChain; j++)
1259 CCollisionChain &cc= cst.CollisionChains[j];
1261 // info are already filled for exterior chains.
1262 if (cc.ExteriorEdge)
1263 continue;
1265 // LeftSurface retrieverInstance is always curInstance.
1266 cc.LeftSurface.RetrieverInstanceId= curInstance;
1268 // If RightSurface is not an "edgeId" ie a pointer on a neighbor surface on another retrieverInstance.
1269 const CChain &originalChain= localRetriever.getChain(cc.ChainId);
1270 if( !originalChain.isBorderChainId(cc.RightSurface.SurfaceId) )
1272 cc.RightSurface.RetrieverInstanceId= curInstance;
1274 else
1276 // we must find the surfaceIdent of the neighbor.
1278 CRetrieverInstance::CLink link;
1279 // get the link to the next surface from the instance
1280 link = retrieverInstance.getBorderChainLink(CChain::convertBorderChainId(cc.RightSurface.SurfaceId));
1282 // get the neighbor instanceId.
1283 sint neighborInstanceId= (sint16)link.Instance;
1284 // store in the current collisionChain Right.
1285 cc.RightSurface.RetrieverInstanceId= neighborInstanceId;
1287 // If no instance near us, this is a WALL.
1288 if(neighborInstanceId<0)
1290 // mark as a Wall.
1291 cc.RightSurface.SurfaceId= -1;
1293 else
1295 // Get the good neighbor surfaceId.
1296 cc.RightSurface.SurfaceId= (sint16)link.SurfaceId;
1300 nlassert(cc.LeftSurface.RetrieverInstanceId < (sint)_Instances.size());
1301 nlassert(cc.RightSurface.RetrieverInstanceId < (sint)_Instances.size());
1303 // H_AFTER(PACS_GR_findCC_fillSurfIdent);
1306 // For all collision chains added, look if they are a copy of preceding collsion chain (same Left/Right). Then delete them.
1307 //================
1308 // H_BEFORE(PACS_GR_findCC_removeDouble);
1309 for(j=firstCollisionChain; j<nCollisionChain; j++)
1311 const CCollisionChain &cj = cst.CollisionChains[j];
1313 if (cj.ExteriorEdge && cj.LeftSurface.RetrieverInstanceId!=-1)
1314 continue;
1316 // test for all collisionChain inserted before.
1317 for(sint k=0; k<firstCollisionChain; k++)
1319 const CCollisionChain &ck = cst.CollisionChains[k];
1321 if (cj.LeftSurface.RetrieverInstanceId != cj.RightSurface.RetrieverInstanceId &&
1322 cj.LeftSurface == ck.RightSurface && cj.RightSurface == ck.LeftSurface)
1324 const CRetrieverInstance &instj = getInstance(cj.LeftSurface.RetrieverInstanceId),
1325 &instk = getInstance(ck.LeftSurface.RetrieverInstanceId);
1326 const CLocalRetriever &retrj = getRetriever(instj.getRetrieverId()),
1327 &retrk = getRetriever(instk.getRetrieverId());
1329 if (!retrj.isLoaded() || !retrk.isLoaded())
1331 nlwarning("using not loaded retriever %d or %d in bank '%s', aborted", instj.getRetrieverId(), instk.getRetrieverId(), _RetrieverBank->getNamePrefix().c_str());
1332 continue;
1335 nlassert(retrj.getChain(cj.ChainId).isBorderChain() && retrk.getChain(ck.ChainId).isBorderChain());
1337 if (instj.getBorderChainLink(retrj.getChain(cj.ChainId).getBorderChainIndex()).ChainId != ck.ChainId ||
1338 instk.getBorderChainLink(retrk.getChain(ck.ChainId).getBorderChainIndex()).ChainId != cj.ChainId)
1340 continue;
1343 // remove this jth entry.
1344 // by swapping with last entry. Only if not already last.
1345 if(j<nCollisionChain-1)
1347 swap(cst.CollisionChains[j], cst.CollisionChains[nCollisionChain-1]);
1348 // NB: some holes remain in cst._EdgeCollideNodes, but do not matters since reseted at
1349 // each collision test.
1352 // pop last entry.
1353 nCollisionChain--;
1354 cst.CollisionChains.resize(nCollisionChain);
1356 // next entry??
1357 j--;
1358 break;
1361 // if same surface Ident Left/Right==Left/Right or swapped Left/Right==Right/Left
1362 if( cst.CollisionChains[j].sameSurfacesThan(cst.CollisionChains[k]) )
1364 // remove this jth entry.
1365 // by swapping with last entry. Only if not already last.
1366 if(j<nCollisionChain-1)
1368 swap(cst.CollisionChains[j], cst.CollisionChains[nCollisionChain-1]);
1369 // NB: some holes remain in cst._EdgeCollideNodes, but do not matters since reseted at
1370 // each collision test.
1373 // pop last entry.
1374 nCollisionChain--;
1375 cst.CollisionChains.resize(nCollisionChain);
1377 // next entry??
1378 j--;
1379 break;
1385 // H_AFTER(PACS_GR_findCC_removeDouble);
1391 // ***************************************************************************
1392 void NLPACS::CGlobalRetriever::testCollisionWithCollisionChains(CCollisionSurfaceTemp &cst, const CVector2f &startCol, const CVector2f &deltaCol,
1393 CSurfaceIdent startSurface, float radius, const CVector2f bboxStart[4], TCollisionType colType) const
1395 // H_AUTO(PACS_GR_testCollisionWithCollisionChains);
1397 // start currentSurface with surface start.
1398 CSurfaceIdent currentSurface= startSurface;
1399 uint nextCollisionSurfaceTested=0;
1400 sint i;
1402 // reset result.
1403 cst.CollisionDescs.clear();
1404 // reset all collisionChain to not tested.
1405 for(i=0; i<(sint)cst.CollisionChains.size(); i++)
1407 CCollisionChain &colChain= cst.CollisionChains[i];
1408 colChain.Tested= false;
1411 vector<pair<sint32, bool> > checkedExtEdges;
1415 To manage recovery, we must use such an algorithm, so we are sure to trace the way across all surfaces really
1416 collided, and discard any other (such as other floor or ceiling).
1418 for(;;)
1420 // run all collisionChain.
1421 //========================
1422 for(i=0; i<(sint)cst.CollisionChains.size(); i++)
1424 CCollisionChain &colChain= cst.CollisionChains[i];
1426 /// TODO Tests Ben
1427 nlassert(colChain.LeftSurface.RetrieverInstanceId < (sint)_Instances.size());
1428 nlassert(colChain.RightSurface.RetrieverInstanceId < (sint)_Instances.size());
1430 // test only currentSurface/X. And don't test chains already tested before.
1431 if(colChain.hasSurface(currentSurface) && !colChain.Tested)
1433 // we are testing this chain.
1434 colChain.Tested= true;
1436 // avoid checking twice a door
1437 if (colChain.ExteriorEdge && colChain.LeftSurface.RetrieverInstanceId != -1)
1439 bool enterInterior = (currentSurface.RetrieverInstanceId == colChain.RightSurface.RetrieverInstanceId);
1441 uint j;
1442 sint32 cmp = (colChain.LeftSurface.RetrieverInstanceId<<16) + colChain.ChainId;
1443 for (j=0; j<checkedExtEdges.size() && (checkedExtEdges[j].first != cmp); ++j)
1445 // if already crossed this edge, abort
1446 // this a door that is crossing a surface frontier
1447 if (j < checkedExtEdges.size())
1449 if (checkedExtEdges[j].second != enterInterior)
1450 continue;
1452 else
1453 checkedExtEdges.push_back(make_pair(cmp, enterInterior));
1456 // test all edges of this chain, and get tmin
1457 //========================
1459 float t=0.0, tMin=1;
1460 CVector2f normal, normalMin(0.0f, 0.0f);
1461 // run list of edge.
1462 sint32 curEdge= colChain.FirstEdgeCollide;
1463 while(curEdge!=(sint32)0xFFFFFFFF)
1465 // get the edge.
1466 CEdgeCollideNode &colEdge= cst.getEdgeCollideNode(curEdge);
1468 // test collision with this edge.
1469 if(colType==CGlobalRetriever::Circle)
1470 t= colEdge.testCircleMove(startCol, deltaCol, radius, normal);
1471 else if(colType==CGlobalRetriever::BBox)
1472 t= colEdge.testBBoxMove(startCol, deltaCol, bboxStart, normal);
1474 // earlier collision??
1475 if(t<tMin)
1477 tMin= t;
1478 normalMin= normal;
1481 // next edge.
1482 curEdge= colEdge.Next;
1486 // If collision with this chain, must insert it in the array of collision.
1487 //========================
1488 if(tMin<1)
1490 CSurfaceIdent collidedSurface= colChain.getOtherSurface(currentSurface);
1492 // if flag as an interior/landscape interface and leave interior surf, retrieve correct surface
1493 if (colChain.ExteriorEdge && currentSurface == colChain.LeftSurface)
1495 // p= position until the bounding object collide the exterior edge
1496 CVector2f p = startCol + deltaCol*tMin;
1497 // get the interior origin
1498 CVector ori = getInstance(startSurface.RetrieverInstanceId).getOrigin();
1499 ori.z = 0.0f;
1501 // Estimate current Z
1502 UGlobalPosition rp;
1503 rp.InstanceId = currentSurface.RetrieverInstanceId;
1504 rp.LocalPosition.Surface = currentSurface.SurfaceId;
1505 rp.LocalPosition.Estimation = p;
1506 // NB: getMeanHeight() should work here since we are still deep in the interior surface (edge collided with bounding volume)
1507 float estimatedZ= getMeanHeight(rp);
1509 // retrieve the position, with the estimated Z
1510 CVectorD zp = CVectorD(p.x, p.y, estimatedZ) + CVectorD(ori);
1511 // Do not allow the current interior instance
1512 _ForbiddenInstances.clear();
1513 _ForbiddenInstances.push_back(currentSurface.RetrieverInstanceId);
1514 UGlobalPosition gp = retrievePosition(zp);
1516 collidedSurface.RetrieverInstanceId = gp.InstanceId;
1517 collidedSurface.SurfaceId = gp.LocalPosition.Surface;
1520 /// TODO Tests Ben
1521 nlassert(collidedSurface.RetrieverInstanceId < (sint)_Instances.size());
1523 // insert or replace this collision in collisionDescs.
1524 // NB: yes this looks like a N algorithm (so N^2). But not so many collisions may arise, so don't bother.
1525 sint indexInsert= (sint)cst.CollisionDescs.size();
1526 sint colFound= -1;
1528 // start to search with nextCollisionSurfaceTested, because can't insert before.
1529 for(sint j= nextCollisionSurfaceTested; j<(sint)cst.CollisionDescs.size(); j++)
1531 // we must keep time order.
1532 if(tMin < cst.CollisionDescs[j].ContactTime)
1534 indexInsert= min(j, indexInsert);
1536 // Does the collision with this surface already exist??
1537 if(cst.CollisionDescs[j].ContactSurface==collidedSurface)
1539 colFound= j;
1540 // if we have found our old collision, stop, there is no need to search more.
1541 break;
1545 // Insert only if the surface was not already collided, or that new collision arise before old.
1546 if(colFound==-1 || indexInsert<=colFound)
1548 CCollisionSurfaceDesc newCol;
1549 newCol.ContactSurface= collidedSurface;
1550 newCol.ContactTime= tMin;
1551 newCol.ContactNormal.set(normalMin.x, normalMin.y, 0);
1553 // if, by chance, indexInsert==colFound, just replace old collision descriptor.
1554 if(colFound==indexInsert)
1556 cst.CollisionDescs[indexInsert]= newCol;
1558 else
1560 // if any, erase old collision against this surface. NB: here, colFound>indexInsert.
1561 if(colFound!=-1)
1562 cst.CollisionDescs.erase(cst.CollisionDescs.begin() + colFound);
1564 // must insert the collision.
1565 cst.CollisionDescs.insert(cst.CollisionDescs.begin() + indexInsert, newCol);
1572 // Find next surface to test.
1573 //========================
1574 // No more?? so this is the end.
1575 if(nextCollisionSurfaceTested>=cst.CollisionDescs.size())
1576 break;
1577 // else next one.
1578 else
1580 // NB: with this algorithm, we are sure that no more collisions will arise before currentCollisionSurfaceTested.
1581 // so just continue with following surface.
1582 currentSurface= cst.CollisionDescs[nextCollisionSurfaceTested].ContactSurface;
1584 // Do we touch a wall??
1585 bool isWall;
1586 if(currentSurface.SurfaceId<0)
1587 isWall= true;
1588 else
1590 // test if it is a walkable wall.
1591 sint32 locRetId= this->getInstance(currentSurface.RetrieverInstanceId).getRetrieverId();
1593 if (!_RetrieverBank->getRetriever(locRetId).isLoaded())
1595 nextCollisionSurfaceTested++;
1596 continue;
1599 const CLocalRetriever &retr = _RetrieverBank->getRetriever(locRetId);
1600 if (currentSurface.SurfaceId < (sint)retr.getSurfaces().size())
1602 const CRetrievableSurface &surf= _RetrieverBank->getRetriever(locRetId).getSurface(currentSurface.SurfaceId);
1603 isWall= !(surf.isFloor() || surf.isCeiling());
1605 else
1607 isWall = true;
1611 // If we touch a wall, this is the end of search.
1612 if(isWall)
1614 // There can be no more collision after this one.
1615 cst.CollisionDescs.resize(nextCollisionSurfaceTested+1);
1616 break;
1618 else
1620 // Next time, we will test the following (NB: the array may grow during next pass, or reorder,
1621 // but only after nextCollisionSurfaceTested).
1622 nextCollisionSurfaceTested++;
1630 // ***************************************************************************
1631 bool NLPACS::CGlobalRetriever::verticalChain(const CCollisionChain &colChain) const
1633 // retrieve surfaces.
1634 const CRetrievableSurface *left= getSurfaceById(colChain.LeftSurface);
1635 const CRetrievableSurface *right= getSurfaceById(colChain.RightSurface);
1637 // test if left surface is a wall.
1638 bool leftWall;
1639 if(!left)
1640 leftWall= true;
1641 else
1642 leftWall= !(left->isFloor() || left->isCeiling());
1644 // test if right surface is a wall.
1645 bool rightWall;
1646 if(!right)
1647 rightWall= true;
1648 else
1649 rightWall= !(right->isFloor() || right->isCeiling());
1651 // true if both are a wall.
1652 return leftWall && rightWall;
1656 // ***************************************************************************
1657 NLPACS::CSurfaceIdent NLPACS::CGlobalRetriever::testMovementWithCollisionChains(CCollisionSurfaceTemp &cst, const CVector2f &startCol, const CVector2f &endCol,
1658 CSurfaceIdent startSurface, UGlobalPosition &restart) const
1660 // H_AUTO(PACS_GR_testMovementWithCollisionChains);
1662 // start currentSurface with surface start.
1663 CSurfaceIdent currentSurface= startSurface;
1664 sint i;
1666 // reset result.
1667 cst.MoveDescs.clear();
1670 static vector<pair<sint32, bool> > checkedExtEdges;
1673 To manage recovery, we must use such an algorithm, so we are sure to trace the way across all surfaces really
1674 collided, and discard any other (such as other floor or ceiling).
1676 This function is quite different from testCollisionWithCollisionChains() because she must detect all collisions
1677 with all edges of any chains (and not the minimum collision with a chain).
1678 This is done in 3 parts:
1679 - detect collisions with all edges.
1680 - sort.
1681 - leave only real collisions.
1683 // run all collisionChain.
1684 //========================
1685 for(i=0; i<(sint)cst.CollisionChains.size(); i++)
1687 CCollisionChain &colChain= cst.CollisionChains[i];
1689 if (colChain.ExteriorEdge)
1691 sint32 cmp = (colChain.LeftSurface.RetrieverInstanceId<<16) + colChain.ChainId;
1693 uint j;
1694 for (j=0; j<checkedExtEdges.size() && (checkedExtEdges[j].first != cmp); ++j)
1696 // if already crossed this edge, abort
1697 // this a door that is crossing a surface frontier
1698 if (j < checkedExtEdges.size())
1699 continue;
1702 // test all edges of this chain, and insert if necessary.
1703 //========================
1704 CRational64 t;
1705 // run list of edge.
1706 sint32 curEdge= colChain.FirstEdgeCollide;
1707 while(curEdge!=(sint32)0xFFFFFFFF)
1709 // get the edge.
1710 CEdgeCollideNode &colEdge= cst.getEdgeCollideNode(curEdge);
1712 // test collision with this edge.
1713 CEdgeCollide::TPointMoveProblem pmpb;
1714 t= colEdge.testPointMove(startCol, endCol, pmpb);
1715 // manage multiple problems of precision.
1716 if(t== -1)
1718 static const string errs[CEdgeCollide::PointMoveProblemCount]= {
1719 "ParallelEdges", "StartOnEdge", "StopOnEdge", "TraverseEndPoint", "EdgeNull"};
1720 // return a "Precision Problem" ident. movement is invalid.
1721 // BUT if startOnEdge, which should never arrive.
1722 if(pmpb==CEdgeCollide::StartOnEdge)
1724 nlinfo("COL: Precision Problem: %s", errs[pmpb].c_str());
1725 checkedExtEdges.clear();
1726 return CSurfaceIdent(-1, -1); // so in this case, block....
1728 else if(pmpb==CEdgeCollide::EdgeNull)
1731 // verify if it is an edge which separate 2 walls. in this case, ignore it. else, error.
1732 if(verticalChain(colChain))
1734 t=1; // no collision with this edge.
1736 else
1738 nlinfo("COL: Precision Problem: %s", errs[pmpb]);
1739 nlstop; // this should not append.
1740 return CSurfaceIdent(-1, -1);
1742 /* Actually, this is never a problem: we never get through this edge.
1743 Instead, we'll get through the neighbors edge.
1744 So just disable this edge.
1746 t= 1;
1748 else
1749 return CSurfaceIdent(-2, -2);
1752 // collision??
1753 if(t<1)
1755 // insert in list.
1756 cst.MoveDescs.push_back(CMoveSurfaceDesc(t, colChain.LeftSurface, colChain.RightSurface));
1757 cst.MoveDescs.back().ExteriorEdge = colChain.ExteriorEdge;
1758 cst.MoveDescs.back().ChainId = (uint16)colChain.ChainId;
1759 cst.MoveDescs.back().MovementSens= colEdge.Norm*(endCol-startCol)>=0;
1762 // next edge.
1763 curEdge= colEdge.Next;
1768 // sort.
1769 //================
1770 // sort the collisions in ascending time order.
1771 sort(cst.MoveDescs.begin(), cst.MoveDescs.end());
1774 // Traverse the array of collisions.
1775 //========================
1776 for(i=0;i<(sint)cst.MoveDescs.size();i++)
1778 CMoveSurfaceDesc &msd = cst.MoveDescs[i];
1780 // Do we collide with this chain??
1781 if(msd.hasSurface(currentSurface))
1783 // if flag as an interior/landscape interface and leave interior surf, retrieve correct surface
1784 if (msd.ExteriorEdge && msd.LeftSurface.RetrieverInstanceId != -1)
1786 bool enterInterior = (currentSurface.RetrieverInstanceId == msd.RightSurface.RetrieverInstanceId);
1788 // msd.MovementSens is true if we "geometrically" leave the interior.
1789 // If logic and geometric disagree, discard
1790 if(enterInterior == msd.MovementSens)
1791 continue;
1793 uint j;
1794 sint32 cmp = (msd.LeftSurface.RetrieverInstanceId<<16) + msd.ChainId;
1795 for (j=0; j<checkedExtEdges.size() && (checkedExtEdges[j].first != cmp); ++j)
1797 // if already crossed this edge, abort
1798 // this a door that is crossing a surface frontier
1799 if (j < checkedExtEdges.size())
1801 if (checkedExtEdges[j].second != enterInterior)
1802 continue;
1804 else
1805 checkedExtEdges.push_back(make_pair(cmp, enterInterior));
1807 // if leave interior, retrieve good position
1808 if (!enterInterior)
1810 // p= position until the object center point collide the exterior edge
1811 float ctime = (float)((double)(msd.ContactTime.Numerator)/(double)(msd.ContactTime.Denominator));
1812 CVector2f p = startCol*(1.0f-ctime) + endCol*ctime;
1813 // get the interior origin
1814 CVector ori = getInstance(startSurface.RetrieverInstanceId).getOrigin();
1815 ori.z = 0.0f;
1817 // Estimate current Z
1818 UGlobalPosition rp;
1819 rp.InstanceId = currentSurface.RetrieverInstanceId;
1820 rp.LocalPosition.Surface = currentSurface.SurfaceId;
1821 rp.LocalPosition.Estimation = p;
1822 /* WE HAVE A PRECISION PROBLEM HERE (yoyo 12/04/2006)
1823 Since the point p has moved close to the exterior edge, because of precision, it may be actually
1824 OUT the interior surface!!
1825 thus getMeanHeight() will return 0!!
1826 Then the chosen landscape position can be completly false. eg:
1827 actual InteriorHeight: -84
1828 new possibles landscape surfaces heights: -84 and -16
1829 if we estimate by error InteriorHeight= 0, then we will
1830 have Best Landscape Surface == the one which has height=-16 !
1832 Hence we use a specific method that look a bit outisde the triangles
1834 float estimatedZ = getInteriorHeightAround(rp, 0.1f);
1836 // retrieve the position, with the estimated Z
1837 CVectorD zp = CVectorD(p.x, p.y, estimatedZ) + CVectorD(ori);
1838 // Do not allow the current interior instance
1839 _ForbiddenInstances.clear();
1840 _ForbiddenInstances.push_back(currentSurface.RetrieverInstanceId);
1841 restart = retrievePosition(zp);
1843 return CSurfaceIdent(-3, -3);
1845 else
1847 currentSurface= msd.getOtherSurface(currentSurface);
1850 else
1852 currentSurface= msd.getOtherSurface(currentSurface);
1855 // Do we touch a wall?? should not happens, but important for security.
1856 bool isWall;
1857 if(currentSurface.SurfaceId<0)
1858 isWall= true;
1859 else
1861 // test if it is a walkable wall.
1862 sint32 locRetId= this->getInstance(currentSurface.RetrieverInstanceId).getRetrieverId();
1864 if (!_RetrieverBank->getRetriever(locRetId).isLoaded())
1865 continue;
1867 const CRetrievableSurface &surf= _RetrieverBank->getRetriever(locRetId).getSurface(currentSurface.SurfaceId);
1868 isWall= !(surf.isFloor() || surf.isCeiling());
1871 // If we touch a wall, this is the end of search.
1872 if(isWall)
1874 // return a Wall ident. movement is invalid.
1875 checkedExtEdges.clear();
1876 return CSurfaceIdent(-1, -1);
1881 checkedExtEdges.clear();
1882 return currentSurface;
1887 // ***************************************************************************
1888 const NLPACS::TCollisionSurfaceDescVector
1889 *NLPACS::CGlobalRetriever::testCylinderMove(const UGlobalPosition &startPos, const NLMISC::CVector &delta, float radius, CCollisionSurfaceTemp &cst) const
1891 // H_AUTO(PACS_GR_testCylinderMove);
1893 CSurfaceIdent startSurface(startPos.InstanceId, startPos.LocalPosition.Surface);
1895 // 0. reset.
1896 //===========
1897 // reset result.
1898 cst.CollisionDescs.clear();
1900 // In a surface ?
1901 if (startPos.InstanceId==-1)
1903 // Warning this primitive is not on a surface
1904 //nlassertonce (0);
1906 // Return NULL when lost
1907 return NULL;
1909 // store this request in cst.
1910 cst.PrecStartSurface= startSurface;
1911 cst.PrecStartPos= startPos.LocalPosition.Estimation;
1912 cst.PrecDeltaPos= delta;
1913 cst.PrecValid= true;
1915 // 0.bis
1916 //===========
1917 // Abort if deltamove is 0,0,0.
1918 if (delta.isNull())
1919 return &cst.CollisionDescs;
1921 // 1. Choose a local basis.
1922 //===========
1923 // Take the retrieverInstance of startPos as a local basis.
1924 CVector origin;
1925 origin= getInstance(startPos.InstanceId).getOrigin();
1928 // 2. compute bboxmove.
1929 //===========
1930 CAABBox bboxMove;
1931 // bounds the movement in a bbox.
1932 // compute start and end, relative to the retriever instance.
1933 CVector start= startPos.LocalPosition.Estimation;
1934 CVector end= start+delta;
1935 // extend the bbox.
1936 bboxMove.setCenter(start-CVector(radius, radius, 0));
1937 bboxMove.extend(start+CVector(radius, radius, 0));
1938 bboxMove.extend(end-CVector(radius, radius, 0));
1939 bboxMove.extend(end+CVector(radius, radius, 0));
1942 // 3. find possible collisions in bboxMove+origin. fill cst.CollisionChains.
1943 //===========
1944 findCollisionChains(cst, bboxMove, origin);
1948 // 4. test collisions with CollisionChains.
1949 //===========
1950 CVector2f startCol(start.x, start.y);
1951 CVector2f deltaCol(delta.x, delta.y);
1952 CVector2f obbDummy[4]; // dummy OBB (not obb here so don't bother)
1953 testCollisionWithCollisionChains(cst, startCol, deltaCol, startSurface, radius, obbDummy, CGlobalRetriever::Circle);
1955 // result.
1956 return &cst.CollisionDescs;
1960 // ***************************************************************************
1961 const NLPACS::TCollisionSurfaceDescVector
1962 *NLPACS::CGlobalRetriever::testBBoxMove(const UGlobalPosition &startPos, const NLMISC::CVector &delta,
1963 const NLMISC::CVector &locI, const NLMISC::CVector &locJ, CCollisionSurfaceTemp &cst) const
1965 // H_AUTO(PACS_GR_testBBoxMove);
1967 CSurfaceIdent startSurface(startPos.InstanceId, startPos.LocalPosition.Surface);
1969 // 0. reset.
1970 //===========
1971 // reset result.
1972 cst.CollisionDescs.clear();
1974 // In a surface ?
1975 if (startPos.InstanceId==-1)
1977 // Warning this primitive is not on a surface
1978 //nlassertonce (0);
1980 // Return NULL when lost
1981 return NULL;
1984 // store this request in cst.
1985 cst.PrecStartSurface= startSurface;
1986 cst.PrecStartPos= startPos.LocalPosition.Estimation;
1987 cst.PrecDeltaPos= delta;
1988 cst.PrecValid= true;
1990 // 0.bis
1991 //===========
1992 // Abort if deltamove is 0,0,0.
1993 if (delta.isNull())
1994 return &cst.CollisionDescs;
1996 // 1. Choose a local basis.
1997 //===========
1998 // Take the retrieverInstance of startPos as a local basis.
1999 CVector origin;
2000 origin= getInstance(startPos.InstanceId).getOrigin();
2003 // 2. compute OBB.
2004 //===========
2005 CVector2f obbStart[4];
2006 // compute start, relative to the retriever instance.
2007 CVector start= startPos.LocalPosition.Estimation;
2008 CVector2f obbCenter(start.x, start.y);
2009 CVector2f locI2d(locI.x, locI.y);
2010 CVector2f locJ2d(locJ.x, locJ.y);
2012 // build points in CCW.
2013 obbStart[0]= obbCenter - locI2d - locJ2d;
2014 obbStart[1]= obbCenter + locI2d - locJ2d;
2015 obbStart[2]= obbCenter + locI2d + locJ2d;
2016 obbStart[3]= obbCenter - locI2d + locJ2d;
2018 // 3. compute bboxmove.
2019 //===========
2020 CAABBox bboxMove;
2021 // extend the bbox.
2022 bboxMove.setCenter(CVector(obbStart[0].x, obbStart[0].y, 0));
2023 bboxMove.extend(CVector(obbStart[1].x, obbStart[1].y, 0));
2024 bboxMove.extend(CVector(obbStart[2].x, obbStart[2].y, 0));
2025 bboxMove.extend(CVector(obbStart[3].x, obbStart[3].y, 0));
2026 bboxMove.extend(CVector(obbStart[0].x, obbStart[0].y, 0) + delta);
2027 bboxMove.extend(CVector(obbStart[1].x, obbStart[1].y, 0) + delta);
2028 bboxMove.extend(CVector(obbStart[2].x, obbStart[2].y, 0) + delta);
2029 bboxMove.extend(CVector(obbStart[3].x, obbStart[3].y, 0) + delta);
2033 // 4. find possible collisions in bboxMove+origin. fill cst.CollisionChains.
2034 //===========
2035 findCollisionChains(cst, bboxMove, origin);
2039 // 5. test collisions with CollisionChains.
2040 //===========
2041 CVector2f startCol(start.x, start.y);
2042 CVector2f deltaCol(delta.x, delta.y);
2043 testCollisionWithCollisionChains(cst, startCol, deltaCol, startSurface, 0, obbStart, CGlobalRetriever::BBox);
2045 // result.
2046 return &cst.CollisionDescs;
2051 // ***************************************************************************
2052 NLPACS::UGlobalPosition
2053 NLPACS::CGlobalRetriever::doMove(const NLPACS::UGlobalPosition &startPos, const NLMISC::CVector &delta, float t, NLPACS::CCollisionSurfaceTemp &cst, bool rebuildChains) const
2055 // H_AUTO(PACS_GR_doMove);
2057 CSurfaceIdent startSurface(startPos.InstanceId, startPos.LocalPosition.Surface);
2059 // clamp factor.
2060 clamp(t, 0.0f, 1.0f);
2062 // 0. reset.
2063 //===========
2064 // reset CollisionDescs.
2065 cst.CollisionDescs.clear();
2067 // In a surface ?
2068 if (startPos.InstanceId==-1)
2070 // Warining: this primitive is not on a surface
2071 //nlassertonce (0);
2073 // Return startpos
2074 return startPos;
2077 if(!rebuildChains)
2079 // same move request than prec testMove() ??.
2080 if( cst.PrecStartSurface != startSurface ||
2081 cst.PrecStartPos!=startPos.LocalPosition.Estimation ||
2082 cst.PrecDeltaPos!=delta ||
2083 !cst.PrecValid)
2085 // if not, just return start.
2086 //nlstop;
2087 //nlwarning ("BEN: you must fix this, it s happen!!!");
2088 return startPos;
2090 // Since we are sure we have same movement than prec testMove(), no need to rebuild cst.CollisionChains.
2092 else
2094 // we don't have same movement than prec testMove(), we must rebuild cst.CollisionChains.
2095 // Prec settings no more valids.
2096 cst.PrecValid= false;
2102 // 1. Choose a local basis (same than in testMove()).
2103 //===========
2104 // Take the retrieverInstance of startPos as a local basis.
2105 CVector origin;
2106 origin= getInstance(startPos.InstanceId).getOrigin();
2109 // 2. test collisions with CollisionChains.
2110 //===========
2111 CVector start= startPos.LocalPosition.Estimation;
2112 // compute end with real delta position.
2113 CVector end= start + delta*t;
2115 // If asked, we must rebuild array of collision chains.
2116 if(rebuildChains)
2118 // H_AUTO(PACS_GR_doMove_rebuildChains);
2120 // compute bboxmove.
2121 CAABBox bboxMove;
2122 // must add some extent, to be sure to include snapped CLocalRetriever vertex (2.0f/256 should be sufficient).
2123 // Nb: this include the precision problem just below (move a little).
2124 float radius= 4.0f/Vector2sAccuracy;
2125 bboxMove.setCenter(start-CVector(radius, radius, 0));
2126 bboxMove.extend(start+CVector(radius, radius, 0));
2127 bboxMove.extend(end-CVector(radius, radius, 0));
2128 bboxMove.extend(end+CVector(radius, radius, 0));
2130 // find possible collisions in bboxMove+origin. fill cst.CollisionChains.
2131 findCollisionChains(cst, bboxMove, origin);
2135 // look where we arrive.
2136 CSurfaceIdent endSurface;
2137 CVector endRequest= end;
2138 const sint maxPbPrec= 32; // move away from 4 mm at max, in each 8 direction.
2139 sint pbPrecNum= 0;
2141 // must snap the end position.
2142 CRetrieverInstance::snapVector(endRequest);
2143 end= endRequest;
2145 // verify start is already snapped
2147 CVector startTest= start;
2148 CRetrieverInstance::snapVector(startTest);
2149 nlassert( start == startTest );
2153 // Normally, just one iteration is made in this loop (but if precision problem (stopOnEdge, startOnEdge....).
2154 for(;;)
2156 // must snap the end position.
2157 CRetrieverInstance::snapVector(end);
2159 CVector2f startCol(start.x, start.y);
2160 CVector2f endCol(end.x, end.y);
2162 // If same 2D position, just return startPos (suppose no movement)
2163 if(endCol==startCol)
2165 UGlobalPosition res;
2166 res= startPos;
2167 // keep good z movement.
2168 res.LocalPosition.Estimation.z= end.z;
2169 return res;
2172 // search destination problem.
2173 UGlobalPosition restart;
2174 endSurface= testMovementWithCollisionChains(cst, startCol, endCol, startSurface, restart);
2176 // if no precision problem, Ok, we have found our destination surface (or anormal collide against a wall).
2177 if (endSurface.SurfaceId >= -1)
2179 break;
2181 // left an interior, retrieved position and ask to restart collision from retrieved position
2182 else if (endSurface.SurfaceId == -3)
2184 start = getDoubleGlobalPosition(restart) - origin;
2185 startSurface.RetrieverInstanceId = restart.InstanceId;
2186 startSurface.SurfaceId = restart.LocalPosition.Surface;
2187 // should be snapped here
2188 CVector startTest= start;
2189 CRetrieverInstance::snapVector(startTest);
2190 nlassert( start == startTest );
2192 /* else we are in deep chit, for one on those reason:
2193 - traverse on point.
2194 - stop on a edge (dist==0).
2195 - start on a edge (dist==0).
2196 - run // on a edge (NB: dist==0 too).
2198 else if (endSurface.SurfaceId == -2)
2200 // For simplicty, just try to move a little the end position
2201 if(pbPrecNum<maxPbPrec)
2203 static struct {sint x,y;} dirs[8]= { {1,0}, {1,1}, {0,1}, {-1,1}, {-1,0}, {-1,-1}, {0,-1}, {1,-1}};
2204 sint dir= pbPrecNum%8;
2205 sint dist= pbPrecNum/8+1;
2206 CVector dta;
2208 // compute small move.
2209 dta.x= dirs[dir].x * dist * 1.0f/SnapPrecision;
2210 dta.y= dirs[dir].y * dist * 1.0f/SnapPrecision;
2211 dta.z= 0;
2213 // add it to the original end pos requested.
2214 end= endRequest + dta;
2216 pbPrecNum++;
2218 else
2220 // do not move at all.
2221 endSurface= CSurfaceIdent(-1,-1);
2222 break;
2227 // 3. return result.
2228 //===========
2229 // Problem?? do not move.
2230 if(endSurface.SurfaceId==-1)
2231 return startPos;
2232 else
2234 // else must return good GlobalPosition.
2235 CGlobalPosition res;
2237 res.InstanceId= endSurface.RetrieverInstanceId;
2238 res.LocalPosition.Surface= endSurface.SurfaceId;
2240 // compute newPos, localy to the endSurface.
2241 // get delta between startPos.instance and curInstance.
2242 // NB: for float precision, it is important to compute deltaOrigin, and after compute newPos in local.
2243 CVector deltaOrigin;
2244 deltaOrigin= origin - getInstance(res.InstanceId).getOrigin();
2246 // Because Origin precision is 1 meter, and end precision is 1/1024 meter, we have no precision problem.
2247 // this is true because we cannot move more than, say 4*160 meters in one doMove().
2248 // So global position should not be bigger than 1024 * 1024/1024 meters. => Hence 20 bits of precision is
2249 // required. We have 23 with floats.
2250 res.LocalPosition.Estimation= end + deltaOrigin;
2253 // result.
2254 return res;
2260 // ***************************************************************************
2261 const NLPACS::TCollisionSurfaceDescVector &NLPACS::CGlobalRetriever::testBBoxRot(const CGlobalPosition &startPos,
2262 const NLMISC::CVector &locI, const NLMISC::CVector &locJ, CCollisionSurfaceTemp &cst) const
2264 // H_AUTO(PACS_GR_testBBoxRot);
2266 CSurfaceIdent startSurface(startPos.InstanceId, startPos.LocalPosition.Surface);
2268 // 0. reset.
2269 //===========
2270 // reset result.
2271 cst.CollisionDescs.clear();
2273 // should not doMove() after a testBBoxRot.
2274 cst.PrecValid= false;
2277 // 1. Choose a local basis.
2278 //===========
2279 // Take the retrieverInstance of startPos as a local basis.
2280 CVector origin;
2281 origin= getInstance(startPos.InstanceId).getOrigin();
2284 // 2. compute OBB.
2285 //===========
2286 CVector2f obbStart[4];
2287 // compute start, relative to the retriever instance.
2288 CVector start= startPos.LocalPosition.Estimation;
2289 CVector2f obbCenter(start.x, start.y);
2290 CVector2f locI2d(locI.x, locI.y);
2291 CVector2f locJ2d(locJ.x, locJ.y);
2293 // build points in CCW.
2294 obbStart[0]= obbCenter - locI2d - locJ2d;
2295 obbStart[1]= obbCenter + locI2d - locJ2d;
2296 obbStart[2]= obbCenter + locI2d + locJ2d;
2297 obbStart[3]= obbCenter - locI2d + locJ2d;
2299 // 3. compute bboxmove.
2300 //===========
2301 CAABBox bboxMove;
2302 // extend the bbox.
2303 bboxMove.setCenter(CVector(obbStart[0].x, obbStart[0].y, 0));
2304 bboxMove.extend(CVector(obbStart[1].x, obbStart[1].y, 0));
2305 bboxMove.extend(CVector(obbStart[2].x, obbStart[2].y, 0));
2306 bboxMove.extend(CVector(obbStart[3].x, obbStart[3].y, 0));
2310 // 4. find possible collisions in bboxMove+origin. fill cst.CollisionChains.
2311 //===========
2312 findCollisionChains(cst, bboxMove, origin);
2316 // 5. test Rotcollisions with CollisionChains.
2317 //===========
2318 CVector2f startCol(start.x, start.y);
2319 testRotCollisionWithCollisionChains(cst, startCol, startSurface, obbStart);
2322 // result.
2323 return cst.CollisionDescs;
2327 // ***************************************************************************
2328 void NLPACS::CGlobalRetriever::testRotCollisionWithCollisionChains(CCollisionSurfaceTemp &cst, const CVector2f &/* startCol */, CSurfaceIdent startSurface, const CVector2f bbox[4]) const
2330 // H_AUTO(PACS_GR_testRotCollisionWithCollisionChains);
2332 // start currentSurface with surface start.
2333 CSurfaceIdent currentSurface= startSurface;
2334 sint i;
2336 // reset result.
2337 cst.RotDescs.clear();
2338 cst.CollisionDescs.clear();
2342 Test collisions with all collision chains. Then, to manage recovery, test the graph of surfaces.
2344 // run all collisionChain.
2345 //========================
2346 for(i=0; i<(sint)cst.CollisionChains.size(); i++)
2348 CCollisionChain &colChain= cst.CollisionChains[i];
2351 // test all edges of this chain, and insert if necessary.
2352 //========================
2353 // run list of edge.
2354 sint32 curEdge= colChain.FirstEdgeCollide;
2355 while(curEdge!=(sint32)0xFFFFFFFF)
2357 // get the edge.
2358 CEdgeCollideNode &colEdge= cst.getEdgeCollideNode(curEdge);
2360 // test collision with this edge.
2361 if(colEdge.testBBoxCollide(bbox))
2363 // yes we have a 2D collision with this chain.
2364 cst.RotDescs.push_back(CRotSurfaceDesc(colChain.LeftSurface, colChain.RightSurface));
2365 break;
2368 // next edge.
2369 curEdge= colEdge.Next;
2374 // Traverse the array of collisions.
2375 //========================
2376 sint indexCD=0;
2377 for(;;)
2379 // What surfaces collided do we reach from this currentSurface??
2380 for(i=0;i<(sint)cst.RotDescs.size();i++)
2382 // Do we collide with this chain?? chain not tested??
2383 if(cst.RotDescs[i].hasSurface(currentSurface) && !cst.RotDescs[i].Tested)
2385 cst.RotDescs[i].Tested= true;
2387 // insert the collision with the other surface.
2388 CCollisionSurfaceDesc col;
2389 col.ContactTime= 0;
2390 col.ContactNormal= CVector::Null;
2391 col.ContactSurface= cst.RotDescs[i].getOtherSurface(currentSurface);
2392 cst.CollisionDescs.push_back(col);
2396 // get the next currentSurface from surface collided (traverse the graph of collisions).
2397 if(indexCD<(sint)cst.CollisionDescs.size())
2398 currentSurface= cst.CollisionDescs[indexCD++].ContactSurface;
2399 else
2400 break;
2405 // ***************************************************************************
2407 NLPACS::UGlobalRetriever *NLPACS::UGlobalRetriever::createGlobalRetriever (const char *globalRetriever, const NLPACS::URetrieverBank *retrieverBank)
2410 // Cast
2411 // nlassert (dynamic_cast<const NLPACS::CRetrieverBank*>(retrieverBank));
2412 const NLPACS::CRetrieverBank* bank=static_cast<const NLPACS::CRetrieverBank*>(retrieverBank);
2414 CIFile file;
2415 if (file.open(CPath::lookup(globalRetriever)))
2417 CGlobalRetriever *retriever = new CGlobalRetriever();
2419 // always set the retriever bank before serializing !!
2420 retriever->setRetrieverBank(bank);
2422 file.serial(*retriever);
2423 retriever->initAll(false); // don't init instances as we serialized them
2425 return static_cast<UGlobalRetriever *>(retriever);
2427 else
2428 return NULL;
2431 // ***************************************************************************
2433 void NLPACS::UGlobalRetriever::deleteGlobalRetriever (UGlobalRetriever *retriever)
2435 // Cast
2436 nlassert (dynamic_cast<NLPACS::CGlobalRetriever*>(retriever));
2437 NLPACS::CGlobalRetriever* r=static_cast<NLPACS::CGlobalRetriever*>(retriever);
2439 // Delete
2440 delete r;
2443 // ***************************************************************************
2445 float NLPACS::CGlobalRetriever::getMeanHeight(const UGlobalPosition &pos) const
2447 // for wrong positions, leave it unchanged
2448 if ((pos.InstanceId==-1)||(pos.LocalPosition.Surface==-1))
2449 return pos.LocalPosition.Estimation.z;
2451 // get instance/localretriever.
2452 const CRetrieverInstance &instance = getInstance(pos.InstanceId);
2453 const CLocalRetriever &retriever= _RetrieverBank->getRetriever(instance.getRetrieverId());
2455 if (!retriever.isLoaded())
2456 return pos.LocalPosition.Estimation.z;
2458 // return height from local retriever
2459 return retriever.getHeight(pos.LocalPosition);
2462 // ***************************************************************************
2463 float NLPACS::CGlobalRetriever::getInteriorHeightAround(const UGlobalPosition &pos, float outsideTolerance) const
2465 // for wrong positions, leave it unchanged
2466 if ((pos.InstanceId==-1)||(pos.LocalPosition.Surface==-1))
2467 return pos.LocalPosition.Estimation.z;
2469 // get instance/localretriever.
2470 const CRetrieverInstance &instance = getInstance(pos.InstanceId);
2471 const CLocalRetriever &retriever= _RetrieverBank->getRetriever(instance.getRetrieverId());
2473 if (!retriever.isLoaded())
2474 return pos.LocalPosition.Estimation.z;
2476 // return height from local retriever
2477 return retriever.getInteriorHeightAround(pos.LocalPosition, outsideTolerance);
2480 // ***************************************************************************
2482 bool NLPACS::CGlobalRetriever::testRaytrace (const CVectorD &/* v0 */, const CVectorD &/* v1 */)
2484 // TODO: implement raytrace
2485 return false;
2488 // ***************************************************************************
2490 void NLPACS::CGlobalRetriever::refreshLrAround(const CVector &position, float radius)
2492 NLPACS_HAUTO_REFRESH_LR_AROUND
2494 // check if retriever bank is all loaded, and if yes don't refresh it
2495 if (_RetrieverBank->allLoaded())
2496 return;
2498 std::list<CLrLoader>::iterator ite = _LrLoaderList.begin();
2499 while (ite != _LrLoaderList.end())
2501 // Finished loaded a lr, stream it into rbank
2502 if (ite->Finished && ite->Successful)
2504 if (!ite->_Buffer.isReading())
2505 ite->_Buffer.invert();
2507 ite->_Buffer.resetBufPos();
2509 // NLMEMORY::CheckHeap (true);
2511 const_cast<CRetrieverBank*>(_RetrieverBank)->loadRetriever(ite->LrId, ite->_Buffer);
2513 // NLMEMORY::CheckHeap (true);
2515 ite->_Buffer.clear();
2517 // NLMEMORY::CheckHeap (true);
2519 //nlinfo("Lr '%s' loading task complete", ite->LoadFile.c_str());
2521 // Remove this entry
2522 _LrLoaderList.erase (ite);
2524 break;
2527 // Next lr
2528 ite++;
2531 CAABBox box;
2532 box.setCenter(position);
2533 box.setHalfSize(CVector(radius, radius, 1000.0f));
2535 selectInstances(box, _InternalCST);
2537 set<uint> newlr, in, out;
2538 map<uint, CVector> lrPosition;
2540 uint i;
2541 for (i=0; i<_InternalCST.CollisionInstances.size(); ++i)
2543 uint lrId = (uint)(_Instances[_InternalCST.CollisionInstances[i]].getRetrieverId());
2544 newlr.insert(lrId);
2545 lrPosition.insert (map<uint, CVector>::value_type(lrId, _Instances[_InternalCST.CollisionInstances[i]].getBBox().getCenter()));
2548 const_cast<CRetrieverBank*>(_RetrieverBank)->diff(newlr, in, out);
2550 set<uint>::iterator it;
2552 // unload all possible retrievers
2553 for (it=out.begin(); it!=out.end(); ++it)
2555 const_cast<CRetrieverBank*>(_RetrieverBank)->unloadRetriever(*it);
2556 //nlinfo("Freed Lr '%s'", (_RetrieverBank->getNamePrefix() + "_" + toString(*it) + ".lr").c_str());
2559 // if load task idle and more lr to load, setup load task
2560 set<uint>::iterator iteIn = in.begin();
2561 while (iteIn != in.end())
2563 // Already exist ?
2564 ite = _LrLoaderList.begin();
2565 while (ite != _LrLoaderList.end())
2567 if (ite->LrId == *iteIn)
2568 break;
2570 ite++;
2573 // Not found ?
2574 if (ite == _LrLoaderList.end())
2576 // Get the position fot this LR
2577 map<uint, CVector>::iterator iteLR = lrPosition.find(*iteIn);
2578 nlassert (iteLR != lrPosition.end());
2580 _LrLoaderList.push_back (CLrLoader (iteLR->second));
2581 CLrLoader &loader = _LrLoaderList.back();
2582 loader.Finished = false;
2583 loader.LrId = *iteIn;
2584 loader.LoadFile = _RetrieverBank->getNamePrefix() + "_" + toString(loader.LrId) + ".lr";
2586 CAsyncFileManager::getInstance().addLoadTask(&loader);
2588 //nlinfo("Lr '%s' added to load", loader.LoadFile.c_str());
2591 // Next lr to load
2592 iteIn++;
2596 // ***************************************************************************
2597 void NLPACS::CGlobalRetriever::waitEndOfAsyncLoading()
2599 while (!_LrLoaderList.empty ())
2601 std::list<CLrLoader>::iterator ite = _LrLoaderList.begin();
2602 while (ite != _LrLoaderList.end())
2604 // Finished loaded a lr, stream it into rbank
2605 if (ite->Finished)
2607 if (!ite->_Buffer.isReading())
2608 ite->_Buffer.invert();
2610 const_cast<CRetrieverBank*>(_RetrieverBank)->loadRetriever(ite->LrId, ite->_Buffer);
2612 ite->_Buffer.clear();
2614 // Remove this from the list
2615 _LrLoaderList.erase(ite);
2617 break;
2621 ite++;
2624 if (!_LrLoaderList.empty())
2625 nlSleep(0);
2630 // ***************************************************************************
2631 void NLPACS::CGlobalRetriever::refreshLrAroundNow(const CVector &position, float radius)
2633 // check if retriever bank is all loaded, and if yes don't refresh it
2634 if (_RetrieverBank->allLoaded())
2635 return;
2637 // must wait all current have finished loading
2638 waitEndOfAsyncLoading();
2640 // Select new to load
2641 CAABBox box;
2642 box.setCenter(position);
2643 box.setHalfSize(CVector(radius, radius, 1000.0f));
2645 selectInstances(box, _InternalCST);
2647 set<uint> newlr, in, out;
2648 uint i;
2649 for (i=0; i<_InternalCST.CollisionInstances.size(); ++i)
2650 newlr.insert((uint)(_Instances[_InternalCST.CollisionInstances[i]].getRetrieverId()));
2652 const_cast<CRetrieverBank*>(_RetrieverBank)->diff(newlr, in, out);
2654 set<uint>::iterator it;
2656 // unload all possible retrievers
2657 for (it=out.begin(); it!=out.end(); ++it)
2658 const_cast<CRetrieverBank*>(_RetrieverBank)->unloadRetriever(*it);
2660 // unload all possible retrievers
2661 for (it=in.begin(); it!=in.end(); ++it)
2663 string fname = _RetrieverBank->getNamePrefix() + "_" + toString(*it) + ".lr";
2664 CIFile f;
2665 if (!f.open(CPath::lookup(fname, false)))
2667 nlwarning("Couldn't find file '%s' to load, retriever loading aborted", fname.c_str());
2668 continue;
2671 const_cast<CRetrieverBank*>(_RetrieverBank)->loadRetriever(*it, f);
2675 void NLPACS::CGlobalRetriever::CLrLoader::run()
2677 CIFile f;
2679 // async
2680 f.setAsyncLoading(true);
2681 f.setCacheFileOnOpen(true);
2683 Successful = false;
2685 if (!f.open(CPath::lookup(LoadFile, false)))
2687 nlwarning("Couldn't find file '%s' to load, retriever loading aborted", LoadFile.c_str());
2688 _Buffer.clear();
2689 Finished = true;
2690 return;
2693 if (!_Buffer.isReading())
2694 _Buffer.invert();
2696 uint8 *buffer = _Buffer.bufferToFill(f.getFileSize());
2697 f.serialBuffer(buffer, f.getFileSize());
2699 Successful = true;
2700 Finished = true;
2703 // ***************************************************************************
2704 void NLPACS::CGlobalRetriever::CLrLoader::getName (std::string &result) const
2706 result = "LoadLR(" + LoadFile + ")";
2711 NLMISC_CATEGORISED_VARIABLE(nel, uint, PacsRetrieveVerbose, "Allow retrieve position to dump info");
2713 // end of CGlobalRetriever methods implementation