/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** *********************************************************************************************** * * * Project Name : WWPhys * * * * $Archive:: /Commando/Code/wwphys/physaabtreecull.cpp $* * * * Author:: Greg Hjelstrom * * * * $Modtime:: 5/05/01 5:14p $* * * * $Revision:: 23 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "physaabtreecull.h" #include "pscene.h" #include "physcoltest.h" #include "physinttest.h" #include "wwstring.h" /* ** Static members of PhysAABTreeCullClass */ bool PhysAABTreeCullClass::_HierarchicalVisCullingEnabled = true; /* ** PhysAABTreeCullClass is a derived AABTree which assumes it contains PhysClasses ** these two functions encapsulate some typecasting which happens in a lot ** of places... */ inline PhysClass * get_first_object(AABTreeNodeClass * node) { return (PhysClass *)(node->Object); } inline PhysClass * get_next_object(PhysClass * tile) { return (PhysClass *)(((AABTreeLinkClass *)tile->Get_Cull_Link())->NextObject); } /* ** Implementation of PhysAABTreeCullClass */ PhysAABTreeCullClass::PhysAABTreeCullClass(PhysicsSceneClass * pscene) : Scene(pscene) { } PhysAABTreeCullClass::~PhysAABTreeCullClass(void) { } bool PhysAABTreeCullClass::Verify(StringClass & error_report) { return Verify_Recursive(RootNode,error_report); } bool PhysAABTreeCullClass::Cast_Ray_Recursive ( AABTreeNodeClass * node, PhysRayCollisionTestClass & raytest ) { /* ** Cull the collision test against the bounding volume of this node ** If it is culled, stop descending the tree. */ if (raytest.Cull(node->Box)) { return false; } /* ** Test any objects in this node */ bool res = false; if (node->Object) { PhysClass * obj = get_first_object(node); while (obj) { if ( Scene->Do_Groups_Collide(obj->Get_Collision_Group(),raytest.CollisionGroup) && !obj->Is_Ignore_Me()) { res |= obj->Cast_Ray(raytest); } obj = get_next_object(obj); } } /* ** If it wasn't culled, collision test on to the children */ if (node->Back) { res = res | Cast_Ray_Recursive(node->Back,raytest); } if (node->Front) { res = res | Cast_Ray_Recursive(node->Front,raytest); } return res; } bool PhysAABTreeCullClass::Cast_AABox_Recursive ( AABTreeNodeClass * node, PhysAABoxCollisionTestClass & boxtest ) { /* ** Cull the collision test against the bounding volume of this node ** If it is culled, stop descending the tree. */ if (boxtest.Cull(node->Box)) { return false; } /* ** Test any objects in this node */ bool res = false; if (node->Object) { PhysClass * obj = get_first_object(node); while (obj) { if ( Scene->Do_Groups_Collide(obj->Get_Collision_Group(),boxtest.CollisionGroup) && !obj->Is_Ignore_Me() ) { res |= obj->Cast_AABox(boxtest); } obj = get_next_object(obj); } } /* ** If it wasn't culled, collision test on to the children */ if (node->Back) { res = res | Cast_AABox_Recursive(node->Back,boxtest); } if (node->Front) { res = res | Cast_AABox_Recursive(node->Front,boxtest); } return res; } bool PhysAABTreeCullClass::Cast_OBBox_Recursive ( AABTreeNodeClass * node, PhysOBBoxCollisionTestClass & boxtest ) { /* ** Cull the collision test against the bounding volume of this node ** If it is culled, stop descending the tree. */ if (boxtest.Cull(node->Box)) { return false; } /* ** Test any objects in this node */ bool res = false; if (node->Object) { PhysClass * obj = get_first_object(node); while (obj) { if ( Scene->Do_Groups_Collide(obj->Get_Collision_Group(),boxtest.CollisionGroup) && !obj->Is_Ignore_Me() ) { res |= obj->Cast_OBBox(boxtest); } obj = get_next_object(obj); } } /* ** If it wasn't culled, collision test on to the children */ if (node->Back) { res = res | Cast_OBBox_Recursive(node->Back,boxtest); } if (node->Front) { res = res | Cast_OBBox_Recursive(node->Front,boxtest); } return res; } bool PhysAABTreeCullClass::Intersection_Test(PhysAABoxIntersectionTestClass & boxtest) { Reset_Collection(); Collect_Objects(boxtest.Box); PhysClass * obj = Get_First_Collected_Object(); while (obj != NULL) { if ( Scene->Do_Groups_Collide(obj->Get_Collision_Group(),boxtest.CollisionGroup) && !obj->Is_Ignore_Me() ) { if (obj->Intersection_Test(boxtest)) { return true; } } obj = Get_Next_Collected_Object(obj); } return false; } bool PhysAABTreeCullClass::Intersection_Test(PhysOBBoxIntersectionTestClass & boxtest) { Reset_Collection(); Collect_Objects(boxtest.BoundingBox); PhysClass * obj = Get_First_Collected_Object(); while (obj != NULL) { if ( Scene->Do_Groups_Collide(obj->Get_Collision_Group(),boxtest.CollisionGroup) && !obj->Is_Ignore_Me() ) { if (obj->Intersection_Test(boxtest)) { return true; } } obj = Get_Next_Collected_Object(obj); } return false; } bool PhysAABTreeCullClass::Intersection_Test(PhysMeshIntersectionTestClass & meshtest) { Reset_Collection(); Collect_Objects(meshtest.BoundingBox); PhysClass * obj = Get_First_Collected_Object(); while (obj != NULL) { if ( Scene->Do_Groups_Collide(obj->Get_Collision_Group(),meshtest.CollisionGroup) && !obj->Is_Ignore_Me() ) { if (obj->Intersection_Test(meshtest)) { return true; } } obj = Get_Next_Collected_Object(obj); } return false; } bool PhysAABTreeCullClass::Verify_Recursive(AABTreeNodeClass * node,StringClass & error_report) { /* ** Test any objects in this node */ bool res = true; StringClass working; if (node->Object) { PhysClass * obj = get_first_object(node); while (obj) { const AABoxClass & box = node->Box; const AABoxClass & box2 = obj->Get_Cull_Box(); bool outside = false; Vector3 dc; Vector3::Subtract(box.Center,box2.Center,&dc); if (box.Extent.X + box2.Extent.X < WWMath::Fabs(dc.X)) outside = true; if (box.Extent.Y + box2.Extent.Y < WWMath::Fabs(dc.Y)) outside = true; if (box.Extent.Z + box2.Extent.Z < WWMath::Fabs(dc.Z)) outside = true; if ( (dc.X + box2.Extent.X <= box.Extent.X + WWMATH_EPSILON)& (dc.Y + box2.Extent.Y <= box.Extent.Y + WWMATH_EPSILON) && (dc.Z + box2.Extent.Z <= box.Extent.Z + WWMATH_EPSILON) && (dc.X - box2.Extent.X >= -box.Extent.X - WWMATH_EPSILON) && (dc.Y - box2.Extent.Y >= -box.Extent.Y - WWMATH_EPSILON) && (dc.Z - box2.Extent.Z >= -box.Extent.Z - WWMATH_EPSILON)) { outside = false; } else { outside = true; } if ( outside ) { //CollisionMath::Overlap_Test(node->Box,obj->Get_Cull_Box()) != CollisionMath::INSIDE) { //CollisionMath::Overlap_Test(node->Box,obj->Get_Cull_Box()); working.Format("Node %d doesn't contain object: %s\r\n",node->Index,obj->Peek_Model()->Get_Name()); error_report += working; res = false; } obj = get_next_object(obj); } } /* ** If it wasn't culled, collision test on to the children */ if (node->Back) { res &= Verify_Recursive(node->Back,error_report); } if (node->Front) { res &= Verify_Recursive(node->Front,error_report); } return res; }