335 lines
8.9 KiB
C++
335 lines
8.9 KiB
C++
/*
|
|
** 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/***********************************************************************************************
|
|
*** 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;
|
|
}
|