/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* * OPCODE - Optimized Collision Detection * Copyright (C) 2001 Pierre Terdiman * Homepage: http://www.codercorner.com/Opcode.htm */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Contains code for box pruning. * \file IceBoxPruning.cpp * \author Pierre Terdiman * \date January, 29, 2000 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// You could use a complex sweep-and-prune as implemented in I-Collide. You could use a complex hashing scheme as implemented in V-Clip or recently in ODE it seems. You could use a "Recursive Dimensional Clustering" algorithm as implemented in GPG2. Or you could use this. Faster ? I don't know. Probably not. It would be a shame. But who knows ? Easier ? Definitely. Enjoy the sheer simplicity. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Precompiled Header #include "Stdafx.h" using namespace Opcode; inline_ void FindRunningIndex(udword& index, float* array, udword* sorted, int last, float max) { int First=index; while(First<=last) { index = (First+last)>>1; if(max>array[sorted[index]]) First = index+1; else last = index-1; } } // ### could be log(n) ! // and maybe use cmp integers // InsertionSort has better coherence, RadixSort is better for one-shot queries. #define PRUNING_SORTER RadixSort //#define PRUNING_SORTER InsertionSort // Static for coherence static PRUNING_SORTER* gCompletePruningSorter = null; static PRUNING_SORTER* gBipartitePruningSorter0 = null; static PRUNING_SORTER* gBipartitePruningSorter1 = null; inline_ PRUNING_SORTER* GetCompletePruningSorter() { if(!gCompletePruningSorter) gCompletePruningSorter = new PRUNING_SORTER; return gCompletePruningSorter; } inline_ PRUNING_SORTER* GetBipartitePruningSorter0() { if(!gBipartitePruningSorter0) gBipartitePruningSorter0 = new PRUNING_SORTER; return gBipartitePruningSorter0; } inline_ PRUNING_SORTER* GetBipartitePruningSorter1() { if(!gBipartitePruningSorter1) gBipartitePruningSorter1 = new PRUNING_SORTER; return gBipartitePruningSorter1; } void ReleasePruningSorters() { DELETESINGLE(gBipartitePruningSorter1); DELETESINGLE(gBipartitePruningSorter0); DELETESINGLE(gCompletePruningSorter); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Bipartite box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set. * \param nb0 [in] number of boxes in the first set * \param array0 [in] array of boxes for the first set * \param nb1 [in] number of boxes in the second set * \param array1 [in] array of boxes for the second set * \param pairs [out] array of overlapping pairs * \param axes [in] projection order (0,2,1 is often best) * \return true if success. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Opcode::BipartiteBoxPruning(udword nb0, const AABB** array0, udword nb1, const AABB** array1, Pairs& pairs, const Axes& axes) { // Checkings if(!nb0 || !array0 || !nb1 || !array1) return false; // Catch axes udword Axis0 = axes.mAxis0; udword Axis1 = axes.mAxis1; udword Axis2 = axes.mAxis2; // Allocate some temporary data float* MinPosList0 = new float[nb0]; float* MinPosList1 = new float[nb1]; // 1) Build main lists using the primary axis for(udword i=0;iGetMin(Axis0); for(udword i=0;iGetMin(Axis0); // 2) Sort the lists PRUNING_SORTER* RS0 = GetBipartitePruningSorter0(); PRUNING_SORTER* RS1 = GetBipartitePruningSorter1(); const udword* Sorted0 = RS0->Sort(MinPosList0, nb0).GetRanks(); const udword* Sorted1 = RS1->Sort(MinPosList1, nb1).GetRanks(); // 3) Prune the lists udword Index0, Index1; const udword* const LastSorted0 = &Sorted0[nb0]; const udword* const LastSorted1 = &Sorted1[nb1]; const udword* RunningAddress0 = Sorted0; const udword* RunningAddress1 = Sorted1; while(RunningAddress1GetMax(Axis0)) { if(array0[Index0]->Intersect(*array1[Index1], Axis1)) { if(array0[Index0]->Intersect(*array1[Index1], Axis2)) { pairs.AddPair(Index0, Index1); } } } } //// while(RunningAddress0GetMax(Axis0)) { if(array0[Index1]->Intersect(*array1[Index0], Axis1)) { if(array0[Index1]->Intersect(*array1[Index0], Axis2)) { pairs.AddPair(Index1, Index0); } } } } DELETEARRAY(MinPosList1); DELETEARRAY(MinPosList0); return true; } #define ORIGINAL_VERSION //#define JOAKIM /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set. * \param nb [in] number of boxes * \param array [in] array of boxes * \param pairs [out] array of overlapping pairs * \param axes [in] projection order (0,2,1 is often best) * \return true if success. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Opcode::CompleteBoxPruning(udword nb, const AABB** array, Pairs& pairs, const Axes& axes) { // Checkings if(!nb || !array) return false; // Catch axes udword Axis0 = axes.mAxis0; udword Axis1 = axes.mAxis1; udword Axis2 = axes.mAxis2; #ifdef ORIGINAL_VERSION // Allocate some temporary data // float* PosList = new float[nb]; float* PosList = new float[nb+1]; // 1) Build main list using the primary axis for(udword i=0;iGetMin(Axis0); PosList[nb++] = MAX_FLOAT; // 2) Sort the list PRUNING_SORTER* RS = GetCompletePruningSorter(); const udword* Sorted = RS->Sort(PosList, nb).GetRanks(); // 3) Prune the list const udword* const LastSorted = &Sorted[nb]; const udword* RunningAddress = Sorted; udword Index0, Index1; while(RunningAddressGetMax(Axis0)) while(PosList[Index1 = *RunningAddress2++]<=array[Index0]->GetMax(Axis0)) { // if(Index0!=Index1) // { if(array[Index0]->Intersect(*array[Index1], Axis1)) { if(array[Index0]->Intersect(*array[Index1], Axis2)) { pairs.AddPair(Index0, Index1); } } // } } } } DELETEARRAY(PosList); #endif #ifdef JOAKIM // Allocate some temporary data // float* PosList = new float[nb]; float* MinList = new float[nb+1]; // 1) Build main list using the primary axis for(udword i=0;iGetMin(Axis0); MinList[nb] = MAX_FLOAT; // 2) Sort the list PRUNING_SORTER* RS = GetCompletePruningSorter(); udword* Sorted = RS->Sort(MinList, nb+1).GetRanks(); // 3) Prune the list // const udword* const LastSorted = &Sorted[nb]; // const udword* const LastSorted = &Sorted[nb-1]; const udword* RunningAddress = Sorted; udword Index0, Index1; // while(RunningAddressGetMax(Axis0)) // float CurrentMin = array[Index0]->GetMin(Axis0); float CurrentMax = array[Index0]->GetMax(Axis0); while(MinList[Index1 = *RunningAddress2] <= CurrentMax) // while(PosList[Index1 = *RunningAddress] <= CurrentMax) { // if(Index0!=Index1) // { if(array[Index0]->Intersect(*array[Index1], Axis1)) { if(array[Index0]->Intersect(*array[Index1], Axis2)) { pairs.AddPair(Index0, Index1); } } // } RunningAddress2++; // RunningAddress++; } } } DELETEARRAY(MinList); #endif return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Brute-force versions are kept: // - to check the optimized versions return the correct list of intersections // - to check the speed of the optimized code against the brute-force one /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Brute-force bipartite box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set. * \param nb0 [in] number of boxes in the first set * \param array0 [in] array of boxes for the first set * \param nb1 [in] number of boxes in the second set * \param array1 [in] array of boxes for the second set * \param pairs [out] array of overlapping pairs * \return true if success. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Opcode::BruteForceBipartiteBoxTest(udword nb0, const AABB** array0, udword nb1, const AABB** array1, Pairs& pairs) { // Checkings if(!nb0 || !array0 || !nb1 || !array1) return false; // Brute-force nb0*nb1 overlap tests for(udword i=0;iIntersect(*array1[j])) pairs.AddPair(i, j); } } return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set. * \param nb [in] number of boxes * \param array [in] array of boxes * \param pairs [out] array of overlapping pairs * \return true if success. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Opcode::BruteForceCompleteBoxTest(udword nb, const AABB** array, Pairs& pairs) { // Checkings if(!nb || !array) return false; // Brute-force n(n-1)/2 overlap tests for(udword i=0;iIntersect(*array[j])) pairs.AddPair(i, j); } } return true; }