Vegastrike 0.5.1 rc1  1.0
Original sources for Vegastrike Evolved
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
OPC_SweepAndPrune.cpp
Go to the documentation of this file.
1 /*
3  * OPCODE - Optimized Collision Detection
4  * Copyright (C) 2001 Pierre Terdiman
5  * Homepage: http://www.codercorner.com/Opcode.htm
6  */
8 
10 
16 
19 // Precompiled Header
20 #include "Stdafx.h"
21 
22 
23 using namespace Opcode;
24 
25 inline_ void Sort(udword& id0, udword& id1)
26 {
27  if(id0>id1) Swap(id0, id1);
28 }
29 
31  {
32  public:
34  inline_ SAP_Element(udword id, SAP_Element* next) : mID(id), mNext(next) {}
36 
39  };
40 
42  {
43  public:
44  SAP_EndPoint* Min[3];
45  SAP_EndPoint* Max[3];
46  };
47 
49  {
50  public:
51  float Value; // Min or Max value
52  SAP_EndPoint* Previous; // Previous EndPoint whose Value is smaller than ours (or null)
53  SAP_EndPoint* Next; // Next EndPoint whose Value is greater than ours (or null)
54  udword Data; // Parent box ID *2 | MinMax flag
55 
56  inline_ void SetData(udword box_id, bool is_max) { Data = (box_id<<1)|(is_max?1:0); }
57  inline_ bool IsMax() const { return Data & 1; }
58  inline_ udword GetBoxID() const { return Data>>1; }
59 
61  {
62  if(this!=element && this!=element->Next)
63  {
64  // Remove
65  if(Previous) Previous->Next = Next;
66  if(Next) Next->Previous = Previous;
67 
68  // Insert
69  Next = element->Next;
70  if(Next) Next->Previous = this;
71 
72  element->Next = this;
73  Previous = element;
74  }
75  }
76 
78  {
79  if(this!=element && this!=element->Previous)
80  {
81  // Remove
82  if(Previous) Previous->Next = Next;
83  if(Next) Next->Previous = Previous;
84 
85  // Insert
86  Previous = element->Previous;
87  element->Previous = this;
88 
89  Next = element;
90  if(Previous) Previous->Next = this;
91  }
92  }
93  };
94 
95 
96 
97 
98 
99 
100 
101 
102 
103 
105 
110  mNbElements (0),
111  mNbUsedElements (0),
112  mElementPool (null),
113  mFirstFree (null),
114  mNbObjects (0),
115  mArray (null)
116 {
117 }
118 
120 
125 {
126  Release();
127 }
128 
129 void SAP_PairData::Release()
130 {
131  mNbElements = 0;
132  mNbUsedElements = 0;
133  mNbObjects = 0;
134  DELETEARRAY(mElementPool);
135  DELETEARRAY(mArray);
136 }
137 
139 
144 bool SAP_PairData::Init(udword nb_objects)
146 {
147  // Make sure everything has been released
148  Release();
149  if(!nb_objects) return false;
150 
151  mArray = new SAP_Element*[nb_objects];
152  CHECKALLOC(mArray);
153  ZeroMemory(mArray, nb_objects*sizeof(SAP_Element*));
154  mNbObjects = nb_objects;
155 
156  return true;
157 }
158 
160 
165 inline_ void Remap(SAP_Element*& element, udword delta)
167 {
168  if(element) element = (SAP_Element*)(uintptr_t(element) + delta);
169 }
170 
172 
179 SAP_Element* SAP_PairData::GetFreeElem(udword id, SAP_Element* next, udword* remap)
181 {
182  if(remap) *remap = 0;
183 
184  SAP_Element* FreeElem;
185  if(mFirstFree)
186  {
187  // Recycle
188  FreeElem = mFirstFree;
189  mFirstFree = mFirstFree->mNext; // First free = next free (or null)
190  }
191  else
192  {
193  if(mNbUsedElements==mNbElements)
194  {
195  // Resize
196  mNbElements = mNbElements ? (mNbElements<<1) : 2;
197 
198  SAP_Element* NewElems = new SAP_Element[mNbElements];
199 
200  if(mNbUsedElements) CopyMemory(NewElems, mElementPool, mNbUsedElements*sizeof(SAP_Element));
201 
202  // Remap everything
203  {
204  udword Delta = uintptr_t(NewElems) - uintptr_t(mElementPool);
205 
206  udword i;
207  for(i=0;i<mNbUsedElements;i++) Remap(NewElems[i].mNext, Delta);
208  for(i=0;i<mNbObjects;i++) Remap(mArray[i], Delta);
209 
210  Remap(mFirstFree, Delta);
211  Remap(next, Delta);
212 
213  if(remap) *remap = Delta;
214  }
215 
216  DELETEARRAY(mElementPool);
217  mElementPool = NewElems;
218  }
219 
220  FreeElem = &mElementPool[mNbUsedElements++];
221  }
222 
223  FreeElem->mID = id;
224  FreeElem->mNext = next;
225 
226  return FreeElem;
227 }
228 
230 
234 inline_ void SAP_PairData::FreeElem(SAP_Element* elem)
236 {
237  elem->mNext = mFirstFree; // Next free
238  mFirstFree = elem;
239 }
240 
241 // Add a pair to the set.
243 {
244  // Order the ids
245  Sort(id1, id2);
246 
247  OPASSERT(id1<mNbObjects);
248  if(id1>=mNbObjects) return;
249 
250  // Select the right list from "mArray".
251  SAP_Element* Current = mArray[id1];
252 
253  if(!Current)
254  {
255  // Empty slot => create new element
256  mArray[id1] = GetFreeElem(id2, null);
257  }
258  else if(Current->mID>id2)
259  {
260  // The list is not empty but all elements are greater than id2 => insert id2 in the front.
261  mArray[id1] = GetFreeElem(id2, mArray[id1]);
262  }
263  else
264  {
265  // Else find the correct location in the sorted list (ascending order) and insert id2 there.
266  while(Current->mNext)
267  {
268  if(Current->mNext->mID > id2) break;
269 
270  Current = Current->mNext;
271  }
272 
273  if(Current->mID==id2) return; // The pair already exists
274 
275 // Current->mNext = GetFreeElem(id2, Current->mNext);
276  udword Delta;
277  SAP_Element* E = GetFreeElem(id2, Current->mNext, &Delta);
278  if(Delta) Remap(Current, Delta);
279  Current->mNext = E;
280  }
281 }
282 
283 // Delete a pair from the set.
285 {
286  // Order the ids.
287  Sort(id1, id2);
288 
289  // Exit if the pair doesn't exist in the set
290  if(id1>=mNbObjects) return;
291 
292  // Otherwise, select the correct list.
293  SAP_Element* Current = mArray[id1];
294 
295  // If this list is empty, the pair doesn't exist.
296  if(!Current) return;
297 
298  // Otherwise, if id2 is the first element, delete it.
299  if(Current->mID==id2)
300  {
301  mArray[id1] = Current->mNext;
302  FreeElem(Current);
303  }
304  else
305  {
306  // If id2 is not the first element, start traversing the sorted list.
307  while(Current->mNext)
308  {
309  // If we have moved too far away without hitting id2, then the pair doesn't exist
310  if(Current->mNext->mID > id2) return;
311 
312  // Otherwise, delete id2.
313  if(Current->mNext->mID == id2)
314  {
315  SAP_Element* Temp = Current->mNext;
316  Current->mNext = Temp->mNext;
317  FreeElem(Temp);
318  return;
319  }
320  Current = Current->mNext;
321  }
322  }
323 }
324 
325 void SAP_PairData::DumpPairs(Pairs& pairs) const
326 {
327  // ### Ugly and slow
328  for(udword i=0;i<mNbObjects;i++)
329  {
330  SAP_Element* Current = mArray[i];
331  while(Current)
332  {
333  OPASSERT(Current->mID<mNbObjects);
334 
335  pairs.AddPair(i, Current->mID);
336  Current = Current->mNext;
337  }
338  }
339 }
340 
341 void SAP_PairData::DumpPairs(PairCallback callback, void* user_data) const
342 {
343  if(!callback) return;
344 
345  // ### Ugly and slow
346  for(udword i=0;i<mNbObjects;i++)
347  {
348  SAP_Element* Current = mArray[i];
349  while(Current)
350  {
351  OPASSERT(Current->mID<mNbObjects);
352 
353  if(!(callback)(i, Current->mID, user_data)) return;
354  Current = Current->mNext;
355  }
356  }
357 }
358 
359 
360 
361 
362 
363 
364 
365 
366 
367 
368 
369 
370 
371 
372 
373 
374 
375 
376 
377 
378 
379 
380 
381 
382 
383 
384 
385 
387 
392 {
393 }
394 
396 
401 {
402 }
403 
404 void SweepAndPrune::GetPairs(Pairs& pairs) const
405 {
406  mPairs.DumpPairs(pairs);
407 }
408 
409 void SweepAndPrune::GetPairs(PairCallback callback, void* user_data) const
410 {
411  mPairs.DumpPairs(callback, user_data);
412 }
413 
414 bool SweepAndPrune::Init(udword nb_objects, const AABB** boxes)
415 {
416  // 1) Create sorted lists
417  mNbObjects = nb_objects;
418 
419  mBoxes = new SAP_Box[nb_objects];
420 // for(udword i=0;i<nb_objects;i++) mBoxes[i].Box = *boxes[i];
421 
422  float* Data = new float[nb_objects*2];
423 
424  for(udword Axis=0;Axis<3;Axis++)
425  {
426  mList[Axis] = new SAP_EndPoint[nb_objects*2];
427 
428  udword i;
429  for(i=0;i<nb_objects;i++)
430  {
431  Data[i*2+0] = boxes[i]->GetMin(Axis);
432  Data[i*2+1] = boxes[i]->GetMax(Axis);
433  }
434  RadixSort RS;
435  const udword* Sorted = RS.Sort(Data, nb_objects*2).GetRanks();
436 
437  SAP_EndPoint* PreviousEndPoint = null;
438 
439  for(i=0;i<nb_objects*2;i++)
440  {
441  udword SortedIndex = *Sorted++;
442  float SortedCoord = Data[SortedIndex];
443  udword BoxIndex = SortedIndex>>1;
444 
445  OPASSERT(BoxIndex<nb_objects);
446 
447  SAP_EndPoint* CurrentEndPoint = &mList[Axis][SortedIndex];
448  CurrentEndPoint->Value = SortedCoord;
449 // CurrentEndPoint->IsMax = SortedIndex&1; // ### could be implicit ?
450 // CurrentEndPoint->ID = BoxIndex; // ### could be implicit ?
451  CurrentEndPoint->SetData(BoxIndex, SortedIndex&1); // ### could be implicit ?
452  CurrentEndPoint->Previous = PreviousEndPoint;
453  CurrentEndPoint->Next = null;
454  if(PreviousEndPoint) PreviousEndPoint->Next = CurrentEndPoint;
455 
456  if(CurrentEndPoint->IsMax()) mBoxes[BoxIndex].Max[Axis] = CurrentEndPoint;
457  else mBoxes[BoxIndex].Min[Axis] = CurrentEndPoint;
458 
459  PreviousEndPoint = CurrentEndPoint;
460  }
461  }
462 
463  DELETEARRAY(Data);
464 
465  CheckListsIntegrity();
466 
467  // 2) Quickly find starting pairs
468 
469  mPairs.Init(nb_objects);
470 
471  {
472  Pairs P;
473  CompleteBoxPruning(nb_objects, boxes, P, Axes(AXES_XZY));
474  for(udword i=0;i<P.GetNbPairs();i++)
475  {
476  const Pair* PP = P.GetPair(i);
477 
478  udword id0 = PP->id0;
479  udword id1 = PP->id1;
480 
481  if(id0!=id1 && boxes[id0]->Intersect(*boxes[id1]))
482  {
483  mPairs.AddPair(id0, id1);
484  }
485  else OPASSERT(0);
486  }
487  }
488 
489  return true;
490 }
491 
492 bool SweepAndPrune::CheckListsIntegrity()
493 {
494  for(udword Axis=0;Axis<3;Axis++)
495  {
496  // Find list head
497  SAP_EndPoint* Current = mList[Axis];
498  while(Current->Previous) Current = Current->Previous;
499 
500  udword Nb = 0;
501 
502  SAP_EndPoint* Previous = null;
503  while(Current)
504  {
505  Nb++;
506 
507  if(Previous)
508  {
509  OPASSERT(Previous->Value <= Current->Value);
510  if(Previous->Value > Current->Value) return false;
511  }
512 
513  OPASSERT(Current->Previous==Previous);
514  if(Current->Previous!=Previous) return false;
515 
516  Previous = Current;
517  Current = Current->Next;
518  }
519 
520  OPASSERT(Nb==mNbObjects*2);
521  }
522  return true;
523 }
524 
525 inline_ bool Intersect(const AABB& a, const SAP_Box& b)
526 {
527  if(b.Max[0]->Value < a.GetMin(0) || a.GetMax(0) < b.Min[0]->Value
528  || b.Max[1]->Value < a.GetMin(1) || a.GetMax(1) < b.Min[1]->Value
529  || b.Max[2]->Value < a.GetMin(2) || a.GetMax(2) < b.Min[2]->Value) return FALSE;
530 
531  return TRUE;
532 }
533 
534 
535 
537 {
538  for(udword Axis=0;Axis<3;Axis++)
539  {
540 // udword Base = (udword)&mList[Axis][0];
541 
542  // Update min
543  {
544  SAP_EndPoint* const CurrentMin = mBoxes[i].Min[Axis];
545  OPASSERT(!CurrentMin->IsMax());
546 
547  const float Limit = box.GetMin(Axis);
548  if(Limit == CurrentMin->Value)
549  {
550  }
551  else if(Limit < CurrentMin->Value)
552  {
553  CurrentMin->Value = Limit;
554 
555  // Min is moving left:
556  SAP_EndPoint* NewPos = CurrentMin;
557  OPASSERT(NewPos);
558 
559  SAP_EndPoint* tmp;
560  while((tmp = NewPos->Previous) && tmp->Value > Limit)
561  {
562  NewPos = tmp;
563 
564  if(NewPos->IsMax())
565  {
566  // Our min passed a max => start overlap
567  //udword SortedIndex = (udword(CurrentMin) - Base)/sizeof(NS_EndPoint);
568  const udword id0 = CurrentMin->GetBoxID();
569  const udword id1 = NewPos->GetBoxID();
570 
571  if(id0!=id1 && Intersect(box, mBoxes[id1])) mPairs.AddPair(id0, id1);
572  }
573  }
574 
575  CurrentMin->InsertBefore(NewPos);
576  }
577  else// if(Limit > CurrentMin->Value)
578  {
579  CurrentMin->Value = Limit;
580 
581  // Min is moving right:
582  SAP_EndPoint* NewPos = CurrentMin;
583  OPASSERT(NewPos);
584 
585  SAP_EndPoint* tmp;
586  while((tmp = NewPos->Next) && tmp->Value < Limit)
587  {
588  NewPos = tmp;
589 
590  if(NewPos->IsMax())
591  {
592  // Our min passed a max => stop overlap
593  const udword id0 = CurrentMin->GetBoxID();
594  const udword id1 = NewPos->GetBoxID();
595 
596  if(id0!=id1) mPairs.RemovePair(id0, id1);
597  }
598  }
599 
600  CurrentMin->InsertAfter(NewPos);
601  }
602  }
603 
604  // Update max
605  {
606  SAP_EndPoint* const CurrentMax = mBoxes[i].Max[Axis];
607  OPASSERT(CurrentMax->IsMax());
608 
609  const float Limit = box.GetMax(Axis);
610  if(Limit == CurrentMax->Value)
611  {
612  }
613  else if(Limit > CurrentMax->Value)
614  {
615  CurrentMax->Value = Limit;
616 
617  // Max is moving right:
618  SAP_EndPoint* NewPos = CurrentMax;
619  OPASSERT(NewPos);
620 
621  SAP_EndPoint* tmp;
622  while((tmp = NewPos->Next) && tmp->Value < Limit)
623  {
624  NewPos = tmp;
625 
626  if(!NewPos->IsMax())
627  {
628  // Our max passed a min => start overlap
629  const udword id0 = CurrentMax->GetBoxID();
630  const udword id1 = NewPos->GetBoxID();
631 
632  if(id0!=id1 && Intersect(box, mBoxes[id1])) mPairs.AddPair(id0, id1);
633  }
634  }
635 
636  CurrentMax->InsertAfter(NewPos);
637  }
638  else// if(Limit < CurrentMax->Value)
639  {
640  CurrentMax->Value = Limit;
641 
642  // Max is moving left:
643  SAP_EndPoint* NewPos = CurrentMax;
644  OPASSERT(NewPos);
645 
646  SAP_EndPoint* tmp;
647  while((tmp = NewPos->Previous) && tmp->Value > Limit)
648  {
649  NewPos = tmp;
650 
651  if(!NewPos->IsMax())
652  {
653  // Our max passed a min => stop overlap
654  const udword id0 = CurrentMax->GetBoxID();
655  const udword id1 = NewPos->GetBoxID();
656 
657  if(id0!=id1) mPairs.RemovePair(id0, id1);
658  }
659  }
660 
661  CurrentMax->InsertBefore(NewPos);
662  }
663  }
664  }
665 
666  return true;
667 }
668