#include "munga.h" #pragma hdrstop #include "boxtree.h" #include "point3d.h" #include "line.h" //############################################################################# //######################### BoundingBoxTreeNode ######################### //############################################################################# MemoryBlock *BoundingBoxTreeNode::GetAllocatedMemory() { static MemoryBlock allocatedMemory(sizeof(BoundingBoxTreeNode), 500, 50, "BoundingBoxTree Nodes"); return &allocatedMemory; } int BoundingBoxTreeNode::TraversalOrder[6]={4,5,0,1,2,3}; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BoundingBoxTreeNode::SetTraversalOrder( int first, int second, int third ) { Verify((unsigned)first <= Z_Axis && first != second); Verify((unsigned)second <= Z_Axis && second != third); Verify((unsigned)third <= Z_Axis && third != first); TraversalOrder[0] = first << 1; TraversalOrder[1] = TraversalOrder[0] + 1; TraversalOrder[2] = second << 1; TraversalOrder[3] = TraversalOrder[2] + 1; TraversalOrder[4] = third << 1; TraversalOrder[5] = TraversalOrder[4] + 1; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BoundingBoxTreeNode::BoundingBoxTreeNode( BoundingBox *volume, const ExtentBox &extents ) { Check_Pointer(this); Check(&extents); nodeExtents = extents; staticContents = volume; innerNode = NULL; for (int i=0; i nodeExtents[face]) { is_clipped = clipped_box[opp_face] < nodeExtents[face]; } else { continue; } } // //------------------------------------------------- // Handle testing the minimum value side of the box //------------------------------------------------- // else { if (clipped_box[face] < nodeExtents[face]) { is_clipped = clipped_box[opp_face] > nodeExtents[face]; } else { continue; } } // //--------------------------------------------------------------------- // If the new box is to be clipped, save the old value for the opposite // face and replace it with this space's bounding face //--------------------------------------------------------------------- // if (is_clipped) { save = clipped_box[opp_face]; clipped_box[opp_face] = nodeExtents[face]; } // //----------------------------------------------------------------------- // Make sure that we are not registering an empty slice of the new volume //----------------------------------------------------------------------- // if (volume->IntersectsBounded(clipped_box)) { // //-------------------------------------------------------------------- // If no spaces have been defined within the facing voxel, construct // a new one out of the clipped box, otherwise call this routine again // with the given voxel and the clipped box //-------------------------------------------------------------------- // if (!nodeBranches[face]) { nodeBranches[face] = new BoundingBoxTreeNode(volume, clipped_box); Register_Object(nodeBranches[face]); } else { Check(nodeBranches[face]); nodeBranches[face]->Add(volume, clipped_box); } } // //--------------------------------------------------------------------- // If no clipping was necessary for this voxel, then no portion of // the box remains to be tested, so abort the loop. Otherwise, restore // the opposing face of the new box, and clip the newly created // voxel out of the new box //--------------------------------------------------------------------- // if (!is_clipped) return; clipped_box[opp_face] = save; clipped_box[face] = nodeExtents[face]; } //----------------------------------------------------------------------- // Make sure that we are not registering an empty slice of the new volume //----------------------------------------------------------------------- // if (volume->IntersectsBounded(clipped_box)) { // //---------------------------------------------------------------------- // The remaining portion of the box is inside our current space, so link // it there //---------------------------------------------------------------------- // if (!innerNode) { innerNode = new BoundingBoxTreeNode(volume, clipped_box); Register_Object(innerNode); } else { Check(innerNode); innerNode->Add(volume, clipped_box); } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BoundingBoxTreeNode::Remove( BoundingBox *volume, const ExtentBox &extents ) { Check(this); Check(volume); Check(&extents); // //---------------------------------------------------------------------- // If this node contains the desired volume, immediately NULL it out and // return //---------------------------------------------------------------------- // if (staticContents == volume) { staticContents = NULL; return; } // //--------------------------------------------------- // Make our copy of the new box for clipping purposes //--------------------------------------------------- // ExtentBox clipped_box(extents); // //------------------------------------------------------------------------ // Check along each of the six faces, separately clipping off elements of // the new box which do not lie within this voxel //------------------------------------------------------------------------ // for (int i=0; i<6; ++i) { int is_clipped; Scalar save; // //------------------------------------------------------------------ // Handle testing the maximum value side of the box. Make sure that // volumes actually extend into the current voxel before they are // clipped, preventing insertion of zero-thickness voxels //------------------------------------------------------------------ // int face = TraversalOrder[i]; int opp_face = face^1; if (face&1) { if (clipped_box[face] > nodeExtents[face]) { is_clipped = clipped_box[opp_face] < nodeExtents[face]; } else { continue; } } // //------------------------------------------------- // Handle testing the minimum value side of the box //------------------------------------------------- // else { if (clipped_box[face] < nodeExtents[face]) { is_clipped = clipped_box[opp_face] > nodeExtents[face]; } else { continue; } } // //--------------------------------------------------------------------- // If the new box is to be clipped, save the old value for the opposite // face and replace it with this space's bounding face //--------------------------------------------------------------------- // if (is_clipped) { save = clipped_box[opp_face]; clipped_box[opp_face] = nodeExtents[face]; } // //--------------------------------------------------------------------- // Because the volume has already been added, branches must exist along // all faces it gets clipped by unless it was an empty slice //--------------------------------------------------------------------- // if (volume->IntersectsBounded(clipped_box)) { Check(nodeBranches[face]); nodeBranches[face]->Remove(volume, clipped_box); } // //--------------------------------------------------------------------- // If no clipping was necessary for this voxel, then no portion of // the box remains to be tested, so abort the loop. Otherwise, restore // the opposing face of the new box, and clip the newly created // voxel out of the new box //--------------------------------------------------------------------- // if (!is_clipped) return; clipped_box[opp_face] = save; clipped_box[face] = nodeExtents[face]; } // //-------------------------------------------------------------------------- // If we are not dealing with an empty slice of the volume, the remaining // slice of the extents must have been entered as internal subspacing //-------------------------------------------------------------------------- // if (volume->IntersectsBounded(clipped_box)) { Check(innerNode); innerNode->Remove(volume, clipped_box); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BoundingBoxTreeNode* BoundingBoxTreeNode::FindSmallestNodeContaining( const ExtentBox &extents, BoundingBoxTreeNode *parent ) { Check(&extents); // //------------------------------------------------------------------------ // Check along each of the six faces to see if this volume cuts any of the // planes of the node, //------------------------------------------------------------------------ // BoundingBoxTreeNode *branch = this; Check_Node: Check(branch); for (int i=0; i<6; ++i) { // //------------------------------------------------- // Handle testing the maximum value side of the box //------------------------------------------------- // int face = TraversalOrder[i]; int opp_face = face^1; if (face&1) { if (extents[face] > branch->nodeExtents[face]) { if (extents[opp_face] <= branch->nodeExtents[face]) { return (parent) ? parent : branch; } } else { continue; } } // //------------------------------------------------- // Handle testing the minimum value side of the box //------------------------------------------------- // else { if (extents[face] < branch->nodeExtents[face]) { if (extents[opp_face] >= branch->nodeExtents[face]) { return (parent) ? parent : branch; } } else { continue; } } // //---------------------------------------------------- // If no branch exists, then this is the smallest node //---------------------------------------------------- // if (!branch->nodeBranches[face]) { return (parent) ? parent : branch; } branch = branch->nodeBranches[face]; Check(branch); goto Check_Node; } // //--------------------------------------------------------- // If no inner space exists, then this is the smallest node //--------------------------------------------------------- // if (!branch->innerNode) { return branch; } // //------------------------------------------------------------------------- // Once we cross into an innerspace, we have to modify the behaviour of the // previous, as a volume which is enclosed by the node could itself enclose // the innerspace node, meaning that the parent node should be reported as // the smallest node //------------------------------------------------------------------------- // parent = branch; branch = branch->innerNode; Check(branch); goto Check_Node; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BoundingBoxTreeNode* BoundingBoxTreeNode::FindSmallestNodeContainingColumn( const ExtentBox &extents ) { Check(&extents); // //------------------------------------------------------------------------ // Check along each of the six faces to see if this volume cuts any of the // planes of the node, //------------------------------------------------------------------------ // BoundingBoxTreeNode *branch = this; Check_Node: Check(this); for (int i=0; i<4; ++i) { // //------------------------------------------------- // Handle testing the maximum value side of the box //------------------------------------------------- // int face = TraversalOrder[i]; int opp_face = face^1; if (face&1) { if (extents[face] > branch->nodeExtents[face]) { if (extents[opp_face] <= branch->nodeExtents[face]) { return branch; } } else { continue; } } // //------------------------------------------------- // Handle testing the minimum value side of the box //------------------------------------------------- // else { if (extents[face] < branch->nodeExtents[face]) { if (extents[opp_face] >= branch->nodeExtents[face]) { return branch; } } else { continue; } } // //---------------------------------------------------- // If no branch exists, then this is the smallest node //---------------------------------------------------- // if (!branch->nodeBranches[face]) { return branch; } branch = branch->nodeBranches[face]; Check(branch); goto Check_Node; } return branch; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BoundingBox* BoundingBoxTreeNode::FindBoundingBoxContaining( const Point3D &point, BoundingBox *parent ) { Check(this); Check(&point); // //---------------------------------------------------- // Set the content pointer to point at this root voxel //---------------------------------------------------- // BoundingBoxTreeNode *branch = this; // //------------------------------------------------------------------------- // Keep checking our voxel branches until the point lies inside the current // voxel //------------------------------------------------------------------------- // Check_Node: Check(branch); for (int i=0; i<6; ++i) { // //------------------------------------------------------- // Keep looking if the point is not beyond a given extent //------------------------------------------------------- // int face = TraversalOrder[i]; int axis = face >> 1; if (face&1) { if (point[axis] <= branch->nodeExtents[face]) { continue; } } else if (point[axis] >= branch->nodeExtents[face]) { continue; } // //----------------------------------------------------------------------- // The point lies in a side's subspace, so make sure that the subspace on // that side exists. If not, no voxel will contain the point so return // the parent. If a facing subspace exists, set the branch pointer to // that subspace, then check to see where the point is relative to that // subspace //----------------------------------------------------------------------- // if (!branch->nodeBranches[face]) { return parent; } branch = branch->nodeBranches[face]; goto Check_Node; } // //------------------------------------------------------------------------- // Since we exited the loop, we have to check inside the node to see if it // has been subspaced. If so, restart the function again on the internal // subspace, using the contents of this subspace as the new parent solid. // // Note that negative space will not be correctly handled for cases where // the space being carved out does not fully occupy the zones it is in such // as with a cylinder //------------------------------------------------------------------------- // if (branch->innerNode) { if (branch->staticContents) { Check(branch->staticContents); if (branch->staticContents->ContainsBounded(point)) { parent = branch->staticContents; } } else { parent = NULL; } branch = branch->innerNode; goto Check_Node; } // //---------------------------------------------------------------------- // If our content pointer is NULL, then this node is considered negative // space, and thus empty //---------------------------------------------------------------------- // if (!branch->staticContents) { return NULL; } // //-------------------------------------------------------------------------- // We are pointing at a real solid, so if it does in fact contain our point, // go ahead a return the volume, otherwise return our parent volume //-------------------------------------------------------------------------- // Check(branch->staticContents); if (branch->staticContents->ContainsBounded(point)) { parent = branch->staticContents; } return parent; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BoundingBoxTreeNode::FindBoundingBoxesContaining( BoundingBox *volume, const ExtentBox &slice, BoundingBoxCollisionList &list ) { Check(this); Check(volume); Check(&slice); Check(&list); Verify(list.collisionsLeft>0); // //--------------------------------------------------------------------- // Check along each of the six faces, to see if any portion of the test // volume lies within any of the subspace partitions //--------------------------------------------------------------------- // ExtentBox clipped_box = slice; for (int i=0; i<6; ++i) { int is_clipped; Scalar save; // //----------------------------------------------------------------------- // Figure out the opposing face, then check to see if we are testing // against a maximum face. If so, check to see if some part of the test // volume protrudes into the defined subspace. If it does, figure out if // some portion of the test volume remains to be checked, otherwise keep // checking faces //----------------------------------------------------------------------- // int face = TraversalOrder[i]; int opp_face = face ^ 1; if (face&1) { if (clipped_box[face] > nodeExtents[face]) { is_clipped = clipped_box[opp_face] <= nodeExtents[face]; } else { continue; } } // //----------------------------------------------------------------------- // Otherwise, we are testing against a maximum face. If so, check to see // if some part of the test volume protrudes into the defined subspace. // If it does, figure out if some portion of the test volume remains to // be checked, otherwise keep checking faces //----------------------------------------------------------------------- // else { if (clipped_box[face] < nodeExtents[face]) { is_clipped = clipped_box[opp_face] >= nodeExtents[face]; } else { continue; } } // //------------------------------------------------------------------- // If the test volume needs to be clipped, save the old value for the // opposite face and replace it with this space's bounding face //------------------------------------------------------------------- // if (is_clipped) { save = clipped_box[opp_face]; clipped_box[opp_face] = nodeExtents[face]; } // //---------------------------------------------------------------------- // If a facing node exists, call this routine again with it and the part // of the test volume we just trimmed off //---------------------------------------------------------------------- // if (nodeBranches[face]) { Check(nodeBranches[face]); nodeBranches[face]->FindBoundingBoxesContaining( volume, clipped_box, list ); } // //----------------------------------------------------------------------- // If no clipping was necessary for this subspace, or we found no entries // remain in the collision list, we are done checking, so just return //----------------------------------------------------------------------- // if (!is_clipped || !list.collisionsLeft) { return; } // //------------------------------------------------------------------ // Otherwise, restore the opposing face of the new box, and clip the // already tested slice out of the test volume //------------------------------------------------------------------ // clipped_box[opp_face] = save; clipped_box[face] = nodeExtents[face]; } // //------------------------------------------------------------------------- // If we exited the loop, then we have to check inside the node. If this // node is not a hole, make sure that it collides against the slice. If it // doesn't, don't override the parent setting, otherwise do override it //------------------------------------------------------------------------- // BoundingBox *hit = NULL; if (staticContents) { Check(staticContents); if (staticContents->IntersectsBounded(clipped_box)) { hit = staticContents; } } // //------------------------------------------------------------------ // If the solid has been subspaced, have it check against collisions //------------------------------------------------------------------ // if (innerNode) { Check(innerNode); innerNode->FindBoundingBoxesContaining( volume, clipped_box, list ); } // //------------------------------------------------------------------------- // Otherwise, if the volume this node points to is not a hole, go ahead and // add it to the list //------------------------------------------------------------------------- // if (hit && list.collisionsLeft) { Check(hit); list.AddCollisionToList(hit, clipped_box); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BoundingBox* BoundingBoxTreeNode::FindBoundingBoxUnder( const Point3D &point, Scalar* height ) { Check(this); Check(&point); Check_Pointer(height); Verify((TraversalOrder[4]>>1) == Y_Axis) BoundingBoxTreeNode *branch = this; BoundingBox *floor; // //-------------------------------------------------------------------------- // Check along each of the faces to see if the point lies within any of // these subspaces. Note the change in the comparison resulting in only the // first four coordinates (for X and Z) being checked, as the Y coordinate // needs special treatment //-------------------------------------------------------------------------- // Find_Column: Check(branch); for (int i=0; i<4; ++i) { int face = TraversalOrder[i]; int axis = face >> 1; if (face&1) { if (point[axis] <= branch->nodeExtents[face]) { continue; } } else if (point[axis] >= branch->nodeExtents[face]) { continue; } // //--------------------------------------------------------------------- // If a facing subspace exists, start this routine again with the given // subspace and the point. If it doesn't exist, nothing will be hit //--------------------------------------------------------------------- // if (branch->nodeBranches[face]) { branch = branch->nodeBranches[face]; goto Find_Column; } else { *height = -1.0f; return NULL; } } // //------------------------------------------------------------------------- // We now know that the object has a presence along the line defined by the // projection of the point unto the XZ plane. If the point lies above the // object, and other objects exist in the maxZ subspace, and they are able // to determine the height, return the height they returned //------------------------------------------------------------------------- // if (point.y > branch->nodeExtents.maxY && branch->nodeBranches[3]) { Check(branch->nodeBranches[3]); floor = branch->nodeBranches[3]->FindBoundingBoxUnder(point, height); if (floor) { return floor; } } // //-------------------------------------------------------------------------- // Handle processing the ray internal to this solid if we need to project // the ray into our solid. If we are negative space, allow the ray to // project through the node unhindered. // // Note that this code will not properly handle going through negative space // if the negative space does not completely bore through solids //-------------------------------------------------------------------------- // if (point.y >= branch->nodeExtents.minY) { if (branch->staticContents) { Check(branch->staticContents); floor = branch->staticContents; *height = branch->staticContents->FindDistanceBelowBounded(point); if (*height == -1.0f || point.y - *height < branch->nodeExtents.minY) { goto Missed_Us; } } else { Missed_Us: floor = NULL; *height = -1.0f; } // //----------------------------------------------------------------------- // If there is internal space defined, we will rely upon it to return the // proper value if it supersedes this node's height value //----------------------------------------------------------------------- // if (branch->innerNode) { Check(branch->innerNode); Scalar height_2; BoundingBox *floor_2; floor_2 = branch->innerNode->FindBoundingBoxUnder(point, &height_2); if (floor_2 && (height_2 < *height || !floor)) { *height = height_2; floor = floor_2; } } // //-------------------------------------------------- // If something was struck, return that as the floor //-------------------------------------------------- // if (floor) { return floor; } } // //-------------------------------------------------------------------------- // We now know that the projection went through our bounding box without // intersecting our solid. If any objects exist in our Min_Z subspace, have // them try and return the height //-------------------------------------------------------------------------- // if (branch->nodeBranches[2]) { Check(branch->nodeBranches[2]); return branch->nodeBranches[2]->FindBoundingBoxUnder(point, height); } // //-------------------------------------------------------------- // Since there is nothing below this bounding box, return no hit //-------------------------------------------------------------- // else { *height = -1.0f; return NULL; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BoundingBox* BoundingBoxTreeNode::FindBoundingBoxHitBy(Line *line) { Check(this); Check(line); int test_queue[7]; Scalar enter = 0.0f, leave = 0.0f, perpendicular, drift, distance; BoundingBox *result; // //-------------------- // Set up for the test //-------------------- // int first = 0; int last = 6; Logical entered = False; Logical left = False; int i; for (i=0; i<6; ++i) { // //-------------------------------------------------------------------- // Figure out what axis we are dealing with, then based upon the // direction of the face, find out the distance from the origin to the // place against the normal, and find out how fast the perpendicular // distance changes with a unit movement along the line //-------------------------------------------------------------------- // int face = TraversalOrder[i]; int axis = face >> 1; if (face&1) { perpendicular = line->origin[axis] - nodeExtents[face]; drift = line->direction[axis]; } else { perpendicular = nodeExtents[face] - line->origin[axis]; drift = -line->direction[axis]; } // //---------------------------------------------------------------------- // If the line is parallel to the face, figure out whether or not the // line origin lies within the face's half-space. If not, put it in the // list and abort the loop //---------------------------------------------------------------------- // if (Small_Enough(drift)) { if (perpendicular > 0.0f) { if (nodeBranches[face]) { test_queue[first++] = face; } break; } else { continue; } } // //-------------------------------------------------------------------- // If the drift is towards the plane's halfspace, this plane is one of // the one through which the line could enter the node //-------------------------------------------------------------------- // distance = -perpendicular / drift; if (drift < 0.0f) { if (!entered) { entered = True; enter = distance; } else if (distance > enter) { enter = distance; } // //-------------------------------------------------------------------- // Queue up the node branch if the origin lies outside of this plane's // halfspace. If the line cannot reach the plane, abort any more // testing //-------------------------------------------------------------------- // if (perpendicular >= 0.0f) { if (nodeBranches[face]) { test_queue[first++] = face; } if (distance > line->length || left && enter > leave) { break; } } } // //-------------------------------------------------------------------- // If the drift is towards the plane's halfspace, this plane is one of // the one through which the line could enter the node //-------------------------------------------------------------------- // else { if (!left) { left = True; leave = distance; } else if (distance < leave) { leave = distance; } // //-------------------------------------------------------------------- // Queue up the node branch if the origin lies outside of this plane's // halfspace, and stop further testing. If the origin is inside the // halfspace, register this branch if the line will reach the plane //-------------------------------------------------------------------- // if (perpendicular >= 0.0f) { if (nodeBranches[face]) { test_queue[last--] = face; } break; } if (distance <= line->length) { if (nodeBranches[face]) { test_queue[last--] = face; } } if (entered && leave < enter) { break; } } } // //------------------------------------------------------------------------- // If the loop exited normally, check to see if the bounding box was struck // by the ray. If it was, then the interior of the voxel must be checked. // We know that the box was hit if the farthest face opposing the ray // direction is not farther than the nearest face not opposing the ray //------------------------------------------------------------------------- // if (i == 6) { test_queue[last--] = 6; } // //------------------------------------------------------------------------- // Step through the queue, checking in each of the identified regions to be // processed first. If a hit is found in one of these, immediately return // the result //------------------------------------------------------------------------- // for (i=0; iFindBoundingBoxHitBy(line); if (result) { Check(result); return result; } } // //----------------------------------- // Process the interior if it's there //----------------------------------- // if (last<6 && test_queue[last+1] == 6) { result = NULL; if (!staticContents) { if (innerNode) { Check(innerNode); result = innerNode->FindBoundingBoxHitBy(line); } } else { Check(staticContents); if (staticContents->HitByBounded(line, enter, leave)) { result = staticContents; } if (innerNode) { Check(innerNode); BoundingBox *result2 = innerNode->FindBoundingBoxHitBy(line); if (result2) { result = result2; } } } // //--------------------------------------------------------------------- // If the ray hit something, return the result, otherwise increment the // last pointer so that we don't process this subspace again //--------------------------------------------------------------------- // if (result) { Check(result); return result; } last++; } // //------------------------------------------------------------------------- // Step through the queue, checking in each of the identified regions to be // processed last. If a hit is found, immediately return the result //------------------------------------------------------------------------- // for (i=last+1; i<7; ++i) { Check(nodeBranches[test_queue[i]]); result = nodeBranches[test_queue[i]]->FindBoundingBoxHitBy(line); if (result) { Check(result); return result; } } // //---------------------------------------------------------- // The ray hit nothing in this voxel, so return the sentinel //---------------------------------------------------------- // return NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Logical BoundingBoxTreeNode::TestInstance() const { return True; } //############################################################################# //########################### BoundingBoxTree ########################### //############################################################################# //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BoundingBoxTree::Add( BoundingBox* volume, const ExtentBox &slice ) { Check(this); Check(volume); if (!root) { root = new BoundingBoxTreeNode(volume, slice); Register_Object(root); } else { Check(root); root->Add(volume, slice); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BoundingBoxTree::EraseTree() { if (root) { Unregister_Object(root); delete root; } root = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BoundingBox* BoundingBoxTree::FindBoundingBoxHitBy(Line *line) { Check(this); Check(line); Check(root); Point3D line_end; line->FindEnd(&line_end); ExtentBox line_volume(line->origin, line_end); return root->FindSmallestNodeContaining(line_volume, NULL) ->FindBoundingBoxHitBy(line); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Logical BoundingBoxTree::TestInstance() const { return True; }