//-- void KDTree::VAddObjects(const std::vector<ISpatialObject*>& refObjects) { ISpatialObject* pObjectList = NULL; ISpatialObject* pObjectTemp; std::vector<ISpatialObject*>::const_iterator iter_object; for(iter_object = refObjects.begin(); iter_object != refObjects.end(); iter_object++) { pObjectTemp = (*iter_object); pObjectTemp->VSetNext(pObjectList); pObjectList = pObjectTemp; // [rad] Store objects locally for fast update / iteration m_vecObjects.push_back((*iter_object)); } // [rad] Pre-allocate the tree Preallocate(s_i32MaxDepth); Vector3 vec3Min(m_vec3Center.x - m_f32HalfWidth, m_vec3Center.y - m_f32HalfWidth, m_vec3Center.z - m_f32HalfWidth); Vector3 vec3Max(m_vec3Center.x + m_f32HalfWidth, m_vec3Center.y + m_f32HalfWidth, m_vec3Center.z + m_f32HalfWidth); // [rad] Based on this info, construct recursively, starting at root m_pRootNode->Construct(pObjectTemp, refObjects.size(), vec3Min, vec3Max); }
//-- void KDTreeNode::RemoveObject(ISpatialObject* pObject) { if(m_pObjects == pObject) { m_pObjects = m_pObjects->VGetNext(); } else { // [rad] traverse list and remove ISpatialObject* pIter = m_pObjects; ISpatialObject* pPrev; while(pIter) { pPrev = pIter; pIter = pIter->VGetNext(); if(pObject == pIter) { // [rad] Remove node and decrement node count pPrev->VSetNext(pObject->VGetNext()); break; } } } // [rad] Decrement counts m_i32ObjectCount--; m_i32ObjectTotal--; // [rad] Traverse up and decrement parent total counts KDTreeNode* pNode = m_pParent; // [rad] Check if this node still contains this object while(pNode) { pNode->m_i32ObjectTotal--; // [rad] Go up pNode = pNode->m_pParent; } }
//-- void UniformGridHashBucket::RemoveObject(ISpatialObject* pObject) { /* // [rad] Iterate over all objects in this bucket std::vector<ISpatialObject*>::iterator iter_object; for(iter_object = m_vecObjects.begin(); iter_object != m_vecObjects.end(); iter_object++) { if(pObject == (*iter_object)) { // [rad] Remove this object m_vecObjects.erase(iter_object); return; } } */ if(m_pObjects == pObject) { m_pObjects = m_pObjects->VGetNext(); } else { // [rad] traverse list and remove ISpatialObject* pIter = m_pObjects; ISpatialObject* pPrev; while(pIter) { pPrev = pIter; pIter = pIter->VGetNext(); if(pIter == pObject) { pPrev->VSetNext(pIter->VGetNext()); break; } } } }
//-- void LooseOctree::VUpdate() { // [rad] We are completely rebuilding this loose octree if(m_i32Rebuild) { // [rad] We are going to rebuild octree (without dealocating space) m_pRootNode->Rebuild(); // [rad] Populate the octree std::vector<ISpatialObject*>::iterator iter_object; for(iter_object = m_vecObjects.begin(); iter_object != m_vecObjects.end(); iter_object++) { m_pRootNode->AddObject((*iter_object)); } } else { LooseOctreeNode* pNode; ISpatialObject* pObject; ISpatialObject* pPrev; std::vector<ISpatialObject*>::iterator iter_object; for(iter_object = m_vecObjects.begin(); iter_object != m_vecObjects.end(); iter_object++) { pNode = static_cast<LooseOctreeNode*>((*iter_object)->VGetCell()); // [rad] Check if this node still contains the element if(pNode->CheckContains(*iter_object)) { continue; } else { // [rad] Remove element if(pNode->m_pObjects == (*iter_object)) { pNode->m_pObjects = pNode->m_pObjects->VGetNext(); } else { // [rad] traverse list and remove pObject = pNode->m_pObjects; while(pObject) { pPrev = pObject; pObject = pObject->VGetNext(); if(pObject == (*iter_object)) { pPrev->VSetNext(pObject->VGetNext()); break; } } } // [rad] Go up one node pNode = pNode->m_pParent; // [rad] Check if this node still contains this object while(pNode) { // [rad] Check if contains, here we use non-loose // check, because it's possible that object will be // stuck in the parent's node (since it might happen // that object will be in it's loose dimensions) if(pNode->CheckContainsNonLoose(*iter_object)) { pNode->AddObject((*iter_object)); break; } else { pNode = pNode->m_pParent; } } //m_pRootNode->AddObject(*iter_object); } } } // [rad] Do top-down collision testing std::vector<ISpatialObject*>::iterator iter_object; for(iter_object = m_vecObjects.begin(); iter_object != m_vecObjects.end(); iter_object++) { m_pRootNode->CheckCollisions((*iter_object)); } }
//-- 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); }
//-- ISpatialObject* KDTreeNode::Invalidate() { ISpatialObject* pIter; ISpatialObject* pTemp; ISpatialObject* pObjects = NULL; int i32ObjectCount; // [rad] If this is a leaf if(!m_pChildLeft || !m_pChildRight) { pObjects = m_pObjects; i32ObjectCount = m_i32ObjectCount; m_i32ObjectCount = 0; m_i32ObjectTotal = 0; m_pObjects = NULL; return(pObjects); } ISpatialObject* pObjectsLeft = NULL; if(m_pChildLeft->m_i32ObjectTotal) { pObjectsLeft = m_pChildLeft->Invalidate(); } ISpatialObject* pObjectsRight = NULL; if(m_pChildRight->m_i32ObjectTotal) { pObjectsRight = m_pChildRight->Invalidate(); } pIter = pObjectsLeft; while(pIter) { pTemp = pIter->VGetNext(); pIter->VSetNext(pObjects); pObjects = pIter; pIter = pTemp; } pIter = pObjectsRight; while(pIter) { pTemp = pIter->VGetNext(); pIter->VSetNext(pObjects); pObjects = pIter; pIter = pTemp; } pIter = m_pObjects; while(pIter) { pTemp = pIter->VGetNext(); pIter->VSetNext(pObjects); pObjects = pIter; pIter = pTemp; } /* ISpatialObject* pIterLeft = NULL; ISpatialObject* pIterParent = NULL; ISpatialObject* pTemp; // [rad] traverse left link list looking for end if(pObjectsLeft) { pIterLeft = pObjectsLeft; while(1) { pTemp = pIterLeft->VGetNext(); if(!pTemp) { break; } pIterLeft = pTemp; } } // [rad] traverse straddling collection (stored at this node) looking for end if(m_pObjects) { pIterParent = m_pObjects; while(1) { pTemp = pIterParent->VGetNext(); if(!pTemp) { break; } pIterParent = pTemp; } } // [rad] Glue 3 single link lists together if(pIterLeft && pIterParent) { pIterParent->VSetNext(pObjectsRight); pIterLeft->VSetNext(pIterParent); pObjects = pIterLeft; } else if(pIterLeft) { pIterLeft->VSetNext(pObjectsRight); pObjects = pIterLeft; } else if(pIterParent) { pIterParent->VSetNext(pObjectsRight); pObjects = pIterParent; } else { pObjects = pObjectsRight; } */ m_i32ObjectCount = 0; m_i32ObjectTotal = 0; m_pObjects = NULL; return(pObjects); }
//-- void Octree::VUpdate() { ISpatialObject* pElement; // [rad] We are rebuilding octree every frame if(m_i32Rebuild) { ISpatialObject* pObjectList = NULL; // [rad] We are going to rebuild octree (without dealocating space) m_pRootNode->Rebuild(); // [rad] Populate the octree std::vector<ISpatialObject*>::iterator iter_object; for(iter_object = m_vecObjects.begin(); iter_object != m_vecObjects.end(); iter_object++) { pElement = (*iter_object); //m_pRootNode->AddObject((*iter_object)); pElement->VSetNext(pObjectList); pObjectList = pElement; } m_pRootNode->AddObjects(pObjectList, m_vecObjects.size()); } else { // [rad] Instead of rebuilding octree every frame we will remove objects // bottom-up OctreeNode* pNode; ISpatialObject* pObject; ISpatialObject* pPrev; std::vector<ISpatialObject*>::iterator iter_object; for(iter_object = m_vecObjects.begin(); iter_object != m_vecObjects.end(); iter_object++) { pElement = (*iter_object); pNode = static_cast<OctreeNode*>(pElement->VGetCell()); // [rad] Remove element if(pNode->m_pObjects == pElement) { // [rad] Remove object from the list (first position) pNode->m_pObjects = pNode->m_pObjects->VGetNext(); } else { // [rad] traverse list and remove pObject = pNode->m_pObjects; while(pObject) { pPrev = pObject; pObject = pObject->VGetNext(); if(pObject == pElement) { // [rad] Remove object from the list pPrev->VSetNext(pObject->VGetNext()); break; } } } // [rad] Decrement node's object count pNode->m_i32ObjectCount--; // [rad] Attempt to re-insert element if(pNode->CheckContains(pElement)) { pNode->AddObject(pElement); } else { // [rad] Go up one node pNode = pNode->m_pParent; // [rad] Check if this node still contains this object while(pNode) { // [rad] Check if contains if(pNode->CheckContains(pElement)) { pNode->AddObject(pElement); break; } else { pNode = pNode->m_pParent; } } } } } // [rad] Check collisionsgdb1 std::vector<OctreeNode*> vecAncestors; m_pRootNode->CheckCollisions(vecAncestors); }
//-- 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 Octree::VAddObjects(const std::vector<ISpatialObject*>& refObjects) { float f32MinDiameter = 1.0e38f; float f32CellSize = 2.0f * m_f32HalfWidth; float f32Diameter; int i32Depth = 0; int i32Divisions = 1; std::vector<ISpatialObject*>::const_iterator iter_object; for(iter_object = refObjects.begin(); iter_object != refObjects.end(); iter_object++) { f32Diameter = (*iter_object)->VGetRadius(); if(f32Diameter < f32MinDiameter) { // [rad] Found a smaller object, update f32MinDiameter = f32Diameter; } } // [rad] Make cell size 'x' times as big as the smallest diameter f32MinDiameter *= s_f32MaxObjectNodeRatio; // [rad] Calculate depth f32CellSize = 2.0f * m_f32HalfWidth; while(f32MinDiameter <= f32CellSize) { // [rad] Don't make more than max depth if(s_i32MaxDepth == i32Depth) { break; } i32Divisions *= 2; f32CellSize = 2.0f * m_f32HalfWidth / static_cast<float>(i32Divisions); i32Depth++; } // [rad] Delete everything but the root m_pRootNode->Free(); // [rad] Pre-allocate the tree Preallocate(i32Depth); ISpatialObject* pObjectList = NULL; ISpatialObject* pObjectTemp; // [rad] Insert the elements into the tree for(iter_object = refObjects.begin(); iter_object != refObjects.end(); iter_object++) { if(m_i32Rebuild) { m_pRootNode->AddObject((*iter_object)); } else { pObjectTemp = (*iter_object); pObjectTemp->VSetNext(pObjectList); pObjectList = pObjectTemp; } // [rad] Store locally in a list, for fast iteration m_vecObjects.push_back((*iter_object)); } if(!m_i32Rebuild) { // [rad] Add objects m_pRootNode->AddObjects(pObjectList, m_vecObjects.size()); } }