//-- void UniformGrid::VAddObjects(const std::vector<ISpatialObject*>& refObjects) { // [rad] We need to determine the size of the grid (size of each cell) int i32Hash; float f32Diameter; ISpatialObject* pObject; // [rad] Iterate through all objects, and find the biggest diameter std::vector<ISpatialObject*>::const_iterator iter_object; for(iter_object = refObjects.begin(); iter_object != refObjects.end(); iter_object++) { pObject = (*iter_object); f32Diameter = pObject->VGetRadius() * 2.0f; if(f32Diameter > m_f32GridSize) { // [rad] Found a bigger object, update m_f32GridSize = f32Diameter; } // [rad] We also locally store object pointers, so we could do a bulk update m_vecObjects.push_back(pObject); } // [rad] We'll make it so that cell's is 'x' times as big as the largest object m_f32GridSize *= s_f32ObjectCellRatio; // [rad] For each object, determine the cell and insert for(iter_object = refObjects.begin(); iter_object != refObjects.end(); iter_object++) { pObject = (*iter_object); // [rad] Compute hash for this object - we use object's center to // determine the proper cell, and then hash that value i32Hash = ComputeHashValue(m_i32HashBuckets, static_cast<int>(pObject->VGetPosition().x / m_f32GridSize), static_cast<int>(pObject->VGetPosition().y / m_f32GridSize), static_cast<int>(pObject->VGetPosition().z / m_f32GridSize)); // [rad] Insert at the end of this bucket (m_vecHashBuckets[i32Hash])->InsertObject(pObject); } }
//-- void KDTreeNode::Construct(ISpatialObject* pObjectList, int i32ObjectCount, const Vector3& refVectorMin, const Vector3& refVectorMax) { // [rad] Store voxel info m_vec3Max = refVectorMax; m_vec3Min = refVectorMin; // [rad] Split plane position is already stored ISpatialObject* pIter = pObjectList; ISpatialObject* pObjectTemp; ISpatialObject* pObject; // [rad] If there are no children, or there's less objects than bins, // store everything in this node if(!m_pChildLeft || !m_pChildRight || i32ObjectCount < s_i32BinCount) { // [rad] There's no splitpane and position is not important m_f32SplitPosition = 0.0f; //m_i32SplitPane = -1; // [rad] Copy elements... m_pObjects = pObjectList; m_i32ObjectCount = i32ObjectCount; m_i32ObjectTotal = i32ObjectCount; // [rad] Iterate through all elements and set this node as a containing cell pIter = m_pObjects; while(pIter) { pIter->VSetCell(this); pIter = pIter->VGetNext(); } return; } // [rad] Compute sizes float f32Span = m_vec3Max[m_i32SplitPane] - m_vec3Min[m_i32SplitPane]; float f32BucketSize = f32Span / static_cast<float>(s_i32BinCount); float f32Offset = -m_vec3Min[m_i32SplitPane]; int i32BinIndex; int i32Index; // [rad] Clean prefix sums and previous values for(i32Index = 0; i32Index < s_i32BinCount; i32Index++) { s_vecBins.at(i32Index).first = 0; s_vecBins.at(i32Index).second = 0; s_vecSums.at(i32Index).first = 0; s_vecSums.at(i32Index).second = 0; } // [rad] Go linearly through the list and do binning pIter = pObjectList; Vector3 vec3Center; float f32Radius; while(pIter) { // [rad] Get center and radius of this object vec3Center = pIter->VGetPosition(); f32Radius = pIter->VGetRadius(); // [rad] Increment proper bins i32BinIndex = static_cast<int>(floorf((f32Offset + vec3Center[m_i32SplitPane] - f32Radius) * s_i32BinCount / f32Span)); s_vecBins[i32BinIndex].first++; i32BinIndex = static_cast<int>(floorf((f32Offset + vec3Center[m_i32SplitPane] + f32Radius) * s_i32BinCount / f32Span)); s_vecBins[i32BinIndex].second++; // [rad] iterate to next element in list pIter = pIter->VGetNext(); } // [rad] Compute prefix sums int i32SumMin = 0; int i32SumMax = 0; for(i32Index = 0; i32Index < s_i32BinCount; i32Index++) { i32SumMin += s_vecBins.at(i32Index).first; i32SumMax += s_vecBins.at(i32Index).second; s_vecSums.at(i32Index).first = i32SumMin; s_vecSums.at(i32Index).second = i32SumMax; } // [rad] Compute split candidate int i32MinDiff = std::numeric_limits<int>::max(); int i32BinDiff; int i32SplitPosition = 0; for(i32Index = 0; i32Index < s_i32BinCount; i32Index++) { i32BinDiff = abs(s_vecSums.at(i32Index).first - s_vecSums.at(s_i32BinCount - i32Index - 1).second); if(i32BinDiff < i32MinDiff) { // [rad] This is a good candidate i32MinDiff = i32BinDiff; i32SplitPosition = i32Index; } } // [rad] Find real split position //m_f32SplitPosition = ((i32SplitPosition * f32Span) / f32BucketSize) - f32Offset; m_f32SplitPosition = m_vec3Min[m_i32SplitPane] + i32SplitPosition * f32BucketSize; // [rad] Now go insert objects into child nodes ISpatialObject* pObjectListLeft = NULL; ISpatialObject* pObjectListRight = NULL; int i32CountLeft = 0; int i32CountRight = 0; pIter = pObjectList; while(pIter) { pObject = pIter->VGetNext(); // [rad] Get center and radius of this object vec3Center = pIter->VGetPosition(); f32Radius = pIter->VGetRadius(); // [rad] Check where this object belongs if(vec3Center[m_i32SplitPane] + f32Radius <= m_f32SplitPosition) { // [rad] Left child pIter->VSetNext(pObjectListLeft); pObjectListLeft = pIter; i32CountLeft++; } else if(vec3Center[m_i32SplitPane] - f32Radius >= m_f32SplitPosition) { // [rad] Right child pIter->VSetNext(pObjectListRight); pObjectListRight = pIter; i32CountRight++; } else { pIter->VSetCell(this); pIter->VSetNext(m_pObjects); m_pObjects = pIter; m_i32ObjectCount++; } pIter = pObject; } // [rad] We'll keep track of how many objects we have in this node // and in children m_i32ObjectTotal += i32ObjectCount; Vector3 vec3Min = refVectorMin; vec3Min[m_i32SplitPane] = m_f32SplitPosition; Vector3 vec3Max = refVectorMax; vec3Max[m_i32SplitPane] = m_f32SplitPosition; // [rad] Recurse into left child m_pChildLeft->Construct(pObjectListLeft, i32CountLeft, refVectorMin, vec3Max); // [rad] Recurse into right child m_pChildRight->Construct(pObjectListRight, i32CountRight, vec3Min, refVectorMax); }
//-- void OctreeNode::AddObjects(ISpatialObject* pObjectList, int i32ObjectCount) { ISpatialObject* pIter; ISpatialObject* pObject; // [rad] Check if we can stop splitting if(i32ObjectCount < s_i32MinSplitCount) { m_pObjects = pObjectList; pIter = m_pObjects; while(pIter) { pIter->VSetCell(this); pIter = pIter->VGetNext(); } m_i32ObjectCount = i32ObjectCount; return; } int i32Index = 0; ISpatialObject* apChildObjects[8]; int aiChildCounts[8]; for(i32Index = 0; i32Index < 8; i32Index++) { apChildObjects[i32Index] = NULL; aiChildCounts[i32Index] = 0; } pIter = pObjectList; while(pIter) { pObject = pIter->VGetNext(); int i32Position = 0; int i32Straddle = 0; float f32Radius = pIter->VGetRadius(); Vector3 vec3Center = pIter->VGetPosition(); for(i32Index = 0; i32Index < 3; i32Index++) { if(m_vec3Center[i32Index] < vec3Center[i32Index]) { if(m_vec3Center[i32Index] > vec3Center[i32Index] - f32Radius) { // [rad] Straddle occurs i32Straddle = 1; break; } else { i32Position |= (1 << i32Index); } } else { if(m_vec3Center[i32Index] < vec3Center[i32Index] + f32Radius) { // [rad] Straddle occurs i32Straddle = 1; break; } } } if(!i32Straddle && m_pChildren[i32Position]) { // [rad] Contained in existing child node //m_pChildren[i32Position]->AddObject(pObject); pIter->VSetNext(apChildObjects[i32Position]); apChildObjects[i32Position] = pIter; aiChildCounts[i32Position]++; } else { // [rad] Store this node for fast back-link pIter->VSetCell(this); // [rad] Straddling or no child node available pIter->VSetNext(m_pObjects); m_pObjects = pIter; m_i32ObjectCount++; } pIter = pObject; } // [rad] At this point for(i32Index = 0; i32Index < 8; i32Index++) { // [rad] Delegate to children if(m_pChildren[i32Index] && aiChildCounts[i32Index]) { m_pChildren[i32Index]->AddObjects(apChildObjects[i32Index], aiChildCounts[i32Index]); } } }
//-- void UniformGrid::VUpdate() { int i32X1, i32X2; int i32Y1, i32Y2; int i32Z1, i32Z2; int i32Hash; float f32Delta; UniformGridHashBucket* pBucket; std::vector<ISpatialObject*>::iterator iter_object; for(iter_object = m_vecObjects.begin(); iter_object != m_vecObjects.end(); iter_object++) { ISpatialObject* pObject = (*iter_object); // [rad] Retrieve the bucket in which this object is stored pBucket = static_cast<UniformGridHashBucket*>(pObject->VGetCell()); // [rad] Retrieve new object position const Vector3& vec3Position = pObject->VGetPosition(); // [rad] Check if we need bucket update (compute hash) i32Hash = ComputeHashValue(m_i32HashBuckets, static_cast<int>(vec3Position.x / m_f32GridSize), static_cast<int>(vec3Position.y / m_f32GridSize), static_cast<int>(vec3Position.z / m_f32GridSize)); // [rad] Check if we need to switch buckets if(m_vecHashBuckets[i32Hash] != pBucket) { // [rad] Remove from old bucket pBucket->RemoveObject(pObject); // [rad] Add to new (other) bucket (m_vecHashBuckets[i32Hash])->InsertObject(pObject); } // [rad] Update frame ++m_i32FrameCount; // [rad] Check collisions within the current bucket pBucket->CheckCollisions(m_i32FrameCount, pObject); // [rad] Now we need to check adjacent buckets: // compute all cells which this object might overlap. // Because initially we picked size for our cells big enough to // encompass any object, we have to check at most 8 cells (3D). f32Delta = pObject->VGetRadius() + m_f32GridSize / s_f32ObjectCellRatio + s_f32Epsilon; i32X1 = static_cast<int>(floorf((vec3Position.x - f32Delta) / m_f32GridSize)); i32X2 = static_cast<int>(ceilf((vec3Position.x + f32Delta) / m_f32GridSize)); i32Y1 = static_cast<int>(floorf((vec3Position.y - f32Delta) / m_f32GridSize)); i32Y2 = static_cast<int>(ceilf((vec3Position.y + f32Delta) / m_f32GridSize)); i32Z1 = static_cast<int>(floorf((vec3Position.z - f32Delta) / m_f32GridSize)); i32Z2 = static_cast<int>(ceilf((vec3Position.z + f32Delta) / m_f32GridSize)); // [rad] Check all grid cells for(int i32XIndex = i32X1; i32XIndex <= i32X2; i32XIndex++) { for(int i32YIndex = i32Y1; i32YIndex <= i32Y2; i32YIndex++) { for(int i32ZIndex = i32Z1; i32ZIndex <= i32Z2; i32ZIndex++) { i32Hash = ComputeHashValue(m_i32HashBuckets, i32XIndex, i32YIndex, i32ZIndex); // [rad] Check if we have checked this bucket already if(m_vecHashBuckets[i32Hash]->GetLastFrame() == m_i32FrameCount) { continue; } // [rad] Otherwise check collisions (m_vecHashBuckets[i32Hash])->CheckCollisions(m_i32FrameCount, pObject); } } } } }