1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
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;
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
);
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()
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
))
114 if (_Instances
[*it
].getBBox().intersect(bbox
))
116 if (!_RetrieverBank
->isLoaded(_Instances
[*it
].getRetrieverId()))
118 cst
.CollisionInstances
.push_back(*it
);
127 void NLPACS::CGlobalRetriever::serial(NLMISC::IStream
&f
)
133 (void)f
.serialVersion(0);
135 f
.serialCont(_Instances
);
144 void NLPACS::CGlobalRetriever::check() const
148 for (i
=0; i
<_Instances
.size(); ++i
)
150 if (_Instances
[i
].getInstanceId() == -1)
152 nlwarning("Uninitialized instance %d", i
);
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
);
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
);
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
);
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
));
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
));
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
);
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())
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
)
255 if (pos
.InstanceId
< 0)
258 CVectorD gpos
= getDoubleGlobalPosition(pos
);
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
)
270 selectInstances(sbox
, _InternalCST
);
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())
280 CChainQuad
&chainquad
= retriever
.getChainQuad();
283 CVector origin
= instance
.getOrigin();
284 box
.setCenter(sbox
.getCenter()-origin
);
285 box
.setHalfSize(sbox
.getHalfSize());
286 chainquad
.selectEdges(box
, _InternalCST
);
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));
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;
316 if (!retriever
.getFullOrderedChains().empty())
318 const COrderedChain3f
&ochain
= retriever
.getFullOrderedChain(entry
.OChainId
);
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;
339 const COrderedChain
&ochain
= retriever
.getOrderedChain(entry
.OChainId
);
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
)
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
);
378 for (i
=0; i
<_InternalCST
.CollisionInstances
.size(); ++i
)
380 CRetrieverInstance
&neighbor
= _Instances
[_InternalCST
.CollisionInstances
[i
]];
382 if (neighbor
.getInstanceId() == instance
.getInstanceId())
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()
404 for (n
=0; n
<_Instances
.size(); ++n
)
405 _Instances
[n
].unlink(_Instances
);
409 void NLPACS::CGlobalRetriever::makeAllLinks()
414 for (n
=0; n
<_Instances
.size(); ++n
)
418 void NLPACS::CGlobalRetriever::initAll(bool initInstances
)
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()));
434 const NLPACS::CRetrieverInstance
&NLPACS::CGlobalRetriever::makeInstance(uint32 retrieverId
, uint8 orientation
, const CVector
&origin
)
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();
453 if (hsize
!= CVector::Null
)
455 if (_BBox
.getHalfSize() == CVector::Null
)
457 _BBox
= instance
.getBBox();
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());
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();
511 // get the best matching instances
513 bbpos
.setCenter(estimated
);
514 bbpos
.setHalfSize(CVector(0.5f
, 0.5f
, 0.5f
));
515 if (!selectInstances(bbpos
, _InternalCST
, retrieveSpec
))
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());
532 for (j
=0; j
<_ForbiddenInstances
.size(); ++j
)
533 if (_ForbiddenInstances
[j
] == (sint32
)id
)
536 if (j
<_ForbiddenInstances
.size() || !retriever
.isLoaded())
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());
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
)
559 if (selInstance
== 0)
560 bestDist
= _InternalCST
.SortedSurfaces
[0].Distance
;
563 if (selInstance
>= _InternalCST
.SortedSurfaces
.size())
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
)
586 moved
= retriever
.insurePosition(result
.LocalPosition
);
589 while (moved
&& numMove
< 100);
590 // the algo won't loop infinitely
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);
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");
617 if (PacsRetrieveVerbose
)
618 nlwarning("PACS: unable to retrieve correct position (%f,%f,%f)", estimated
.x
, estimated
.y
, estimated
.z
);
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();
641 // get the best matching instances
643 bbpos
.setCenter(estimated
);
644 bbpos
.setHalfSize(CVector(0.5f
, 0.5f
, 0.5f
));
645 bool canGet
= selectInstances(bbpos
, _InternalCST
);
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());
665 for (j
=0; j
<_ForbiddenInstances
.size(); ++j
)
666 if (_ForbiddenInstances
[j
] == (sint32
)id
)
669 if (j
<_ForbiddenInstances
.size() || !retriever
.isLoaded())
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
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
)
709 moved
= retriever
.insurePosition(result
.LocalPosition
);
712 while (moved
&& numMove
< 100);
713 // the algo won't loop infinitely
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);
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
);
732 // nlwarning("PACS: unable to retrieve correct position (%f,%f,%f)", estimated.x, estimated.y, estimated.z);
743 sint32
NLPACS::CGlobalRetriever::getIdentifier(const string
&id
) const
746 for (i
=0; i
<(sint32
)(_RetrieverBank
->getRetrievers().size()); ++i
)
747 if (getRetriever(i
).getIdentifier() == id
)
753 const string
&NLPACS::CGlobalRetriever::getIdentifier(const NLPACS::UGlobalPosition
&position
) const
755 static const string nullString
;
757 if (position
.InstanceId
== -1)
760 return getRetriever(_Instances
[position
.InstanceId
].getRetrieverId()).getIdentifier();
763 sint32
NLPACS::CGlobalRetriever::getLocalRetrieverId(const NLPACS::UGlobalPosition
&position
) const
765 if (position
.InstanceId
== -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
);
780 // check retriever exists
784 const CRetrieverInstance
&instance
= makeInstance(retrieverId
, 0, CVector(position
));
786 // check make instance success
787 if (instance
.getInstanceId() == -1 || instance
.getRetrieverId() != retrieverId
)
790 // links new instance to its neighbors
791 makeLinks(instance
.getInstanceId());
793 instanceId
= instance
.getInstanceId();
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
);
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)
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
);
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
);
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
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;
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
));
911 // couldn't find a path
915 multimap
<float, CRetrieverInstance::CAStarNodeAccess
>::iterator it
;
924 CRetrieverInstance::CAStarNodeAccess pathNode
= node
;
926 while (pathNode
.InstanceId
!= -1)
929 CRetrieverInstance
&instance
= _Instances
[pathNode
.InstanceId
];
930 CRetrieverInstance::CAStarNodeInfo
&pathInfo
= instance
._NodesInformation
[pathNode
.NodeId
];
931 pathNode
= pathInfo
.Parent
;
934 path
.resize(numNodes
);
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());
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)
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
;
1016 // if the chain cannot be crossed
1020 nextSurface
= &(nextRetriever
->getSurface(nextNode
.NodeId
));
1022 if (nextSurface
->getFlags() & forbidFlags
)
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
)
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
)
1047 if (openIt
!= open
.end())
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
;
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
);
1085 path
.resize(astarPath
.size());
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
1098 // if it is the first point, just copy the begin
1099 surf
.Start
.ULocalPosition::operator= (begin
.LocalPosition
);
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
);
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();
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();
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 // ***************************************************************************
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())
1173 const CRetrievableSurface
&surf
= retr
.getSurface(surfId
.SurfaceId
);
1182 // ***************************************************************************
1183 void NLPACS::CGlobalRetriever::findCollisionChains(CCollisionSurfaceTemp
&cst
, const NLMISC::CAABBox
&bboxMove
, const NLMISC::CVector
&origin
) const
1185 // H_AUTO(PACS_GR_findCollisionChains);
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.
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.
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)
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());
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.
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.
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
)
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
;
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)
1291 cc
.RightSurface
.SurfaceId
= -1;
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.
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)
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());
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
)
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.
1354 cst
.CollisionChains
.resize(nCollisionChain
);
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.
1375 cst.CollisionChains.resize(nCollisionChain);
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;
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).
1420 // run all collisionChain.
1421 //========================
1422 for(i
=0; i
<(sint
)cst
.CollisionChains
.size(); i
++)
1424 CCollisionChain
&colChain
= cst
.CollisionChains
[i
];
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
);
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
)
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)
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??
1482 curEdge
= colEdge
.Next
;
1486 // If collision with this chain, must insert it in the array of collision.
1487 //========================
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();
1501 // Estimate current Z
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
;
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();
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
)
1540 // if we have found our old collision, stop, there is no need to search more.
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
;
1560 // if any, erase old collision against this surface. NB: here, colFound>indexInsert.
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())
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??
1586 if(currentSurface
.SurfaceId
<0)
1590 // test if it is a walkable wall.
1591 sint32 locRetId
= this->getInstance(currentSurface
.RetrieverInstanceId
).getRetrieverId();
1593 if (!_RetrieverBank
->getRetriever(locRetId
).isLoaded())
1595 nextCollisionSurfaceTested
++;
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());
1611 // If we touch a wall, this is the end of search.
1614 // There can be no more collision after this one.
1615 cst
.CollisionDescs
.resize(nextCollisionSurfaceTested
+1);
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.
1642 leftWall
= !(left
->isFloor() || left
->isCeiling());
1644 // test if right surface is a wall.
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
;
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.
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
;
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())
1702 // test all edges of this chain, and insert if necessary.
1703 //========================
1705 // run list of edge.
1706 sint32 curEdge
= colChain
.FirstEdgeCollide
;
1707 while(curEdge
!=(sint32
)0xFFFFFFFF)
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.
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.
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.
1749 return CSurfaceIdent(-2, -2);
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;
1763 curEdge
= colEdge
.Next
;
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
)
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
)
1805 checkedExtEdges
.push_back(make_pair(cmp
, enterInterior
));
1807 // if leave interior, retrieve good position
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();
1817 // Estimate current Z
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);
1847 currentSurface
= msd
.getOtherSurface(currentSurface
);
1852 currentSurface
= msd
.getOtherSurface(currentSurface
);
1855 // Do we touch a wall?? should not happens, but important for security.
1857 if(currentSurface
.SurfaceId
<0)
1861 // test if it is a walkable wall.
1862 sint32 locRetId
= this->getInstance(currentSurface
.RetrieverInstanceId
).getRetrieverId();
1864 if (!_RetrieverBank
->getRetriever(locRetId
).isLoaded())
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.
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
);
1898 cst
.CollisionDescs
.clear();
1901 if (startPos
.InstanceId
==-1)
1903 // Warning this primitive is not on a surface
1906 // Return NULL when lost
1909 // store this request in cst.
1910 cst
.PrecStartSurface
= startSurface
;
1911 cst
.PrecStartPos
= startPos
.LocalPosition
.Estimation
;
1912 cst
.PrecDeltaPos
= delta
;
1913 cst
.PrecValid
= true;
1917 // Abort if deltamove is 0,0,0.
1919 return &cst
.CollisionDescs
;
1921 // 1. Choose a local basis.
1923 // Take the retrieverInstance of startPos as a local basis.
1925 origin
= getInstance(startPos
.InstanceId
).getOrigin();
1928 // 2. compute 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
;
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.
1944 findCollisionChains(cst
, bboxMove
, origin
);
1948 // 4. test collisions with CollisionChains.
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
);
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
);
1972 cst
.CollisionDescs
.clear();
1975 if (startPos
.InstanceId
==-1)
1977 // Warning this primitive is not on a surface
1980 // Return NULL when lost
1984 // store this request in cst.
1985 cst
.PrecStartSurface
= startSurface
;
1986 cst
.PrecStartPos
= startPos
.LocalPosition
.Estimation
;
1987 cst
.PrecDeltaPos
= delta
;
1988 cst
.PrecValid
= true;
1992 // Abort if deltamove is 0,0,0.
1994 return &cst
.CollisionDescs
;
1996 // 1. Choose a local basis.
1998 // Take the retrieverInstance of startPos as a local basis.
2000 origin
= getInstance(startPos
.InstanceId
).getOrigin();
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.
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.
2035 findCollisionChains(cst
, bboxMove
, origin
);
2039 // 5. test collisions with CollisionChains.
2041 CVector2f
startCol(start
.x
, start
.y
);
2042 CVector2f
deltaCol(delta
.x
, delta
.y
);
2043 testCollisionWithCollisionChains(cst
, startCol
, deltaCol
, startSurface
, 0, obbStart
, CGlobalRetriever::BBox
);
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
);
2060 clamp(t
, 0.0f
, 1.0f
);
2064 // reset CollisionDescs.
2065 cst
.CollisionDescs
.clear();
2068 if (startPos
.InstanceId
==-1)
2070 // Warining: this primitive is not on a surface
2079 // same move request than prec testMove() ??.
2080 if( cst
.PrecStartSurface
!= startSurface
||
2081 cst
.PrecStartPos
!=startPos
.LocalPosition
.Estimation
||
2082 cst
.PrecDeltaPos
!=delta
||
2085 // if not, just return start.
2087 //nlwarning ("BEN: you must fix this, it s happen!!!");
2090 // Since we are sure we have same movement than prec testMove(), no need to rebuild cst.CollisionChains.
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()).
2104 // Take the retrieverInstance of startPos as a local basis.
2106 origin
= getInstance(startPos
.InstanceId
).getOrigin();
2109 // 2. test collisions with CollisionChains.
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.
2118 // H_AUTO(PACS_GR_doMove_rebuildChains);
2120 // compute 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.
2141 // must snap the end position.
2142 CRetrieverInstance::snapVector(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....).
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
;
2167 // keep good z movement.
2168 res
.LocalPosition
.Estimation
.z
= end
.z
;
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)
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;
2208 // compute small move.
2209 dta
.x
= dirs
[dir
].x
* dist
* 1.0f
/SnapPrecision
;
2210 dta
.y
= dirs
[dir
].y
* dist
* 1.0f
/SnapPrecision
;
2213 // add it to the original end pos requested.
2214 end
= endRequest
+ dta
;
2220 // do not move at all.
2221 endSurface
= CSurfaceIdent(-1,-1);
2227 // 3. return result.
2229 // Problem?? do not move.
2230 if(endSurface
.SurfaceId
==-1)
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
;
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
);
2271 cst
.CollisionDescs
.clear();
2273 // should not doMove() after a testBBoxRot.
2274 cst
.PrecValid
= false;
2277 // 1. Choose a local basis.
2279 // Take the retrieverInstance of startPos as a local basis.
2281 origin
= getInstance(startPos
.InstanceId
).getOrigin();
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.
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.
2312 findCollisionChains(cst
, bboxMove
, origin
);
2316 // 5. test Rotcollisions with CollisionChains.
2318 CVector2f
startCol(start
.x
, start
.y
);
2319 testRotCollisionWithCollisionChains(cst
, startCol
, startSurface
, obbStart
);
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
;
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)
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
));
2369 curEdge
= colEdge
.Next
;
2374 // Traverse the array of collisions.
2375 //========================
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
;
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
;
2405 // ***************************************************************************
2407 NLPACS::UGlobalRetriever
*NLPACS::UGlobalRetriever::createGlobalRetriever (const char *globalRetriever
, const NLPACS::URetrieverBank
*retrieverBank
)
2411 // nlassert (dynamic_cast<const NLPACS::CRetrieverBank*>(retrieverBank));
2412 const NLPACS::CRetrieverBank
* bank
=static_cast<const NLPACS::CRetrieverBank
*>(retrieverBank
);
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
);
2431 // ***************************************************************************
2433 void NLPACS::UGlobalRetriever::deleteGlobalRetriever (UGlobalRetriever
*retriever
)
2436 nlassert (dynamic_cast<NLPACS::CGlobalRetriever
*>(retriever
));
2437 NLPACS::CGlobalRetriever
* r
=static_cast<NLPACS::CGlobalRetriever
*>(retriever
);
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
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())
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
);
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
;
2541 for (i
=0; i
<_InternalCST
.CollisionInstances
.size(); ++i
)
2543 uint lrId
= (uint
)(_Instances
[_InternalCST
.CollisionInstances
[i
]].getRetrieverId());
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())
2564 ite
= _LrLoaderList
.begin();
2565 while (ite
!= _LrLoaderList
.end())
2567 if (ite
->LrId
== *iteIn
)
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());
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
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
);
2624 if (!_LrLoaderList
.empty())
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())
2637 // must wait all current have finished loading
2638 waitEndOfAsyncLoading();
2640 // Select new to load
2642 box
.setCenter(position
);
2643 box
.setHalfSize(CVector(radius
, radius
, 1000.0f
));
2645 selectInstances(box
, _InternalCST
);
2647 set
<uint
> newlr
, in
, out
;
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";
2665 if (!f
.open(CPath::lookup(fname
, false)))
2667 nlwarning("Couldn't find file '%s' to load, retriever loading aborted", fname
.c_str());
2671 const_cast<CRetrieverBank
*>(_RetrieverBank
)->loadRetriever(*it
, f
);
2675 void NLPACS::CGlobalRetriever::CLrLoader::run()
2680 f
.setAsyncLoading(true);
2681 f
.setCacheFileOnOpen(true);
2685 if (!f
.open(CPath::lookup(LoadFile
, false)))
2687 nlwarning("Couldn't find file '%s' to load, retriever loading aborted", LoadFile
.c_str());
2693 if (!_Buffer
.isReading())
2696 uint8
*buffer
= _Buffer
.bufferToFill(f
.getFileSize());
2697 f
.serialBuffer(buffer
, f
.getFileSize());
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