OPC_SweepAndPrune.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2. /*
  3. * OPCODE - Optimized Collision Detection
  4. * Copyright (C) 2001 Pierre Terdiman
  5. * Homepage: http://www.codercorner.com/Opcode.htm
  6. */
  7. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  8. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  9. /**
  10. * Contains an implementation of the sweep-and-prune algorithm (moved from Z-Collide)
  11. * \file OPC_SweepAndPrune.cpp
  12. * \author Pierre Terdiman
  13. * \date January, 29, 2000
  14. */
  15. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  16. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  17. #include "Opcode.h"
  18. #include <stdint.h>
  19. using namespace Opcode;
  20. inline_ void Sort(udword& id0, udword& id1)
  21. {
  22. if(id0>id1) Swap(id0, id1);
  23. }
  24. class Opcode::SAP_Element
  25. {
  26. public:
  27. inline_ SAP_Element() {}
  28. inline_ SAP_Element(udword id, SAP_Element* next) : mID(id), mNext(next) {}
  29. inline_ ~SAP_Element() {}
  30. udword mID;
  31. SAP_Element* mNext;
  32. };
  33. class Opcode::SAP_Box
  34. {
  35. public:
  36. SAP_EndPoint* Min[3];
  37. SAP_EndPoint* Max[3];
  38. };
  39. class Opcode::SAP_EndPoint
  40. {
  41. public:
  42. float Value; // Min or Max value
  43. SAP_EndPoint* Previous; // Previous EndPoint whose Value is smaller than ours (or null)
  44. SAP_EndPoint* Next; // Next EndPoint whose Value is greater than ours (or null)
  45. udword Data; // Parent box ID *2 | MinMax flag
  46. inline_ void SetData(udword box_id, BOOL is_max) { Data = (box_id<<1)|is_max; }
  47. inline_ BOOL IsMax() const { return Data & 1; }
  48. inline_ udword GetBoxID() const { return Data>>1; }
  49. inline_ void InsertAfter(SAP_EndPoint* element)
  50. {
  51. if(this!=element && this!=element->Next)
  52. {
  53. // Remove
  54. if(Previous) Previous->Next = Next;
  55. if(Next) Next->Previous = Previous;
  56. // Insert
  57. Next = element->Next;
  58. if(Next) Next->Previous = this;
  59. element->Next = this;
  60. Previous = element;
  61. }
  62. }
  63. inline_ void InsertBefore(SAP_EndPoint* element)
  64. {
  65. if(this!=element && this!=element->Previous)
  66. {
  67. // Remove
  68. if(Previous) Previous->Next = Next;
  69. if(Next) Next->Previous = Previous;
  70. // Insert
  71. Previous = element->Previous;
  72. element->Previous = this;
  73. Next = element;
  74. if(Previous) Previous->Next = this;
  75. }
  76. }
  77. };
  78. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  79. /**
  80. * Constructor.
  81. */
  82. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  83. SAP_PairData::SAP_PairData() :
  84. mNbElements (0),
  85. mNbUsedElements (0),
  86. mElementPool (null),
  87. mFirstFree (null),
  88. mNbObjects (0),
  89. mArray (null)
  90. {
  91. }
  92. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  93. /**
  94. * Destructor.
  95. */
  96. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  97. SAP_PairData::~SAP_PairData()
  98. {
  99. Release();
  100. }
  101. void SAP_PairData::Release()
  102. {
  103. mNbElements = 0;
  104. mNbUsedElements = 0;
  105. mNbObjects = 0;
  106. DELETEARRAY(mElementPool);
  107. DELETEARRAY(mArray);
  108. }
  109. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  110. /**
  111. * Initializes.
  112. * \param nb_objects [in]
  113. * \return true if success
  114. */
  115. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  116. bool SAP_PairData::Init(udword nb_objects)
  117. {
  118. // Make sure everything has been released
  119. Release();
  120. if(!nb_objects) return false;
  121. mArray = new SAP_Element*[nb_objects];
  122. CHECKALLOC(mArray);
  123. ZeroMemory(mArray, nb_objects*sizeof(SAP_Element*));
  124. mNbObjects = nb_objects;
  125. return true;
  126. }
  127. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  128. /**
  129. * Remaps a pointer when pool gets resized.
  130. * \param element [in/out] remapped element
  131. * \param delta [in] offset in bytes
  132. */
  133. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  134. inline_ void Remap(SAP_Element*& element, udword delta)
  135. {
  136. if(element) element = (SAP_Element*)(element + delta);
  137. }
  138. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  139. /**
  140. * Gets a free element in the pool.
  141. * \param id [in] element id
  142. * \param next [in] next element
  143. * \param remap [out] possible remapping offset
  144. * \return the new element
  145. */
  146. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  147. SAP_Element* SAP_PairData::GetFreeElem(udword id, SAP_Element* next, udword* remap)
  148. {
  149. if(remap) *remap = 0;
  150. SAP_Element* FreeElem;
  151. if(mFirstFree)
  152. {
  153. // Recycle
  154. FreeElem = mFirstFree;
  155. mFirstFree = mFirstFree->mNext; // First free = next free (or null)
  156. }
  157. else
  158. {
  159. if(mNbUsedElements==mNbElements)
  160. {
  161. // Resize
  162. mNbElements = mNbElements ? (mNbElements<<1) : 2;
  163. SAP_Element* NewElems = new SAP_Element[mNbElements];
  164. if(mNbUsedElements) CopyMemory(NewElems, mElementPool, mNbUsedElements*sizeof(SAP_Element));
  165. // Remap everything
  166. {
  167. udword Delta = uintptr_t(NewElems) - uintptr_t(mElementPool);
  168. for(udword i=0;i<mNbUsedElements;i++) Remap(NewElems[i].mNext, Delta);
  169. for(udword i=0;i<mNbObjects;i++) Remap(mArray[i], Delta);
  170. Remap(mFirstFree, Delta);
  171. Remap(next, Delta);
  172. if(remap) *remap = Delta;
  173. }
  174. DELETEARRAY(mElementPool);
  175. mElementPool = NewElems;
  176. }
  177. FreeElem = &mElementPool[mNbUsedElements++];
  178. }
  179. FreeElem->mID = id;
  180. FreeElem->mNext = next;
  181. return FreeElem;
  182. }
  183. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  184. /**
  185. * Frees an element of the pool.
  186. * \param elem [in] element to free/recycle
  187. */
  188. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  189. inline_ void SAP_PairData::FreeElem(SAP_Element* elem)
  190. {
  191. elem->mNext = mFirstFree; // Next free
  192. mFirstFree = elem;
  193. }
  194. // Add a pair to the set.
  195. void SAP_PairData::AddPair(udword id1, udword id2)
  196. {
  197. // Order the ids
  198. Sort(id1, id2);
  199. ASSERT(id1<mNbObjects);
  200. if(id1>=mNbObjects) return;
  201. // Select the right list from "mArray".
  202. SAP_Element* Current = mArray[id1];
  203. if(!Current)
  204. {
  205. // Empty slot => create new element
  206. mArray[id1] = GetFreeElem(id2, null);
  207. }
  208. else if(Current->mID>id2)
  209. {
  210. // The list is not empty but all elements are greater than id2 => insert id2 in the front.
  211. mArray[id1] = GetFreeElem(id2, mArray[id1]);
  212. }
  213. else
  214. {
  215. // Else find the correct location in the sorted list (ascending order) and insert id2 there.
  216. while(Current->mNext)
  217. {
  218. if(Current->mNext->mID > id2) break;
  219. Current = Current->mNext;
  220. }
  221. if(Current->mID==id2) return; // The pair already exists
  222. // Current->mNext = GetFreeElem(id2, Current->mNext);
  223. udword Delta;
  224. SAP_Element* E = GetFreeElem(id2, Current->mNext, &Delta);
  225. if(Delta) Remap(Current, Delta);
  226. Current->mNext = E;
  227. }
  228. }
  229. // Delete a pair from the set.
  230. void SAP_PairData::RemovePair(udword id1, udword id2)
  231. {
  232. // Order the ids.
  233. Sort(id1, id2);
  234. // Exit if the pair doesn't exist in the set
  235. if(id1>=mNbObjects) return;
  236. // Otherwise, select the correct list.
  237. SAP_Element* Current = mArray[id1];
  238. // If this list is empty, the pair doesn't exist.
  239. if(!Current) return;
  240. // Otherwise, if id2 is the first element, delete it.
  241. if(Current->mID==id2)
  242. {
  243. mArray[id1] = Current->mNext;
  244. FreeElem(Current);
  245. }
  246. else
  247. {
  248. // If id2 is not the first element, start traversing the sorted list.
  249. while(Current->mNext)
  250. {
  251. // If we have moved too far away without hitting id2, then the pair doesn't exist
  252. if(Current->mNext->mID > id2) return;
  253. // Otherwise, delete id2.
  254. if(Current->mNext->mID == id2)
  255. {
  256. SAP_Element* Temp = Current->mNext;
  257. Current->mNext = Temp->mNext;
  258. FreeElem(Temp);
  259. return;
  260. }
  261. Current = Current->mNext;
  262. }
  263. }
  264. }
  265. void SAP_PairData::DumpPairs(Pairs& pairs) const
  266. {
  267. // ### Ugly and slow
  268. for(udword i=0;i<mNbObjects;i++)
  269. {
  270. SAP_Element* Current = mArray[i];
  271. while(Current)
  272. {
  273. ASSERT(Current->mID<mNbObjects);
  274. pairs.AddPair(i, Current->mID);
  275. Current = Current->mNext;
  276. }
  277. }
  278. }
  279. void SAP_PairData::DumpPairs(PairCallback callback, void* user_data) const
  280. {
  281. if(!callback) return;
  282. // ### Ugly and slow
  283. for(udword i=0;i<mNbObjects;i++)
  284. {
  285. SAP_Element* Current = mArray[i];
  286. while(Current)
  287. {
  288. ASSERT(Current->mID<mNbObjects);
  289. if(!(callback)(i, Current->mID, user_data)) return;
  290. Current = Current->mNext;
  291. }
  292. }
  293. }
  294. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  295. /**
  296. * Constructor.
  297. */
  298. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  299. SweepAndPrune::SweepAndPrune()
  300. {
  301. }
  302. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  303. /**
  304. * Destructor.
  305. */
  306. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  307. SweepAndPrune::~SweepAndPrune()
  308. {
  309. }
  310. void SweepAndPrune::GetPairs(Pairs& pairs) const
  311. {
  312. mPairs.DumpPairs(pairs);
  313. }
  314. void SweepAndPrune::GetPairs(PairCallback callback, void* user_data) const
  315. {
  316. mPairs.DumpPairs(callback, user_data);
  317. }
  318. bool SweepAndPrune::Init(udword nb_objects, const AABB** boxes)
  319. {
  320. // 1) Create sorted lists
  321. mNbObjects = nb_objects;
  322. mBoxes = new SAP_Box[nb_objects];
  323. // for(udword i=0;i<nb_objects;i++) mBoxes[i].Box = *boxes[i];
  324. float* Data = new float[nb_objects*2];
  325. for(udword Axis=0;Axis<3;Axis++)
  326. {
  327. mList[Axis] = new SAP_EndPoint[nb_objects*2];
  328. for(udword i=0;i<nb_objects;i++)
  329. {
  330. Data[i*2+0] = boxes[i]->GetMin(Axis);
  331. Data[i*2+1] = boxes[i]->GetMax(Axis);
  332. }
  333. RadixSort RS;
  334. const udword* Sorted = RS.Sort(Data, nb_objects*2).GetRanks();
  335. SAP_EndPoint* PreviousEndPoint = null;
  336. for(udword i=0;i<nb_objects*2;i++)
  337. {
  338. udword SortedIndex = *Sorted++;
  339. float SortedCoord = Data[SortedIndex];
  340. udword BoxIndex = SortedIndex>>1;
  341. ASSERT(BoxIndex<nb_objects);
  342. SAP_EndPoint* CurrentEndPoint = &mList[Axis][SortedIndex];
  343. CurrentEndPoint->Value = SortedCoord;
  344. // CurrentEndPoint->IsMax = SortedIndex&1; // ### could be implicit ?
  345. // CurrentEndPoint->ID = BoxIndex; // ### could be implicit ?
  346. CurrentEndPoint->SetData(BoxIndex, SortedIndex&1); // ### could be implicit ?
  347. CurrentEndPoint->Previous = PreviousEndPoint;
  348. CurrentEndPoint->Next = null;
  349. if(PreviousEndPoint) PreviousEndPoint->Next = CurrentEndPoint;
  350. if(CurrentEndPoint->IsMax()) mBoxes[BoxIndex].Max[Axis] = CurrentEndPoint;
  351. else mBoxes[BoxIndex].Min[Axis] = CurrentEndPoint;
  352. PreviousEndPoint = CurrentEndPoint;
  353. }
  354. }
  355. DELETEARRAY(Data);
  356. CheckListsIntegrity();
  357. // 2) Quickly find starting pairs
  358. mPairs.Init(nb_objects);
  359. {
  360. Pairs P;
  361. CompleteBoxPruning(nb_objects, boxes, P, Axes(AXES_XZY));
  362. for(udword i=0;i<P.GetNbPairs();i++)
  363. {
  364. const Pair* PP = P.GetPair(i);
  365. udword id0 = PP->id0;
  366. udword id1 = PP->id1;
  367. if(id0!=id1 && boxes[id0]->Intersect(*boxes[id1]))
  368. {
  369. mPairs.AddPair(id0, id1);
  370. }
  371. else ASSERT(0);
  372. }
  373. }
  374. return true;
  375. }
  376. bool SweepAndPrune::CheckListsIntegrity()
  377. {
  378. for(udword Axis=0;Axis<3;Axis++)
  379. {
  380. // Find list head
  381. SAP_EndPoint* Current = mList[Axis];
  382. while(Current->Previous) Current = Current->Previous;
  383. udword Nb = 0;
  384. SAP_EndPoint* Previous = null;
  385. while(Current)
  386. {
  387. Nb++;
  388. if(Previous)
  389. {
  390. ASSERT(Previous->Value <= Current->Value);
  391. if(Previous->Value > Current->Value) return false;
  392. }
  393. ASSERT(Current->Previous==Previous);
  394. if(Current->Previous!=Previous) return false;
  395. Previous = Current;
  396. Current = Current->Next;
  397. }
  398. ASSERT(Nb==mNbObjects*2);
  399. }
  400. return true;
  401. }
  402. inline_ BOOL Intersect(const AABB& a, const SAP_Box& b)
  403. {
  404. if(b.Max[0]->Value < a.GetMin(0) || a.GetMax(0) < b.Min[0]->Value
  405. || b.Max[1]->Value < a.GetMin(1) || a.GetMax(1) < b.Min[1]->Value
  406. || b.Max[2]->Value < a.GetMin(2) || a.GetMax(2) < b.Min[2]->Value) return FALSE;
  407. return TRUE;
  408. }
  409. bool SweepAndPrune::UpdateObject(udword i, const AABB& box)
  410. {
  411. for(udword Axis=0;Axis<3;Axis++)
  412. {
  413. // udword Base = (udword)&mList[Axis][0];
  414. // Update min
  415. {
  416. SAP_EndPoint* const CurrentMin = mBoxes[i].Min[Axis];
  417. ASSERT(!CurrentMin->IsMax());
  418. const float Limit = box.GetMin(Axis);
  419. if(Limit == CurrentMin->Value)
  420. {
  421. }
  422. else if(Limit < CurrentMin->Value)
  423. {
  424. CurrentMin->Value = Limit;
  425. // Min is moving left:
  426. SAP_EndPoint* NewPos = CurrentMin;
  427. ASSERT(NewPos);
  428. SAP_EndPoint* tmp;
  429. while((tmp = NewPos->Previous) && tmp->Value > Limit)
  430. {
  431. NewPos = tmp;
  432. if(NewPos->IsMax())
  433. {
  434. // Our min passed a max => start overlap
  435. //udword SortedIndex = (udword(CurrentMin) - Base)/sizeof(NS_EndPoint);
  436. const udword id0 = CurrentMin->GetBoxID();
  437. const udword id1 = NewPos->GetBoxID();
  438. if(id0!=id1 && Intersect(box, mBoxes[id1])) mPairs.AddPair(id0, id1);
  439. }
  440. }
  441. CurrentMin->InsertBefore(NewPos);
  442. }
  443. else// if(Limit > CurrentMin->Value)
  444. {
  445. CurrentMin->Value = Limit;
  446. // Min is moving right:
  447. SAP_EndPoint* NewPos = CurrentMin;
  448. ASSERT(NewPos);
  449. SAP_EndPoint* tmp;
  450. while((tmp = NewPos->Next) && tmp->Value < Limit)
  451. {
  452. NewPos = tmp;
  453. if(NewPos->IsMax())
  454. {
  455. // Our min passed a max => stop overlap
  456. const udword id0 = CurrentMin->GetBoxID();
  457. const udword id1 = NewPos->GetBoxID();
  458. if(id0!=id1) mPairs.RemovePair(id0, id1);
  459. }
  460. }
  461. CurrentMin->InsertAfter(NewPos);
  462. }
  463. }
  464. // Update max
  465. {
  466. SAP_EndPoint* const CurrentMax = mBoxes[i].Max[Axis];
  467. ASSERT(CurrentMax->IsMax());
  468. const float Limit = box.GetMax(Axis);
  469. if(Limit == CurrentMax->Value)
  470. {
  471. }
  472. else if(Limit > CurrentMax->Value)
  473. {
  474. CurrentMax->Value = Limit;
  475. // Max is moving right:
  476. SAP_EndPoint* NewPos = CurrentMax;
  477. ASSERT(NewPos);
  478. SAP_EndPoint* tmp;
  479. while((tmp = NewPos->Next) && tmp->Value < Limit)
  480. {
  481. NewPos = tmp;
  482. if(!NewPos->IsMax())
  483. {
  484. // Our max passed a min => start overlap
  485. const udword id0 = CurrentMax->GetBoxID();
  486. const udword id1 = NewPos->GetBoxID();
  487. if(id0!=id1 && Intersect(box, mBoxes[id1])) mPairs.AddPair(id0, id1);
  488. }
  489. }
  490. CurrentMax->InsertAfter(NewPos);
  491. }
  492. else// if(Limit < CurrentMax->Value)
  493. {
  494. CurrentMax->Value = Limit;
  495. // Max is moving left:
  496. SAP_EndPoint* NewPos = CurrentMax;
  497. ASSERT(NewPos);
  498. SAP_EndPoint* tmp;
  499. while((tmp = NewPos->Previous) && tmp->Value > Limit)
  500. {
  501. NewPos = tmp;
  502. if(!NewPos->IsMax())
  503. {
  504. // Our max passed a min => stop overlap
  505. const udword id0 = CurrentMax->GetBoxID();
  506. const udword id1 = NewPos->GetBoxID();
  507. if(id0!=id1) mPairs.RemovePair(id0, id1);
  508. }
  509. }
  510. CurrentMax->InsertBefore(NewPos);
  511. }
  512. }
  513. }
  514. return true;
  515. }