mirror of
https://github.com/electronicarts/CnC_Renegade.git
synced 2025-12-16 07:31:40 -05:00
Initial commit of Command & Conquer Renegade source code.
This commit is contained in:
349
Code/wwphys/octbox.cpp
Normal file
349
Code/wwphys/octbox.cpp
Normal file
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
** 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/octbox.cpp $*
|
||||
* *
|
||||
* Original Author:: Greg Hjelstrom *
|
||||
* *
|
||||
* $Author:: Greg_h $*
|
||||
* *
|
||||
* $Modtime:: 11/21/01 2:54p $*
|
||||
* *
|
||||
* $Revision:: 20 $*
|
||||
* *
|
||||
*---------------------------------------------------------------------------------------------*
|
||||
* Functions: *
|
||||
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
#include "octbox.h"
|
||||
#include "pscene.h"
|
||||
#include "physcoltest.h"
|
||||
#include "physinttest.h"
|
||||
#include "physcon.h"
|
||||
|
||||
#define SHOW_CONTACT_DETECTORS 0
|
||||
|
||||
const float DEFAULT_STIFFNESS = 5.0f;
|
||||
const float DEFAULT_DAMPING = 0.1f;
|
||||
const float DEFAULT_THICKNESS = 0.1f;
|
||||
|
||||
const float CONTACT_GRAVITY_MULTIPLIER = 2.0f; // maximum contact force is multiplier * gravity.
|
||||
const float CONTACT_DAMPING_FACTOR = 0.33f; // ratio of damping / (critical damping) (less than zero is underdamped)
|
||||
|
||||
|
||||
OctBoxClass::OctBoxClass
|
||||
(
|
||||
RigidBodyClass & parent,
|
||||
const OBBoxClass & box
|
||||
) :
|
||||
Parent(parent),
|
||||
InnerBox(box),
|
||||
Thickness(DEFAULT_THICKNESS),
|
||||
Stiffness(DEFAULT_STIFFNESS),
|
||||
Damping(DEFAULT_DAMPING)
|
||||
{
|
||||
Set_Thickness(Thickness);
|
||||
}
|
||||
|
||||
|
||||
OctBoxClass::~OctBoxClass(void)
|
||||
{
|
||||
}
|
||||
|
||||
void OctBoxClass::Set_Thickness(float thickness)
|
||||
{
|
||||
Thickness = thickness;
|
||||
|
||||
// Outer corner of the "octant boxes" should be at the corner of the box
|
||||
// Inner corner of the "octant boxes" should be overlapping the neighbor boxes by 'thickness'
|
||||
Vector3 octant_max = InnerBox.Extent;
|
||||
Vector3 octant_min(-Thickness,-Thickness,-Thickness);
|
||||
|
||||
OctantExtent = (octant_max - octant_min) / 2.0f;
|
||||
OctantCenter = (octant_max + octant_min) / 2.0f;
|
||||
}
|
||||
|
||||
void OctBoxClass::Update_Contact_Parameters(void)
|
||||
{
|
||||
float mass = Parent.Get_Mass();
|
||||
|
||||
// Make the contact force have a maximum of MULTIPLIER * gravity
|
||||
Stiffness = mass * WWMath::Fabs(PhysicsConstants::GravityAcceleration.Z);
|
||||
Stiffness *= CONTACT_GRAVITY_MULTIPLIER;
|
||||
|
||||
// Compute critical damping, achieve equilibrium in minimum time.
|
||||
Damping = 2.0f * mass * WWMath::Sqrt(Stiffness / mass);
|
||||
|
||||
// Critical damping seems to be too stiff so reduce it
|
||||
Damping *= CONTACT_DAMPING_FACTOR;
|
||||
}
|
||||
|
||||
void OctBoxClass::Get_Outer_Bounds(AABoxClass * set_bounds)
|
||||
{
|
||||
WWASSERT(set_bounds != NULL);
|
||||
OBBoxClass wrld_outer_box;
|
||||
wrld_outer_box = WrldInnerBox;
|
||||
wrld_outer_box.Extent += Vector3(Thickness,Thickness,Thickness);
|
||||
|
||||
set_bounds->Center = wrld_outer_box.Center;
|
||||
wrld_outer_box.Compute_Axis_Aligned_Extent(&(set_bounds->Extent));
|
||||
}
|
||||
|
||||
bool OctBoxClass::Is_Intersecting(NonRefPhysListClass * result_list,bool check_static_objs,bool check_dyn_objs)
|
||||
{
|
||||
PhysicsSceneClass * the_scene = PhysicsSceneClass::Get_Instance();
|
||||
WWASSERT(the_scene != NULL);
|
||||
|
||||
/*
|
||||
** Test inner box for intersection
|
||||
*/
|
||||
PhysOBBoxIntersectionTestClass test(WrldInnerBox,
|
||||
Parent.Get_Collision_Group(),
|
||||
COLLISION_TYPE_PHYSICAL | COLLISION_TYPE_VEHICLE,
|
||||
result_list);
|
||||
|
||||
test.CheckStaticObjs = check_static_objs;
|
||||
test.CheckDynamicObjs = check_dyn_objs;
|
||||
|
||||
Parent.Inc_Ignore_Counter ();
|
||||
bool intersect = PhysicsSceneClass::Get_Instance ()->Intersection_Test(test);
|
||||
Parent.Dec_Ignore_Counter ();
|
||||
return intersect;
|
||||
}
|
||||
|
||||
bool OctBoxClass::Is_In_Contact_Zone(void)
|
||||
{
|
||||
PhysicsSceneClass * the_scene = PhysicsSceneClass::Get_Instance();
|
||||
WWASSERT(the_scene != NULL);
|
||||
|
||||
/*
|
||||
** Test outer box for intersection
|
||||
*/
|
||||
OBBoxClass wrld_outer_box = WrldInnerBox;
|
||||
wrld_outer_box.Extent += Vector3(Thickness,Thickness,Thickness);
|
||||
|
||||
int colgroup = Parent.Get_Collision_Group();
|
||||
int coltype = COLLISION_TYPE_PHYSICAL | COLLISION_TYPE_VEHICLE;
|
||||
return the_scene->Intersection_Test(wrld_outer_box,colgroup,coltype,true);
|
||||
}
|
||||
|
||||
|
||||
OctBoxClass::CollisionResult
|
||||
OctBoxClass::Compute_Contacts(bool lock_to_centroids)
|
||||
{
|
||||
Parent.Inc_Ignore_Counter();
|
||||
CollisionResult result = Internal_Compute_Contacts(lock_to_centroids);
|
||||
Parent.Dec_Ignore_Counter();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
OctBoxClass::CollisionResult
|
||||
OctBoxClass::Internal_Compute_Contacts(bool lock_to_centroids)
|
||||
{
|
||||
/*
|
||||
** Algorithm:
|
||||
** Quick Rejection:
|
||||
** - Try to quick reject the entire collision by checking the outer box
|
||||
** - Check if we're going to have to search for TOC by checking the inner box
|
||||
** Find Contacts:
|
||||
** - For each octant (total of 8)
|
||||
** - Sweep the octant along the diagonal and record the contact point (if any)
|
||||
*/
|
||||
PhysicsSceneClass * the_scene = PhysicsSceneClass::Get_Instance();
|
||||
WWASSERT(the_scene != NULL);
|
||||
Reset_Contacts();
|
||||
|
||||
/*
|
||||
** compute the outer box in WS
|
||||
*/
|
||||
OBBoxClass wrld_outer_box;
|
||||
wrld_outer_box = WrldInnerBox;
|
||||
wrld_outer_box.Extent += Vector3(Thickness,Thickness,Thickness);
|
||||
|
||||
/*
|
||||
** Check for quick-rejection (intersection or completely separated)
|
||||
*/
|
||||
int colgroup = Parent.Get_Collision_Group();
|
||||
int coltype = COLLISION_TYPE_PHYSICAL | COLLISION_TYPE_VEHICLE;
|
||||
|
||||
if (!the_scene->Intersection_Test(wrld_outer_box,colgroup,coltype,true)) {
|
||||
//Parent.Add_Debug_OBBox(wrld_outer_box,Vector3(0,1,0));
|
||||
return RESULT_NO_COLLISION;
|
||||
}
|
||||
|
||||
if (the_scene->Intersection_Test(WrldInnerBox,colgroup,coltype,true)) {
|
||||
//Parent.Add_Debug_OBBox(wrld_outer_box,Vector3(1,0,0));
|
||||
return RESULT_INTERSECTION;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check the octants for contact
|
||||
*/
|
||||
for (int i=0; i<8; i++) {
|
||||
Compute_Octant_Contact(i,lock_to_centroids);
|
||||
}
|
||||
|
||||
return RESULT_CONTACT;
|
||||
}
|
||||
|
||||
|
||||
void OctBoxClass::Compute_Octant_Contact(int oi,bool lock_to_centroids)
|
||||
{
|
||||
static Vector3 _octant_offset[8] =
|
||||
{
|
||||
Vector3( 1, 1, 1),
|
||||
Vector3(-1, 1, 1),
|
||||
Vector3(-1,-1, 1),
|
||||
Vector3( 1,-1, 1),
|
||||
Vector3( 1, 1,-1),
|
||||
Vector3(-1, 1,-1),
|
||||
Vector3(-1,-1,-1),
|
||||
Vector3( 1,-1,-1),
|
||||
};
|
||||
|
||||
/*
|
||||
** Compute the corner contact "hair" which doubles as the
|
||||
** move vector for the octant box.
|
||||
*/
|
||||
CastResultStruct corner_result;
|
||||
corner_result.ComputeContactPoint = true;
|
||||
|
||||
Vector3 corner = _octant_offset[oi];
|
||||
corner.X *= WrldInnerBox.Extent.X;
|
||||
corner.Y *= WrldInnerBox.Extent.Y;
|
||||
corner.Z *= WrldInnerBox.Extent.Z;
|
||||
Matrix3::Rotate_Vector(WrldInnerBox.Basis,corner,&corner);
|
||||
corner += WrldInnerBox.Center;
|
||||
|
||||
Vector3 corner_move = Thickness * _octant_offset[oi];
|
||||
Matrix3::Rotate_Vector(WrldInnerBox.Basis,corner_move,&corner_move);
|
||||
|
||||
/*
|
||||
** Collision detect for the corner line-segment
|
||||
*/
|
||||
LineSegClass ray(corner,corner + corner_move);
|
||||
WWASSERT(corner_move.Length2() <= ((3*Thickness*Thickness) + 0.01f));
|
||||
WWASSERT(ray.Get_DP().Length2() <= ((3*Thickness*Thickness) + 0.01f));
|
||||
|
||||
PhysRayCollisionTestClass raytest( ray,
|
||||
&corner_result,
|
||||
Parent.Get_Collision_Group(),
|
||||
COLLISION_TYPE_PHYSICAL | COLLISION_TYPE_VEHICLE);
|
||||
PhysicsSceneClass::Get_Instance()->Cast_Ray(raytest,true);
|
||||
|
||||
#ifdef WWDEBUG
|
||||
#if SHOW_CONTACT_DETECTORS
|
||||
Parent.Add_Debug_Vector(corner,corner_move,Vector3(0,1,0));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Compute the contact for this octant-box.
|
||||
*/
|
||||
OBBoxClass octbox = WrldInnerBox;
|
||||
octbox.Extent = OctantExtent;
|
||||
|
||||
Vector3 dc = _octant_offset[oi];
|
||||
dc.X *= OctantCenter.X;
|
||||
dc.Y *= OctantCenter.Y;
|
||||
dc.Z *= OctantCenter.Z;
|
||||
Matrix3::Rotate_Vector(WrldInnerBox.Basis,dc,&dc);
|
||||
octbox.Center += dc;
|
||||
|
||||
/*
|
||||
** Collision detect with this octant box
|
||||
*/
|
||||
CastResultStruct octant_result;
|
||||
octant_result.ComputeContactPoint = true;
|
||||
PhysOBBoxCollisionTestClass boxtest( octbox,
|
||||
corner_move,
|
||||
&octant_result,
|
||||
Parent.Get_Collision_Group(),
|
||||
COLLISION_TYPE_PHYSICAL | COLLISION_TYPE_VEHICLE);
|
||||
PhysicsSceneClass::Get_Instance()->Cast_OBBox(boxtest,true);
|
||||
|
||||
/*
|
||||
** Wake up any vehicle in contact with us
|
||||
*/
|
||||
if ((boxtest.CollidedPhysObj != NULL) && (boxtest.CollidedPhysObj->As_RigidBodyClass() != NULL)) {
|
||||
boxtest.CollidedPhysObj->Force_Awake();
|
||||
}
|
||||
|
||||
/*
|
||||
** Display the octant box
|
||||
*/
|
||||
#ifdef WWDEBUG
|
||||
#if SHOW_CONTACT_DETECTORS
|
||||
static Vector3 _oct_colors[8] = {
|
||||
Vector3(1,0,0),Vector3(0,1,0),Vector3(0,0,1),Vector3(1,1,1),
|
||||
Vector3(1,1,1),Vector3(0,0,1),Vector3(0,1,0),Vector3(1,0,0)
|
||||
};
|
||||
|
||||
octbox.Center += octant_result.Fraction * corner_move;
|
||||
//Parent.Add_Debug_OBBox(octbox,_oct_colors[oi]);
|
||||
//Parent.Add_Debug_Vector(result.Point,result.Normal,Vector3(0,1,0));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Now, decide which (if any) contact to use. We prefer the
|
||||
** corners but have to use the box's contact if it is significantly
|
||||
** closer. If neither hits anything, just bail out of this routine.
|
||||
*/
|
||||
if ((corner_result.Fraction >= 1.0f) && (octant_result.Fraction >= 1.0f)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** If the corner is within 'CORNER_BIAS' of the result of the octant
|
||||
** then use its result
|
||||
*/
|
||||
const float CORNER_BIAS = 0.25f;
|
||||
if ((corner_result.Fraction < 1.0f) && (corner_result.Fraction - CORNER_BIAS < octant_result.Fraction)) {
|
||||
|
||||
if (corner_result.Normal.Length2() > 0.0f) { // the normal will be 0,0,0 when we hit a backside
|
||||
Add_Contact(corner_result,raytest.CollidedPhysObj);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if ((octant_result.Fraction < 1.0f) && (!octant_result.StartBad)) {
|
||||
if (octant_result.Normal.Length2() > 0.0f) { // the normal will be 0,0,0 when we hit a backside
|
||||
Add_Contact(octant_result,boxtest.CollidedPhysObj);
|
||||
}
|
||||
}
|
||||
|
||||
if (corner_result.Fraction < 1.0f) {
|
||||
// also add the corner contact!
|
||||
if (corner_result.Normal.Length2() > 0.0f) { // the normal will be 0,0,0 when we hit a backside
|
||||
Add_Contact(corner_result,raytest.CollidedPhysObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user