This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/wwphys/pscene_collision.cpp

686 lines
17 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/pscene_collision.cpp $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 2/13/02 2:16p $*
* *
* $Revision:: 10 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "pscene.h"
#include "physcoltest.h"
#include "physinttest.h"
#include "staticaabtreecull.h"
#include "dynamicaabtreecull.h"
#include "physgridcull.h"
#include "lightcull.h"
#include "staticphys.h"
bool PhysicsSceneClass::Do_Groups_Collide(int group0,int group1)
{
int index = group0 | (group1 << COLLISION_FLAG_SHIFT);
return AllowCollisionFlags[index];
}
void PhysicsSceneClass::Enable_Collision_Detection(int group0,int group1)
{
assert(group0 >= 0);
assert(group1 >= 0);
assert(group0 <= MAX_COLLISION_GROUP);
assert(group1 <= MAX_COLLISION_GROUP);
unsigned int index;
index = group0 | (group1 << COLLISION_FLAG_SHIFT);
AllowCollisionFlags[index] = 1;
index = group1 | (group0 << COLLISION_FLAG_SHIFT);
AllowCollisionFlags[index] = 1;
}
void PhysicsSceneClass::Disable_Collision_Detection(int group0,int group1)
{
assert(group0 >= 0);
assert(group1 >= 0);
assert(group0 <= MAX_COLLISION_GROUP);
assert(group1 <= MAX_COLLISION_GROUP);
unsigned int index;
index = group0 | (group1 << COLLISION_FLAG_SHIFT);
AllowCollisionFlags[index] = 0;
index = group1 | (group0 << COLLISION_FLAG_SHIFT);
AllowCollisionFlags[index] = 0;
}
void PhysicsSceneClass::Enable_All_Collision_Detections(int group)
{
assert(group >= 0);
assert(group <= MAX_COLLISION_GROUP);
for (int i=0; i <= MAX_COLLISION_GROUP; i++) {
unsigned int index;
index = group | (i << COLLISION_FLAG_SHIFT);
AllowCollisionFlags[index] = 1;
index = i | (group << COLLISION_FLAG_SHIFT);
AllowCollisionFlags[index] = 1;
}
}
void PhysicsSceneClass::Disable_All_Collision_Detections(int group)
{
assert(group >= 0);
assert(group <= MAX_COLLISION_GROUP);
for (int i=0; i <= MAX_COLLISION_GROUP; i++) {
unsigned int index;
index = group | (i << COLLISION_FLAG_SHIFT);
AllowCollisionFlags[index] = 0;
index = i | (group << COLLISION_FLAG_SHIFT);
AllowCollisionFlags[index] = 0;
}
}
void PhysicsSceneClass::Set_Collision_Region(const AABoxClass & bounds,int colgroup)
{
Collect_Collideable_Objects(bounds,colgroup,true,true,&CollisionRegionList);
}
void PhysicsSceneClass::Release_Collision_Region(void)
{
CollisionRegionList.Reset_List();
}
bool PhysicsSceneClass::Cast_Ray(PhysRayCollisionTestClass & raytest,bool use_collision_region)
{
/*
** Assert that the result structure has been initialized with the
** optimistic result that the entire move will be taken. Each call
** should whittle down the Fraction variable so that we are left
** with the fraction that the closest object allowed. If StartBad
** is ever set to true, we can bail out.
*/
assert(raytest.Result->Fraction == 1.0f);
assert(raytest.Result->StartBad == false);
raytest.CollidedPhysObj = NULL;
/*
** Check against physical objects in our vicinity
*/
bool res = false;
if (use_collision_region) {
/*
** Use the cached collision region list
*/
NonRefPhysListIterator it(&CollisionRegionList);
for ( ; !it.Is_Done(); it.Next()) {
PhysClass * obj = it.Peek_Obj();
if ( Do_Groups_Collide(obj->Get_Collision_Group(),raytest.CollisionGroup) &&
!obj->Is_Ignore_Me() )
{
res |= obj->Cast_Ray(raytest);
}
}
} else {
/*
** Cull the collision check using the culling systems
*/
if (raytest.CheckStaticObjs) {
res |= StaticCullingSystem->Cast_Ray(raytest);
if (raytest.Result->StartBad) return true;
}
if (raytest.CheckDynamicObjs) {
res |= DynamicCullingSystem->Cast_Ray(raytest);
if (raytest.Result->StartBad) return true;
}
}
return res;
}
bool PhysicsSceneClass::Cast_AABox(PhysAABoxCollisionTestClass & boxtest,bool use_collision_region)
{
/*
** Assert that the result structure has been initialized with the
** optimistic result that the entire move will be taken. Each call
** should whittle down the Fraction variable so that we are left
** with the fraction that the closest object allowed. If StartBad
** is ever set to true, we can bail out.
*/
WWASSERT(boxtest.Result->Fraction == 1.0f);
WWASSERT(boxtest.Result->StartBad == false);
boxtest.CollidedPhysObj = NULL;
/*
** Check against physical objects in our vicinity
*/
bool res = false;
if (use_collision_region) {
/*
** Use the cached collision region list
*/
NonRefPhysListIterator it(&CollisionRegionList);
for ( ; !it.Is_Done(); it.Next()) {
PhysClass * obj = it.Peek_Obj();
if ( Do_Groups_Collide(obj->Get_Collision_Group(),boxtest.CollisionGroup) &&
!obj->Is_Ignore_Me() )
{
res |= obj->Cast_AABox(boxtest);
}
}
} else {
/*
** Cull the collision check using the culling systems
*/
if (boxtest.CheckStaticObjs) {
res |= StaticCullingSystem->Cast_AABox(boxtest);
if (boxtest.Result->StartBad) return true;
}
if (boxtest.CheckDynamicObjs) {
res |= DynamicCullingSystem->Cast_AABox(boxtest);
if (boxtest.Result->StartBad) return true;
}
}
return res;
}
bool PhysicsSceneClass::Cast_OBBox(PhysOBBoxCollisionTestClass & boxtest,bool use_collision_region)
{
/*
** Assert that the result structure has been initialized with the
** optimistic result that the entire move will be taken. Each call
** should whittle down the Fraction variable so that we are left
** with the fraction that the closest object allowed. If StartBad
** is ever set to true, we can bail out.
*/
assert(boxtest.Result->Fraction == 1.0f);
assert(boxtest.Result->StartBad == false);
boxtest.CollidedPhysObj = NULL;
/*
** Check against physical objects in our vicinity
*/
bool res = false;
if (use_collision_region) {
/*
** Use the cached collision region list
*/
NonRefPhysListIterator it(&CollisionRegionList);
for ( ; !it.Is_Done(); it.Next()) {
PhysClass * obj = it.Peek_Obj();
if ( Do_Groups_Collide(obj->Get_Collision_Group(),boxtest.CollisionGroup) &&
!obj->Is_Ignore_Me() )
{
res |= obj->Cast_OBBox(boxtest);
}
}
} else {
/*
** Cull the collision check using the culling systems
*/
if (boxtest.CheckStaticObjs) {
res |= StaticCullingSystem->Cast_OBBox(boxtest);
if (boxtest.Result->StartBad) return true;
}
if (boxtest.CheckDynamicObjs) {
res |= DynamicCullingSystem->Cast_OBBox(boxtest);
if (boxtest.Result->StartBad) return true;
}
}
return res;
}
bool PhysicsSceneClass::Intersection_Test(PhysAABoxIntersectionTestClass & boxtest,bool use_collision_region)
{
if (use_collision_region) {
/*
** Test for intersection with objects in the cached collision region
*/
NonRefPhysListIterator it(&CollisionRegionList);
for ( ; !it.Is_Done(); it.Next()) {
PhysClass * obj = it.Peek_Obj();
if ( Do_Groups_Collide(obj->Get_Collision_Group(),boxtest.CollisionGroup) &&
!obj->Is_Ignore_Me() )
{
if (obj->Intersection_Test(boxtest)) {
return true;
}
}
}
} else {
/*
** Test for intersection with objects in the static and dynamic culling systems
*/
if (boxtest.CheckStaticObjs) {
if (StaticCullingSystem->Intersection_Test(boxtest)) {
return true;
}
}
if (boxtest.CheckDynamicObjs) {
if (DynamicCullingSystem->Intersection_Test(boxtest)) {
return true;
}
}
}
return false;
}
bool PhysicsSceneClass::Intersection_Test(PhysOBBoxIntersectionTestClass & boxtest,bool use_collision_region)
{
if (use_collision_region) {
/*
** Test for intersection with objects in the cached collision region
*/
NonRefPhysListIterator it(&CollisionRegionList);
for ( ; !it.Is_Done(); it.Next()) {
PhysClass * obj = it.Peek_Obj();
if ( Do_Groups_Collide(obj->Get_Collision_Group(),boxtest.CollisionGroup) &&
!obj->Is_Ignore_Me() )
{
if (obj->Intersection_Test(boxtest)) {
return true;
}
}
}
} else {
/*
** Test for intersection with objects in the static and dynamic culling systems
*/
if (boxtest.CheckStaticObjs) {
if (StaticCullingSystem->Intersection_Test(boxtest)) {
return true;
}
}
if (boxtest.CheckDynamicObjs) {
if (DynamicCullingSystem->Intersection_Test(boxtest)) {
return true;
}
}
}
return false;
}
bool PhysicsSceneClass::Intersection_Test(PhysMeshIntersectionTestClass & meshtest,bool use_collision_region)
{
if (use_collision_region) {
/*
** Test for intersection with objects in the cached collision region
*/
NonRefPhysListIterator it(&CollisionRegionList);
for ( ; !it.Is_Done(); it.Next()) {
PhysClass * obj = it.Peek_Obj();
if ( Do_Groups_Collide(obj->Get_Collision_Group(),meshtest.CollisionGroup) &&
!obj->Is_Ignore_Me() )
{
if (obj->Intersection_Test(meshtest)) {
return true;
}
}
}
} else {
if (meshtest.CheckStaticObjs) {
if (StaticCullingSystem->Intersection_Test(meshtest)) {
return true;
}
}
if (meshtest.CheckDynamicObjs) {
if (DynamicCullingSystem->Intersection_Test(meshtest)) {
return true;
}
}
}
return false;
}
bool PhysicsSceneClass::Intersection_Test(const AABoxClass & box,int collision_group,int collision_type,bool use_collision_region)
{
NonRefPhysListClass intersect_list;
PhysAABoxIntersectionTestClass test(box,collision_group,collision_type,&intersect_list);
return Intersection_Test(test,use_collision_region);
}
bool PhysicsSceneClass::Intersection_Test(const OBBoxClass & box,int collision_group,int collision_type,bool use_collision_region)
{
NonRefPhysListClass intersect_list;
PhysOBBoxIntersectionTestClass test(box,collision_group,collision_type,&intersect_list);
return Intersection_Test(test,use_collision_region);
}
void PhysicsSceneClass::Add_Collected_Objects_To_List
(
bool static_objs,
bool dynamic_objs,
NonRefPhysListClass * list
)
{
// link the static objects
if (static_objs) {
StaticPhysClass * obj;
for ( obj = (StaticPhysClass *)StaticCullingSystem->Get_First_Collected_Object();
obj != NULL;
obj = (StaticPhysClass *)StaticCullingSystem->Get_Next_Collected_Object(obj) )
{
list->Add(obj);
}
}
// link the dynamic objects
if (dynamic_objs) {
PhysClass * obj;
for ( obj = (PhysClass *)DynamicCullingSystem->Get_First_Collected_Object();
obj != NULL;
obj = (PhysClass *)DynamicCullingSystem->Get_Next_Collected_Object(obj) )
{
list->Add(obj);
}
}
}
void PhysicsSceneClass::Add_Collected_Collideable_Objects_To_List
(
int colgroup,
bool static_objs,
bool dynamic_objs,
NonRefPhysListClass * list
)
{
// link the static objects
if (static_objs) {
StaticPhysClass * obj;
for ( obj = (StaticPhysClass *)StaticCullingSystem->Get_First_Collected_Object();
obj != NULL;
obj = (StaticPhysClass *)StaticCullingSystem->Get_Next_Collected_Object(obj) )
{
if ( Do_Groups_Collide(obj->Get_Collision_Group(),colgroup) &&
!obj->Is_Ignore_Me() )
{
list->Add(obj);
}
}
}
// link the dynamic objects
if (dynamic_objs) {
PhysClass * obj;
for ( obj = (PhysClass *)DynamicCullingSystem->Get_First_Collected_Object();
obj != NULL;
obj = (PhysClass *)DynamicCullingSystem->Get_Next_Collected_Object(obj) )
{
if ( Do_Groups_Collide(obj->Get_Collision_Group(),colgroup) &&
!obj->Is_Ignore_Me() )
{
list->Add(obj);
}
}
}
}
void PhysicsSceneClass::Collect_Objects
(
const Vector3 & point,
bool static_objs,
bool dynamic_objs,
NonRefPhysListClass * list
)
{
WWASSERT(list != NULL);
if (static_objs) {
StaticCullingSystem->Reset_Collection();
StaticCullingSystem->Collect_Objects(point);
}
if (dynamic_objs) {
DynamicCullingSystem->Reset_Collection();
DynamicCullingSystem->Collect_Objects(point);
}
Add_Collected_Objects_To_List(static_objs,dynamic_objs,list);
}
void PhysicsSceneClass::Collect_Objects
(
const AABoxClass & box,
bool static_objs,
bool dynamic_objs,
NonRefPhysListClass * list
)
{
WWASSERT(list != NULL);
if (static_objs) {
StaticCullingSystem->Reset_Collection();
StaticCullingSystem->Collect_Objects(box);
}
if (dynamic_objs) {
DynamicCullingSystem->Reset_Collection();
DynamicCullingSystem->Collect_Objects(box);
}
Add_Collected_Objects_To_List(static_objs,dynamic_objs,list);
}
void PhysicsSceneClass::Collect_Objects
(
const OBBoxClass & box,
bool static_objs,
bool dynamic_objs,
NonRefPhysListClass * list
)
{
WWASSERT(list != NULL);
if (static_objs) {
StaticCullingSystem->Reset_Collection();
StaticCullingSystem->Collect_Objects(box);
}
if (dynamic_objs) {
DynamicCullingSystem->Reset_Collection();
DynamicCullingSystem->Collect_Objects(box);
}
Add_Collected_Objects_To_List(static_objs,dynamic_objs,list);
}
void PhysicsSceneClass::Collect_Objects
(
const FrustumClass & frustum,
bool static_objs,
bool dynamic_objs,
NonRefPhysListClass * list
)
{
WWASSERT(list != NULL);
if (static_objs) {
StaticCullingSystem->Reset_Collection();
StaticCullingSystem->Collect_Objects(frustum);
}
if (dynamic_objs) {
DynamicCullingSystem->Reset_Collection();
DynamicCullingSystem->Collect_Objects(frustum);
}
Add_Collected_Objects_To_List(static_objs,dynamic_objs,list);
}
void PhysicsSceneClass::Collect_Collideable_Objects
(
const AABoxClass & box,
int colgroup,
bool static_objs,
bool dynamic_objs,
NonRefPhysListClass * list
)
{
WWASSERT(list != NULL);
if (static_objs) {
StaticCullingSystem->Reset_Collection();
StaticCullingSystem->Collect_Objects(box);
}
if (dynamic_objs) {
DynamicCullingSystem->Reset_Collection();
DynamicCullingSystem->Collect_Objects(box);
}
Add_Collected_Collideable_Objects_To_List(colgroup,static_objs,dynamic_objs,list);
}
void PhysicsSceneClass::Collect_Collideable_Objects
(
const OBBoxClass & box,
int colgroup,
bool static_objs,
bool dynamic_objs,
NonRefPhysListClass * list
)
{
WWASSERT(list != NULL);
if (static_objs) {
StaticCullingSystem->Reset_Collection();
StaticCullingSystem->Collect_Objects(box);
}
if (dynamic_objs) {
DynamicCullingSystem->Reset_Collection();
DynamicCullingSystem->Collect_Objects(box);
}
Add_Collected_Collideable_Objects_To_List(colgroup,static_objs,dynamic_objs,list);
}
void PhysicsSceneClass::Add_Collected_Lights_To_List
(
bool static_lights,
bool dynamic_lights,
NonRefPhysListClass * list
)
{
// link the static lights
if (static_lights) {
LightPhysClass * obj;
for ( obj = StaticLightingSystem->Get_First_Collected_Object();
obj != NULL;
obj = StaticLightingSystem->Get_Next_Collected_Object(obj) )
{
list->Add(obj);
}
}
// link the dynamic lights
// TODO!!
}
void PhysicsSceneClass::Collect_Lights
(
const Vector3 & point,
bool static_lights,
bool dynamic_lights,
NonRefPhysListClass * list
)
{
WWASSERT(list != NULL);
if (static_lights) {
StaticLightingSystem->Reset_Collection();
StaticLightingSystem->Collect_Objects(point);
}
// TODO: Dynamic lights!!
Add_Collected_Lights_To_List(static_lights,dynamic_lights,list);
}
void PhysicsSceneClass::Collect_Lights
(
const AABoxClass & bounds,
bool static_lights,
bool dynamic_lights,
NonRefPhysListClass * list
)
{
WWASSERT(list != NULL);
if (static_lights) {
StaticLightingSystem->Reset_Collection();
StaticLightingSystem->Collect_Objects(bounds);
}
// TODO: Dynamic lights!!
Add_Collected_Lights_To_List(static_lights,dynamic_lights,list);
}