1 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3 * OPCODE - Optimized Collision Detection
4 * Copyright (C) 2001 Pierre Terdiman
5 * Homepage: http://www.codercorner.com/Opcode.htm
7 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
9 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
11 * Contains an array-based version of the sweep-and-prune algorithm
12 * \file OPC_ArraySAP.cpp
13 * \author Pierre Terdiman
14 * \date December, 2, 2007
16 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
18 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
22 using namespace Opcode;
24 //#include "SAP_Utils.h"
26 #define INVALID_USER_ID 0xffff
28 inline_ void Sort(uword& id0, uword& id1) { if(id0>id1) TSwap(id0, id1); }
29 inline_ void Sort(uword& id0, uword& id1, const void*& obj0, const void*& obj1) { if(id0>id1) { TSwap(id0, id1); TSwap(obj0, obj1); } }
32 struct Opcode::IAABB : public Allocateable
41 inline_ udword GetMin(udword i) const { return (&mMinX)[i]; }
42 inline_ udword GetMax(udword i) const { return (&mMaxX)[i]; }
48 - already sorted for batch create?
49 - better axis selection batch create
52 //#define USE_WORDS // Use words or dwords for box indices. Words save memory but seriously limit the max number of objects in the SAP.
55 #define PAIR_USER_DATA
56 #define USE_OVERLAP_TEST_ON_REMOVES // "Useless" but faster overall because seriously reduces number of calls (from ~10000 to ~3 sometimes!)
57 #define RELEASE_ON_RESET // Release memory instead of just doing a reset
59 #include "OPC_ArraySAP.h"
61 //#include "SAP_PairManager.h"
62 //#include "SAP_PairManager.cpp"
65 inline_ udword Hash(uword id0, uword id1) { return Hash32Bits_1( udword(id0)|(udword(id1)<<16) ); }
66 inline_ bool DifferentPair(const ASAP_Pair& p, uword id0, uword id1) { return (id0!=p.id0) || (id1!=p.id1); }
68 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
70 ASAP_PairManager::ASAP_PairManager() :
80 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
82 ASAP_PairManager::~ASAP_PairManager()
87 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
89 void ASAP_PairManager::Purge()
92 ICE_FREE(mActivePairs);
99 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
101 const ASAP_Pair* ASAP_PairManager::FindPair(uword id0, uword id1) const
103 if(!mHashTable) return null; // Nothing has been allocated yet
108 // Compute hash value for this pair
109 udword HashValue = Hash(id0, id1) & mMask;
111 // Look for it in the table
112 udword Offset = mHashTable[HashValue];
113 while(Offset!=INVALID_ID && DifferentPair(mActivePairs[Offset], id0, id1))
115 ASSERT(mActivePairs[Offset].id0!=INVALID_USER_ID);
116 Offset = mNext[Offset]; // Better to have a separate array for this
118 if(Offset==INVALID_ID) return null;
119 ASSERT(Offset<mNbActivePairs);
120 // Match mActivePairs[Offset] => the pair is persistent
121 return &mActivePairs[Offset];
124 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
126 // Internal version saving hash computation
127 inline_ ASAP_Pair* ASAP_PairManager::FindPair(uword id0, uword id1, udword hash_value) const
129 if(!mHashTable) return null; // Nothing has been allocated yet
131 // Look for it in the table
132 udword Offset = mHashTable[hash_value];
133 while(Offset!=INVALID_ID && DifferentPair(mActivePairs[Offset], id0, id1))
135 ASSERT(mActivePairs[Offset].id0!=INVALID_USER_ID);
136 Offset = mNext[Offset]; // Better to have a separate array for this
138 if(Offset==INVALID_ID) return null;
139 ASSERT(Offset<mNbActivePairs);
140 // Match mActivePairs[Offset] => the pair is persistent
141 return &mActivePairs[Offset];
144 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
146 const ASAP_Pair* ASAP_PairManager::AddPair(uword id0, uword id1, const void* object0, const void* object1)
149 Sort(id0, id1, object0, object1);
151 udword HashValue = Hash(id0, id1) & mMask;
153 ASAP_Pair* P = FindPair(id0, id1, HashValue);
156 return P; // Persistent pair
159 // This is a new pair
160 if(mNbActivePairs >= mHashSize)
163 mHashSize = NextPowerOfTwo(mNbActivePairs+1);
168 // Recompute hash value with new hash size
169 HashValue = Hash(id0, id1) & mMask;
172 ASAP_Pair* p = &mActivePairs[mNbActivePairs];
173 p->id0 = id0; // ### CMOVs would be nice here
175 p->object0 = object0;
176 p->object1 = object1;
178 mNext[mNbActivePairs] = mHashTable[HashValue];
179 mHashTable[HashValue] = mNbActivePairs++;
183 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
185 void ASAP_PairManager::RemovePair(uword id0, uword id1, udword hash_value, udword pair_index)
187 // Walk the hash table to fix mNext
188 udword Offset = mHashTable[hash_value];
189 ASSERT(Offset!=INVALID_ID);
191 udword Previous=INVALID_ID;
192 while(Offset!=pair_index)
195 Offset = mNext[Offset];
199 if(Previous!=INVALID_ID)
201 ASSERT(mNext[Previous]==pair_index);
202 mNext[Previous] = mNext[pair_index];
204 // else we were the first
205 else mHashTable[hash_value] = mNext[pair_index];
206 // we're now free to reuse mNext[PairIndex] without breaking the list
209 mNext[pair_index]=INVALID_ID;
216 // 1) Remove last pair
217 const udword LastPairIndex = mNbActivePairs-1;
218 if(LastPairIndex==pair_index)
224 const ASAP_Pair* Last = &mActivePairs[LastPairIndex];
225 const udword LastHashValue = Hash(Last->id0, Last->id1) & mMask;
227 // Walk the hash table to fix mNext
228 udword Offset = mHashTable[LastHashValue];
229 ASSERT(Offset!=INVALID_ID);
231 udword Previous=INVALID_ID;
232 while(Offset!=LastPairIndex)
235 Offset = mNext[Offset];
239 if(Previous!=INVALID_ID)
241 ASSERT(mNext[Previous]==LastPairIndex);
242 mNext[Previous] = mNext[LastPairIndex];
244 // else we were the first
245 else mHashTable[LastHashValue] = mNext[LastPairIndex];
246 // we're now free to reuse mNext[LastPairIndex] without breaking the list
249 mNext[LastPairIndex]=INVALID_ID;
252 // Don't invalidate entry since we're going to shrink the array
254 // 2) Re-insert in free slot
255 mActivePairs[pair_index] = mActivePairs[LastPairIndex];
257 ASSERT(mNext[pair_index]==INVALID_ID);
259 mNext[pair_index] = mHashTable[LastHashValue];
260 mHashTable[LastHashValue] = pair_index;
267 bool ASAP_PairManager::RemovePair(uword id0, uword id1)
272 const udword HashValue = Hash(id0, id1) & mMask;
273 const ASAP_Pair* P = FindPair(id0, id1, HashValue);
278 RemovePair(id0, id1, HashValue, GetPairIndex(P));
284 bool ASAP_PairManager::RemovePairs(const BitArray& array)
287 while(i<mNbActivePairs)
289 const uword id0 = mActivePairs[i].id0;
290 const uword id1 = mActivePairs[i].id1;
291 if(array.IsSet(id0) || array.IsSet(id1))
293 const udword HashValue = Hash(id0, id1) & mMask;
294 RemovePair(id0, id1, HashValue, i);
302 void ASAP_PairManager::ShrinkMemory()
304 // Check correct memory against actually used memory
305 const udword CorrectHashSize = NextPowerOfTwo(mNbActivePairs);
306 if(mHashSize==CorrectHashSize) return;
308 // Reduce memory used
309 mHashSize = CorrectHashSize;
315 void ASAP_PairManager::ReallocPairs()
317 ICE_FREE(mHashTable);
318 mHashTable = (udword*)ICE_ALLOC(mHashSize*sizeof(udword));
319 StoreDwords(mHashTable, mHashSize, INVALID_ID);
321 // Get some bytes for new entries
322 ASAP_Pair* NewPairs = (ASAP_Pair*)ICE_ALLOC(mHashSize * sizeof(ASAP_Pair)); ASSERT(NewPairs);
323 udword* NewNext = (udword*)ICE_ALLOC(mHashSize * sizeof(udword)); ASSERT(NewNext);
325 // Copy old data if needed
326 if(mNbActivePairs) CopyMemory(NewPairs, mActivePairs, mNbActivePairs*sizeof(ASAP_Pair));
327 // ### check it's actually needed... probably only for pairs whose hash value was cut by the and
328 // yeah, since Hash(id0, id1) is a constant
329 // However it might not be needed to recompute them => only less efficient but still ok
330 for(udword i=0;i<mNbActivePairs;i++)
332 const udword HashValue = Hash(mActivePairs[i].id0, mActivePairs[i].id1) & mMask; // New hash value with new mask
333 NewNext[i] = mHashTable[HashValue];
334 mHashTable[HashValue] = i;
339 ICE_FREE(mActivePairs);
341 // Assign new pointer
342 mActivePairs = NewPairs;
348 typedef uword IndexType;
349 #define INVALID_INDEX 0xffff
351 typedef udword IndexType;
352 #define INVALID_INDEX 0xffffffff
356 typedef udword ValType;
357 typedef IAABB SAP_AABB;
359 typedef float ValType;
360 typedef AABB SAP_AABB;
363 struct Opcode::CreateData
369 class Opcode::ASAP_EndPoint : public Allocateable
372 inline_ ASAP_EndPoint() {}
373 inline_ ~ASAP_EndPoint() {}
375 ValType mValue; // Min or Max value
376 udword mData; // Parent box | MinMax flag
379 inline_ bool IsSentinel() const { return (mData&~3)==0xfffffffc; }
381 inline_ void SetData(ValType v, udword owner_box_id, BOOL is_max)
384 mData = owner_box_id<<2;
385 if(is_max) mData |= 3;
387 inline_ BOOL IsMax() const { return mData & 3; }
388 inline_ udword GetOwner() const { return mData>>2; }
391 class Opcode::ASAP_Box : public Allocateable
394 inline_ ASAP_Box() {}
395 inline_ ~ASAP_Box() {}
402 inline_ void SetInvalid() { mMin[0]=INVALID_INDEX; }
403 inline_ bool IsValid() const { return mMin[0]!=INVALID_INDEX; }
405 inline_ ValType GetMaxValue(udword i, const ASAP_EndPoint* base) const
407 return base[mMax[i]].mValue;
410 inline_ ValType GetMinValue(udword i, const ASAP_EndPoint* base) const
412 return base[mMin[i]].mValue;
415 bool HasBeenInserted() const
417 assert(mMin[0]!=INVALID_INDEX);
418 assert(mMax[0]!=INVALID_INDEX);
419 assert(mMin[1]!=INVALID_INDEX);
420 assert(mMax[1]!=INVALID_INDEX);
421 assert(mMin[2]!=INVALID_INDEX);
422 assert(mMax[2]!=INVALID_INDEX);
428 inline_ BOOL Intersect1D_Min(const SAP_AABB& a, const ASAP_Box& b, const ASAP_EndPoint* const base, udword axis)
430 if(b.GetMaxValue(axis, base) < a.GetMin(axis))
435 inline_ BOOL Intersect2D(const ASAP_Box& c, const ASAP_Box& b, udword axis1, udword axis2)
437 if( b.mMax[axis1] < c.mMin[axis1] || c.mMax[axis1] < b.mMin[axis1]
438 || b.mMax[axis2] < c.mMin[axis2] || c.mMax[axis2] < b.mMin[axis2]) return FALSE;
448 mEndPoints[0] = mEndPoints[1] = mEndPoints[2] = null;
449 mFirstFree = INVALID_ID;
452 ArraySAP::~ArraySAP()
457 for(udword i=0;i<3;i++)
459 DELETEARRAY(mEndPoints[i]);
463 void ArraySAP::ResizeBoxArray()
465 const udword NewMaxBoxes = mMaxNbBoxes ? mMaxNbBoxes*2 : 64;
467 ASAP_Box* NewBoxes = ICE_NEW_TMP(ASAP_Box)[NewMaxBoxes];
468 const udword NbSentinels=2;
469 ASAP_EndPoint* NewEndPointsX = ICE_NEW_TMP(ASAP_EndPoint)[NewMaxBoxes*2+NbSentinels];
470 ASAP_EndPoint* NewEndPointsY = ICE_NEW_TMP(ASAP_EndPoint)[NewMaxBoxes*2+NbSentinels];
471 ASAP_EndPoint* NewEndPointsZ = ICE_NEW_TMP(ASAP_EndPoint)[NewMaxBoxes*2+NbSentinels];
475 CopyMemory(NewBoxes, mBoxes, sizeof(ASAP_Box)*mNbBoxes);
476 CopyMemory(NewEndPointsX, mEndPoints[0], sizeof(ASAP_EndPoint)*(mNbBoxes*2+NbSentinels));
477 CopyMemory(NewEndPointsY, mEndPoints[1], sizeof(ASAP_EndPoint)*(mNbBoxes*2+NbSentinels));
478 CopyMemory(NewEndPointsZ, mEndPoints[2], sizeof(ASAP_EndPoint)*(mNbBoxes*2+NbSentinels));
482 // Initialize sentinels
484 const udword Min = EncodeFloat(MIN_FLOAT);
485 const udword Max = EncodeFloat(MAX_FLOAT);
487 const float Min = MIN_FLOAT;
488 const float Max = MAX_FLOAT;
490 NewEndPointsX[0].SetData(Min, INVALID_INDEX, FALSE);
491 NewEndPointsX[1].SetData(Max, INVALID_INDEX, TRUE);
492 NewEndPointsY[0].SetData(Min, INVALID_INDEX, FALSE);
493 NewEndPointsY[1].SetData(Max, INVALID_INDEX, TRUE);
494 NewEndPointsZ[0].SetData(Min, INVALID_INDEX, FALSE);
495 NewEndPointsZ[1].SetData(Max, INVALID_INDEX, TRUE);
498 DELETEARRAY(mEndPoints[2]);
499 DELETEARRAY(mEndPoints[1]);
500 DELETEARRAY(mEndPoints[0]);
502 mEndPoints[0] = NewEndPointsX;
503 mEndPoints[1] = NewEndPointsY;
504 mEndPoints[2] = NewEndPointsZ;
506 mMaxNbBoxes = NewMaxBoxes;
509 inline_ BOOL Intersect(const IAABB& a, const IAABB& b, udword axis)
511 if(b.GetMax(axis) < a.GetMin(axis) || a.GetMax(axis) < b.GetMin(axis)) return FALSE;
515 // ### TODO: the sorts here might be useless, as the values have been sorted already
516 bool ArraySAP::CompleteBoxPruning2(udword nb, const IAABB* array, const Axes& axes, const CreateData* batched)
519 if(!nb || !array) return false;
522 const udword Axis0 = axes.mAxis0;
523 const udword Axis1 = axes.mAxis1;
524 const udword Axis2 = axes.mAxis2;
526 // Allocate some temporary data
527 udword* PosList = (udword*)ICE_ALLOC_TMP(sizeof(udword)*(nb+1));
529 // 1) Build main list using the primary axis
530 for(udword i=0;i<nb;i++) PosList[i] = array[i].GetMin(Axis0);
531 //PosList[nb++] = ConvertToSortable(MAX_FLOAT);
534 // static RadixSort r;
538 // const udword* Sorted = RS->Sort(PosList, nb, RADIX_SIGNED).GetRanks();
539 const udword* Sorted = RS->Sort(PosList, nb, RADIX_UNSIGNED).GetRanks();
542 const udword* const LastSorted = &Sorted[nb];
543 const udword* RunningAddress = Sorted;
544 udword Index0, Index1;
545 while(RunningAddress<LastSorted && Sorted<LastSorted)
549 while(RunningAddress<LastSorted && PosList[*RunningAddress++]<PosList[Index0]);
550 // while(PosList[*RunningAddress++]<PosList[Index0]);
552 if(RunningAddress<LastSorted)
554 const udword* RunningAddress2 = RunningAddress;
556 while(RunningAddress2<LastSorted && PosList[Index1 = *RunningAddress2++]<=array[Index0].GetMax(Axis0))
557 // while(PosList[Index1 = *RunningAddress2++]<=(udword)ConvertToSortable(array[Index0].GetMax(Axis0)))
559 if(Intersect(array[Index0], array[Index1], Axis1))
561 if(Intersect(array[Index0], array[Index1], Axis2))
563 const ASAP_Box* Box0 = mBoxes + batched[Index0].mHandle;
564 const ASAP_Box* Box1 = mBoxes + batched[Index1].mHandle;
565 AddPair(Box0->mObject, Box1->mObject, Box0->mGUID, Box1->mGUID);
576 bool ArraySAP::BipartiteBoxPruning2(udword nb0, const IAABB* array0, udword nb1, const IAABB* array1, const Axes& axes, const CreateData* batched, const udword* box_indices)
579 if(!nb0 || !array0 || !nb1 || !array1) return false;
582 const udword Axis0 = axes.mAxis0;
583 const udword Axis1 = axes.mAxis1;
584 const udword Axis2 = axes.mAxis2;
586 // Allocate some temporary data
587 udword* MinPosList0 = (udword*)ICE_ALLOC_TMP(sizeof(udword)*nb0);
588 udword* MinPosList1 = (udword*)ICE_ALLOC_TMP(sizeof(udword)*nb1);
590 // 1) Build main lists using the primary axis
591 for(udword i=0;i<nb0;i++) MinPosList0[i] = array0[i].GetMin(Axis0);
592 for(udword i=0;i<nb1;i++) MinPosList1[i] = array1[i].GetMin(Axis0);
595 /* static RadixSort r0;
597 RadixSort* RS0 = &r0;
598 RadixSort* RS1 = &r1;*/
601 RadixSort* RS0 = &r0;
602 RadixSort* RS1 = &r1;
604 const udword* Sorted0 = RS0->Sort(MinPosList0, nb0, RADIX_UNSIGNED).GetRanks();
605 const udword* Sorted1 = RS1->Sort(MinPosList1, nb1, RADIX_UNSIGNED).GetRanks();
607 // 3) Prune the lists
608 udword Index0, Index1;
610 const udword* const LastSorted0 = &Sorted0[nb0];
611 const udword* const LastSorted1 = &Sorted1[nb1];
612 const udword* RunningAddress0 = Sorted0;
613 const udword* RunningAddress1 = Sorted1;
615 while(RunningAddress1<LastSorted1 && Sorted0<LastSorted0)
619 while(RunningAddress1<LastSorted1 && MinPosList1[*RunningAddress1]<MinPosList0[Index0]) RunningAddress1++;
621 const udword* RunningAddress2_1 = RunningAddress1;
623 while(RunningAddress2_1<LastSorted1 && MinPosList1[Index1 = *RunningAddress2_1++]<=array0[Index0].GetMax(Axis0))
625 if(Intersect(array0[Index0], array1[Index1], Axis1))
627 if(Intersect(array0[Index0], array1[Index1], Axis2))
629 const ASAP_Box* Box0 = mBoxes + batched[Index0].mHandle;
630 const ASAP_Box* Box1 = mBoxes + box_indices[Index1];
631 AddPair(Box0->mObject, Box1->mObject, Box0->mGUID, Box1->mGUID);
639 while(RunningAddress0<LastSorted0 && Sorted1<LastSorted1)
643 while(RunningAddress0<LastSorted0 && MinPosList0[*RunningAddress0]<=MinPosList1[Index0]) RunningAddress0++;
645 const udword* RunningAddress2_0 = RunningAddress0;
647 while(RunningAddress2_0<LastSorted0 && MinPosList0[Index1 = *RunningAddress2_0++]<=array1[Index0].GetMax(Axis0))
649 if(Intersect(array0[Index1], array1[Index0], Axis1))
651 if(Intersect(array0[Index1], array1[Index0], Axis2))
653 const ASAP_Box* Box0 = mBoxes + batched[Index1].mHandle;
654 const ASAP_Box* Box1 = mBoxes + box_indices[Index0];
655 AddPair(Box0->mObject, Box1->mObject, Box0->mGUID, Box1->mGUID);
662 ICE_FREE(MinPosList0);
663 ICE_FREE(MinPosList1);
667 udword ArraySAP::AddObject(void* object, uword guid, const AABB& box)
669 assert(!(size_t(object)&3)); // We will use the 2 LSBs
672 int a = sizeof(ASAP_Box); // 32
673 int b = sizeof(ASAP_EndPoint); // 8
677 if(mFirstFree!=INVALID_ID)
679 BoxIndex = mFirstFree;
680 mFirstFree = mBoxes[BoxIndex].mGUID;
684 if(mNbBoxes==mMaxNbBoxes)
689 ASAP_Box* Box = &mBoxes[BoxIndex];
691 Box->mObject = object;
693 for(udword i=0;i<3;i++)
695 Box->mMin[i] = INVALID_INDEX;
696 Box->mMax[i] = INVALID_INDEX;
701 CreateData* CD = (CreateData*)mCreated.Reserve(sizeof(CreateData)/sizeof(udword));
702 CD->mHandle = BoxIndex;
708 void ArraySAP::InsertEndPoints(udword axis, const ASAP_EndPoint* end_points, udword nb_endpoints)
710 ASAP_EndPoint* const BaseEP = mEndPoints[axis];
712 const udword OldSize = mNbBoxes*2 - nb_endpoints;
713 const udword NewSize = mNbBoxes*2;
715 BaseEP[NewSize + 1] = BaseEP[OldSize + 1];
717 sdword WriteIdx = NewSize;
718 udword CurrInsIdx = 0;
720 const ASAP_EndPoint* First = &BaseEP[0];
721 const ASAP_EndPoint* Current = &BaseEP[OldSize];
722 while(Current>=First)
724 const ASAP_EndPoint& Src = *Current;
725 const ASAP_EndPoint& Ins = end_points[CurrInsIdx];
727 // We need to make sure we insert maxs before mins to handle exactly equal endpoints correctly
728 const bool ShouldInsert = Ins.IsMax() ? (Src.mValue <= Ins.mValue) : (Src.mValue < Ins.mValue);
730 const ASAP_EndPoint& Moved = ShouldInsert ? Ins : Src;
731 BaseEP[WriteIdx] = Moved;
732 mBoxes[Moved.GetOwner()].mMin[axis + Moved.IsMax()] = WriteIdx--;
737 if(CurrInsIdx >= nb_endpoints)
738 break;//we just inserted the last endpoint
747 void ArraySAP::BatchCreate()
749 udword NbBatched = mCreated.GetNbEntries();
750 if(!NbBatched) return; // Early-exit if no object has been created
751 NbBatched /= sizeof(CreateData)/sizeof(udword);
752 const CreateData* Batched = (const CreateData*)mCreated.GetEntries();
756 const udword NbEndPoints = NbBatched*2;
757 ASAP_EndPoint* NewEPSorted = ICE_NEW_TMP(ASAP_EndPoint)[NbEndPoints];
758 ASAP_EndPoint* Buffer = (ASAP_EndPoint*)ICE_ALLOC_TMP(sizeof(ASAP_EndPoint)*NbEndPoints);
761 for(udword Axis=0;Axis<3;Axis++)
763 for(udword i=0;i<NbBatched;i++)
765 const udword BoxIndex = (udword)Batched[i].mHandle;
766 assert(mBoxes[BoxIndex].mMin[Axis]==INVALID_INDEX);
767 assert(mBoxes[BoxIndex].mMax[Axis]==INVALID_INDEX);
769 const float MinValue = Batched[i].mBox.GetMin(Axis);
770 const float MaxValue = Batched[i].mBox.GetMax(Axis);
772 NewEPSorted[i*2+0].SetData(EncodeFloat(MinValue), BoxIndex, FALSE);
773 NewEPSorted[i*2+1].SetData(EncodeFloat(MaxValue), BoxIndex, TRUE);
776 // Sort endpoints backwards
778 udword* Keys = (udword*)Buffer;
779 for(udword i=0;i<NbEndPoints;i++)
780 Keys[i] = NewEPSorted[i].mValue;
782 const udword* Sorted = RS.Sort(Keys, NbEndPoints, RADIX_UNSIGNED).GetRanks();
784 for(udword i=0;i<NbEndPoints;i++)
785 Buffer[i] = NewEPSorted[Sorted[NbEndPoints-1-i]];
788 InsertEndPoints(Axis, Buffer, NbEndPoints);
792 DELETEARRAY(NewEPSorted);
796 for(udword i=0;i<NbBatched;i++)
798 udword BoxIndex = (udword)Batched[i].mHandle;
799 ASAP_Box* Box = mBoxes + BoxIndex;
800 assert(Box->HasBeenInserted());
802 for(udword i=0;i<mNbBoxes*2+1;i++)
804 assert(mEndPoints[0][i].mValue <= mEndPoints[0][i+1].mValue);
805 assert(mEndPoints[1][i].mValue <= mEndPoints[1][i+1].mValue);
806 assert(mEndPoints[2][i].mValue <= mEndPoints[2][i+1].mValue);
812 BitArray BA(mMaxNbBoxes);
814 // Using box-pruning on array indices....
815 IAABB* NewBoxes = ICE_NEW_TMP(IAABB)[NbBatched];
816 for(udword i=0;i<NbBatched;i++)
818 const ASAP_Box* Box = mBoxes + (udword)Batched[i].mHandle;
819 assert((udword)Batched[i].mHandle<mMaxNbBoxes);
820 BA.SetBit((udword)Batched[i].mHandle);
821 NewBoxes[i].mMinX = Box->mMin[0];
822 NewBoxes[i].mMaxX = Box->mMax[0];
823 NewBoxes[i].mMinY = Box->mMin[1];
824 NewBoxes[i].mMaxY = Box->mMax[1];
825 NewBoxes[i].mMinZ = Box->mMin[2];
826 NewBoxes[i].mMaxZ = Box->mMax[2];
829 CompleteBoxPruning2(NbBatched, NewBoxes, Axes(AXES_XZY), Batched);
831 // the old boxes are not the first ones in the array
833 const udword NbOldBoxes = mNbBoxes - NbBatched;
836 IAABB* OldBoxes = ICE_NEW_TMP(IAABB)[NbOldBoxes];
837 udword* OldBoxesIndices = (udword*)ICE_ALLOC_TMP(sizeof(udword)*NbOldBoxes);
846 const ASAP_Box* Box = mBoxes + i;
850 OldBoxesIndices[Offset] = i;
852 OldBoxes[Offset].mMinX = Box->mMin[0];
853 OldBoxes[Offset].mMaxX = Box->mMax[0];
854 OldBoxes[Offset].mMinY = Box->mMin[1];
855 OldBoxes[Offset].mMaxY = Box->mMax[1];
856 OldBoxes[Offset].mMinZ = Box->mMin[2];
857 OldBoxes[Offset].mMaxZ = Box->mMax[2];
863 // assert(i==NbOldBoxes);
864 BipartiteBoxPruning2(NbBatched, NewBoxes, Offset, OldBoxes, Axes(AXES_XZY), Batched, OldBoxesIndices);
866 ICE_FREE(OldBoxesIndices);
867 DELETEARRAY(OldBoxes);
869 DELETEARRAY(NewBoxes);
871 #ifdef RELEASE_ON_RESET
876 void ArraySAP::BatchRemove()
878 udword NbRemoved = mRemoved.GetNbEntries();
879 if(!NbRemoved) return; // Early-exit if no object has been removed
880 const udword* Removed = mRemoved.GetEntries();
883 for(udword Axis=0;Axis<3;Axis++)
885 ASAP_EndPoint* const BaseEP = mEndPoints[Axis];
886 udword MinMinIndex = MAX_UDWORD;
887 for(udword i=0;i<NbRemoved;i++)
889 assert(Removed[i]<mMaxNbBoxes);
890 const ASAP_Box* RemovedObject = mBoxes + Removed[i];
891 const udword MinIndex = RemovedObject->mMin[Axis];
892 assert(MinIndex<mMaxNbBoxes*2+2);
893 const udword MaxIndex = RemovedObject->mMax[Axis];
894 assert(MaxIndex<mMaxNbBoxes*2+2);
895 assert(BaseEP[MinIndex].GetOwner()==Removed[i]);
896 assert(BaseEP[MaxIndex].GetOwner()==Removed[i]);
897 BaseEP[MinIndex].mData = 0xfffffffe;
898 BaseEP[MaxIndex].mData = 0xfffffffe;
899 if(MinIndex<MinMinIndex) MinMinIndex = MinIndex;
902 udword ReadIndex = MinMinIndex;
903 udword DestIndex = MinMinIndex;
904 const udword Limit = mNbBoxes*2+2;
905 while(ReadIndex!=Limit)
907 while(ReadIndex!=Limit && BaseEP[ReadIndex].mData == 0xfffffffe)
913 if(ReadIndex!=DestIndex)
915 BaseEP[DestIndex] = BaseEP[ReadIndex];
916 assert(BaseEP[DestIndex].mData != 0xfffffffe);
918 if(!BaseEP[DestIndex].IsSentinel())
920 udword BoxOwner = BaseEP[DestIndex].GetOwner();
921 assert(BoxOwner<mMaxNbBoxes);
922 ASAP_Box* Box = mBoxes + BoxOwner;
924 Box->mMin[Axis + BaseEP[DestIndex].IsMax()] = DestIndex;
934 const udword Saved = NbRemoved;
937 udword Index = *Removed++;
938 assert(Index<mMaxNbBoxes);
940 ASAP_Box* Object = mBoxes + Index;
941 assert(Object->mGUID < 65536);
942 BA.SetBit(Object->mGUID);
944 Object->mGUID = mFirstFree;
945 // Object->mObject = null; // ###########
946 Object->SetInvalid();
950 mPairs.RemovePairs(BA);
952 #ifdef RELEASE_ON_RESET
957 bool ArraySAP::RemoveObject(udword handle)
959 mRemoved.Add(handle);
964 bool ArraySAP::UpdateObject(udword handle, const AABB& box_)
966 bool ArraySAP::UpdateObject(udword handle, const AABB& box)
969 const ASAP_Box* Object = mBoxes + handle;
970 assert(Object->HasBeenInserted());
971 const void* UserObject = Object->mObject;
972 const udword UserGUID = Object->mGUID;
976 box.mMinX = EncodeFloat(box_.GetMin(0));
977 box.mMinY = EncodeFloat(box_.GetMin(1));
978 box.mMinZ = EncodeFloat(box_.GetMin(2));
979 box.mMaxX = EncodeFloat(box_.GetMax(0));
980 box.mMaxY = EncodeFloat(box_.GetMax(1));
981 box.mMaxZ = EncodeFloat(box_.GetMax(2));
984 for(udword Axis=0;Axis<3;Axis++)
986 const udword Axis1 = (1 << Axis) & 3;
987 const udword Axis2 = (1 << Axis1) & 3;
989 ASAP_EndPoint* const BaseEP = mEndPoints[Axis];
993 ASAP_EndPoint* CurrentMin = BaseEP + Object->mMin[Axis];
994 ASSERT(!CurrentMin->IsMax());
996 const ValType Limit = box.GetMin(Axis);
997 if(Limit < CurrentMin->mValue)
999 CurrentMin->mValue = Limit;
1001 // Min is moving left:
1002 ASAP_EndPoint Saved = *CurrentMin;
1003 udword EPIndex = (size_t(CurrentMin) - size_t(BaseEP))/sizeof(ASAP_EndPoint);
1004 const udword SavedIndex = EPIndex;
1006 while((--CurrentMin)->mValue > Limit)
1009 _prefetch(CurrentMin-1);
1011 ASAP_Box* id1 = mBoxes + CurrentMin->GetOwner();
1012 const BOOL IsMax = CurrentMin->IsMax();
1015 // Our min passed a max => start overlap
1017 && Intersect2D(*Object, *id1, Axis1, Axis2)
1018 && Intersect1D_Min(box, *id1, BaseEP, Axis)
1020 AddPair(UserObject, id1->mObject, UserGUID, id1->mGUID);
1023 id1->mMin[Axis + IsMax] = EPIndex--;
1024 *(CurrentMin+1) = *CurrentMin;
1027 if(SavedIndex!=EPIndex)
1029 mBoxes[Saved.GetOwner()].mMin[Axis + Saved.IsMax()] = EPIndex;
1030 BaseEP[EPIndex] = Saved;
1033 else if(Limit > CurrentMin->mValue)
1035 CurrentMin->mValue = Limit;
1037 // Min is moving right:
1038 ASAP_EndPoint Saved = *CurrentMin;
1039 udword EPIndex = (size_t(CurrentMin) - size_t(BaseEP))/sizeof(ASAP_EndPoint);
1040 const udword SavedIndex = EPIndex;
1042 while((++CurrentMin)->mValue < Limit)
1045 _prefetch(CurrentMin+1);
1047 ASAP_Box* id1 = mBoxes + CurrentMin->GetOwner();
1048 const BOOL IsMax = CurrentMin->IsMax();
1051 // Our min passed a max => stop overlap
1053 #ifdef USE_OVERLAP_TEST_ON_REMOVES
1054 && Intersect2D(*Object, *id1, Axis1, Axis2)
1057 RemovePair(UserObject, id1->mObject, UserGUID, id1->mGUID);
1060 id1->mMin[Axis + IsMax] = EPIndex++;
1061 *(CurrentMin-1) = *CurrentMin;
1064 if(SavedIndex!=EPIndex)
1066 mBoxes[Saved.GetOwner()].mMin[Axis + Saved.IsMax()] = EPIndex;
1067 BaseEP[EPIndex] = Saved;
1074 ASAP_EndPoint* CurrentMax = BaseEP + Object->mMax[Axis];
1075 ASSERT(CurrentMax->IsMax());
1077 const ValType Limit = box.GetMax(Axis);
1078 if(Limit > CurrentMax->mValue)
1080 CurrentMax->mValue = Limit;
1082 // Max is moving right:
1083 ASAP_EndPoint Saved = *CurrentMax;
1084 udword EPIndex = (size_t(CurrentMax) - size_t(BaseEP))/sizeof(ASAP_EndPoint);
1085 const udword SavedIndex = EPIndex;
1087 while((++CurrentMax)->mValue < Limit)
1090 _prefetch(CurrentMax+1);
1092 ASAP_Box* id1 = mBoxes + CurrentMax->GetOwner();
1093 const BOOL IsMax = CurrentMax->IsMax();
1096 // Our max passed a min => start overlap
1098 && Intersect2D(*Object, *id1, Axis1, Axis2)
1099 && Intersect1D_Min(box, *id1, BaseEP, Axis)
1101 AddPair(UserObject, id1->mObject, UserGUID, id1->mGUID);
1104 id1->mMin[Axis + IsMax] = EPIndex++;
1105 *(CurrentMax-1) = *CurrentMax;
1108 if(SavedIndex!=EPIndex)
1110 mBoxes[Saved.GetOwner()].mMin[Axis + Saved.IsMax()] = EPIndex;
1111 BaseEP[EPIndex] = Saved;
1114 else if(Limit < CurrentMax->mValue)
1116 CurrentMax->mValue = Limit;
1118 // Max is moving left:
1119 ASAP_EndPoint Saved = *CurrentMax;
1120 udword EPIndex = (size_t(CurrentMax) - size_t(BaseEP))/sizeof(ASAP_EndPoint);
1121 const udword SavedIndex = EPIndex;
1123 while((--CurrentMax)->mValue > Limit)
1126 _prefetch(CurrentMax-1);
1128 ASAP_Box* id1 = mBoxes + CurrentMax->GetOwner();
1129 const BOOL IsMax = CurrentMax->IsMax();
1132 // Our max passed a min => stop overlap
1134 #ifdef USE_OVERLAP_TEST_ON_REMOVES
1135 && Intersect2D(*Object, *id1, Axis1, Axis2)
1138 RemovePair(UserObject, id1->mObject, UserGUID, id1->mGUID);
1141 id1->mMin[Axis + IsMax] = EPIndex--;
1142 *(CurrentMax+1) = *CurrentMax;
1145 if(SavedIndex!=EPIndex)
1147 mBoxes[Saved.GetOwner()].mMin[Axis + Saved.IsMax()] = EPIndex;
1148 BaseEP[EPIndex] = Saved;
1156 udword ArraySAP::DumpPairs(SAP_CreatePair create_cb, SAP_DeletePair delete_cb, void* cb_user_data, ASAP_Pair** pairs)
1160 const udword* Entries = mData.GetEntries();
1161 const udword* Last = Entries + mData.GetNbEntries();
1164 udword* ToRemove = (udword*)Entries;
1165 while(Entries!=Last)
1167 const udword ID = *Entries++;
1168 ASAP_Pair* UP = mPairs.mActivePairs + ID;
1171 ASSERT(UP->IsInArray());
1174 // No need to call "ClearInArray" in this case, since the pair will get removed anyway
1177 if(delete_cb && !UP->IsNew())
1179 #ifdef PAIR_USER_DATA
1180 (delete_cb)(UP->GetObject0(), UP->GetObject1(), cb_user_data, UP->userData);
1182 (delete_cb)(UP->GetObject0(), UP->GetObject1(), cb_user_data, null);
1186 *ToRemove++ = udword(UP->id0)<<16|UP->id1;
1191 // Add => already there... Might want to create user data, though
1196 #ifdef PAIR_USER_DATA
1197 UP->userData = (create_cb)(UP->GetObject0(), UP->GetObject1(), cb_user_data);
1199 (create_cb)(UP->GetObject0(), UP->GetObject1(), cb_user_data);
1208 // #### try batch removal here
1209 Entries = mData.GetEntries();
1210 while(Entries!=ToRemove)
1212 const udword ID = *Entries++;
1213 const udword id0 = ID>>16;
1214 const udword id1 = ID&0xffff;
1215 bool Status = mPairs.RemovePair(id0, id1);
1219 #ifdef RELEASE_ON_RESET
1225 if(pairs) *pairs = mPairs.mActivePairs;
1227 return mPairs.mNbActivePairs;