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/animcollisionmanager.cpp

1545 lines
78 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/animcollisionmanager.cpp $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 11/14/01 1:34p $*
* *
* $Revision:: 33 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* AnimCollisionManagerClass::CollideableObjClass::CollideableObjClass -- Constructor *
* AnimCollisionManagerClass::CollideableObjClass::~CollideableObjClass -- Destructor *
* AnimCollisionManagerClass::CollideableObjClass::operator = -- assignment operator *
* AnimCollisionManagerClass::CollideableObjClass::Set_Collision_Object -- set collision mes *
* AnimCollisionManagerClass::CollideableObjClass::Intersect_Scene -- intersection test *
* AnimCollisionManagerClass::CollideableObjClass::Clear_Collision_Bits -- disables collisio *
* AnimCollisionManagerClass::CollideableObjClass::Restore_Collision_Bits -- set collision b *
* AnimCollisionManagerClass::AnimCollisionManagerClass -- Constructor *
* AnimCollisionManagerClass::~AnimCollisionManagerClass -- Destructor *
* AnimCollisionManagerClass::Set_Animation_Mode -- set the current anim mode *
* AnimCollisionManagerClass::Get_Animation_Mode -- Get the current anim mode *
* AnimCollisionManagerClass::Set_Animation -- Set the current animation *
* AnimCollisionManagerClass::Peek_Animation -- Get the current animation *
* AnimCollisionManagerClass::Set_Target_Frame -- Set the target animation frame *
* AnimCollisionManagerClass::Get_Target_Frame -- Get the target animation frame *
* AnimCollisionManagerClass::Set_Target_Frame_End -- Set the target frame to the end of the *
* AnimCollisionManagerClass::Is_At_Target -- returns true if in TARGET mode and done animat *
* AnimCollisionManagerClass::Set_Loop_Start -- Sets frame0 for an anim loop *
* AnimCollisionManagerClass::Set_Loop_End -- Sets frame1 for an anim loop *
* AnimCollisionManagerClass::Get_Loop_Start -- returns frame0 for an anim loop *
* AnimCollisionManagerClass::Get_Loop_End -- returns frame1 for an anim loop *
* AnimCollisionManagerClass::Set_Current_Frame -- Set the current frame *
* AnimCollisionManagerClass::Get_Current_Frame -- Get the current frame *
* AnimCollisionManagerClass::Set_Collision_Mode -- Set the current collision mode *
* AnimCollisionManagerClass::Get_Collision_Mode -- Get the current collision mode *
* AnimCollisionManagerClass::Init -- Init this collision manager *
* AnimCollisionManagerClass::Update_Cached_Model_Parameters -- find all of the collideable *
* AnimCollisionManagerClass::Is_Collision_Model -- classify a sub-object as collideable or *
* AnimCollisionManagerClass::Recursive_Count_Collision_Models -- count the collideable mesh *
* AnimCollisionManagerClass::Recursive_Collect_Collision_Models -- collect collideable mesh *
* AnimCollisionManagerClass::Timestep -- Update the state of this object *
* AnimCollisionManagerClass::Check_Collision -- Check the given collision object *
* AnimCollisionManagerClass::Is_Intersecting -- intersection test *
* AnimCollisionManagerClass::Push_Collided_Object -- push an object we collided with *
* AnimCollisionManagerClass::Save -- save! *
* AnimCollisionManagerClass::Load -- Load! *
* AnimCollisionManagerClass::Revert_Animation_State -- revert to previous anim frame *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "animcollisionmanager.h"
#include "colmathaabox.h"
#include "pscene.h"
#include "physinttest.h"
#include "movephys.h"
#include "bitstream.h"
#include "assetmgr.h"
#include "hanim.h"
#include "boxrobj.h"
#include "chunkio.h"
#include "wwdebug.h"
#include "wwprofile.h"
/*
** Set this if you want verbose logging. It will be un-set if debugging is not enabled...
*/
#define VERBOSE_LOGGING 0
#if VERBOSE_LOGGING
#define VERBOSE_LOG(x) /*if (strstr(Parent.Peek_Model()->Get_Name(),"HND_ELEV03"))*/ WWDEBUG_SAY(x)
#else
#define VERBOSE_LOG(x)
#endif
/**
** PushRecordClass
** This object is used to record each object's initial state before an animated collision
** moves them. At the end of an update, if the animated object has to revert to its initial state,
** all of the objects in the "PushList" will be reverted to there original states as well.
*/
class PushRecordClass : public AutoPoolClass<PushRecordClass,32>
{
public:
PushRecordClass(PhysClass * obj) : Object(NULL), Next(NULL) { REF_PTR_SET(Object,obj); WWASSERT(obj); Transform = obj->Get_Transform(); }
~PushRecordClass(void) { REF_PTR_RELEASE(Object); };
void Revert(void) { Object->Set_Transform(Transform); }
PushRecordClass * Get_Next(void) const { return Next; }
void Set_Next(PushRecordClass * next) { Next = next; }
static void Add_To_List(PushRecordClass ** head, PhysClass * obj)
{
PushRecordClass * new_rec = new PushRecordClass(obj);
new_rec->Set_Next(*head);
*head = new_rec;
}
static void Revert_List(PushRecordClass * head)
{
while (head) {
head->Revert();
head = head->Get_Next();
}
}
static void Delete_List(PushRecordClass * head)
{
while (head) {
PushRecordClass * next = head->Get_Next();
delete head;
head = next;
}
}
protected:
PhysClass * Object;
Matrix3D Transform;
PushRecordClass * Next;
};
DEFINE_AUTO_POOL(PushRecordClass,32);
/*
** Chunk ID's used by AnimCollisionManagerClass
*/
enum
{
ANIMCOLLISIONMANAGER_CHUNK_VARIABLES = 0525000421,
ANIMCOLLISIONMANAGER_VARIABLE_COLLISIONMODE = 0x00,
ANIMCOLLISIONMANAGER_VARIABLE_ANIMATIONMODE,
ANIMCOLLISIONMANAGER_VARIABLE_TARGETFRAME,
ANIMCOLLISIONMANAGER_VARIABLE_CURFRAME,
ANIMCOLLISIONMANAGER_VARIABLE_ANIMATIONNAME,
ANIMCOLLISIONMANAGER_VARIABLE_LOOPSTART,
ANIMCOLLISIONMANAGER_VARIABLE_LOOPEND,
ANIMCOLLISIONMANAGER_VARIABLE_PREVFRAME,
ANIMCOLLISIONMANAGER_VARIABLE_PREVANIMATIONNAME,
};
/********************************************************************************************
**
** AnimCollisionManagerClass::CollideableObjClass Implementation
**
********************************************************************************************/
/***********************************************************************************************
* AnimCollisionManagerClass::CollideableObjClass::CollideableObjClass -- Constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*=============================================================================================*/
AnimCollisionManagerClass::CollideableObjClass::CollideableObjClass(void) :
CollisionMesh(NULL),
StartTransform(1),
EndTransform(1)
{
}
/***********************************************************************************************
* AnimCollisionManagerClass::CollideableObjClass::CollideableObjClass -- Constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*=============================================================================================*/
AnimCollisionManagerClass::CollideableObjClass::CollideableObjClass
(
RenderObjClass * collisionmesh
) :
CollisionMesh(NULL),
StartTransform(1),
EndTransform(1)
{
Set_Collision_Object(collisionmesh);
}
/***********************************************************************************************
* AnimCollisionManagerClass::CollideableObjClass::CollideableObjClass -- Constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*=============================================================================================*/
AnimCollisionManagerClass::CollideableObjClass::CollideableObjClass
(
const CollideableObjClass & that
) :
CollisionMesh(NULL),
StartTransform(1),
EndTransform(1)
{
*this = that;
}
/***********************************************************************************************
* AnimCollisionManagerClass::CollideableObjClass::~CollideableObjClass -- Destructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
AnimCollisionManagerClass::CollideableObjClass::~CollideableObjClass(void)
{
REF_PTR_RELEASE(CollisionMesh);
}
/***********************************************************************************************
* AnimCollisionManagerClass::CollideableObjClass::operator = -- assignment operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
AnimCollisionManagerClass::CollideableObjClass &
AnimCollisionManagerClass::CollideableObjClass::operator = (const CollideableObjClass & that)
{
if (this != &that) {
REF_PTR_SET(CollisionMesh,that.CollisionMesh);
StartTransform = that.StartTransform;
EndTransform = that.EndTransform;
}
return *this;
}
/***********************************************************************************************
* AnimCollisionManagerClass::CollideableObjClass::Set_Collision_Object -- set collision mesh *
* *
* Sets the render object being tracked by this CollideableObjClass *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
void
AnimCollisionManagerClass::CollideableObjClass::Set_Collision_Object(RenderObjClass * mesh)
{
REF_PTR_SET(CollisionMesh,mesh);
StartTransform = EndTransform = CollisionMesh->Get_Transform();
}
/***********************************************************************************************
* AnimCollisionManagerClass::CollideableObjClass::Intersect_Scene -- intersection test *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
void
AnimCollisionManagerClass::CollideableObjClass::Intersect_Scene
(
int colgroup,
NonRefPhysListClass * intersect_list
)
{
if (CollisionMesh == NULL) return;
PhysicsSceneClass * scene = PhysicsSceneClass::Get_Instance();
#if VERBOSE_LOGGING
Vector3 pos = CollisionMesh->Get_Transform().Get_Translation();
WWDEBUG_SAY(("Checking obj: %s position: %10.2f, %10.2f, %10.2f\r\n",CollisionMesh->Get_Name(),pos.X,pos.Y,pos.Z));
#endif
switch (CollisionMesh->Class_ID()) {
case RenderObjClass::CLASSID_MESH:
{
MeshClass * mesh = (MeshClass *)CollisionMesh;
PhysMeshIntersectionTestClass test(mesh,colgroup,COLLISION_TYPE_PHYSICAL,intersect_list);
test.CheckStaticObjs = false;
scene->Intersection_Test(test);
VERBOSE_LOG(("Mesh intersection: %d\r\n",!intersect_list->Is_Empty()));
}
break;
case RenderObjClass::CLASSID_OBBOX:
{
OBBoxRenderObjClass * boxrobj = (OBBoxRenderObjClass *)CollisionMesh;
PhysOBBoxIntersectionTestClass test(boxrobj->Get_Box(),colgroup,COLLISION_TYPE_PHYSICAL,intersect_list);
test.CheckStaticObjs = false;
scene->Intersection_Test(test);
VERBOSE_LOG(("OBBox intersection: %d\r\n",!intersect_list->Is_Empty()));
}
break;
case RenderObjClass::CLASSID_AABOX:
{
AABoxRenderObjClass * boxrobj = (AABoxRenderObjClass *)CollisionMesh;
PhysAABoxIntersectionTestClass test(boxrobj->Get_Box(),colgroup,COLLISION_TYPE_PHYSICAL,intersect_list);
test.CheckStaticObjs = false;
scene->Intersection_Test(test);
VERBOSE_LOG(("AABox intersection: %d\r\n",!intersect_list->Is_Empty()));
}
break;
};
}
/***********************************************************************************************
* AnimCollisionManagerClass::CollideableObjClass::Clear_Collision_Bits -- disables collision *
* *
* Disables all collision on the render object that this CollideableObjClass is managing *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
int AnimCollisionManagerClass::CollideableObjClass::Clear_Collision_Bits(void)
{
int oldbits = CollisionMesh->Get_Collision_Type();
CollisionMesh->Set_Collision_Type(0);
return oldbits;
}
/***********************************************************************************************
* AnimCollisionManagerClass::CollideableObjClass::Restore_Collision_Bits -- set collision bit *
* *
* restores the collision bits on the render object that this CollideableObjClass is managing *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
void AnimCollisionManagerClass::CollideableObjClass::Restore_Collision_Bits(int oldbits)
{
CollisionMesh->Set_Collision_Type(oldbits);
}
/********************************************************************************************
**
** AnimCollisionManagerClass Implementation
**
********************************************************************************************/
/***********************************************************************************************
* AnimCollisionManagerClass::AnimCollisionManagerClass -- Constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*=============================================================================================*/
AnimCollisionManagerClass::AnimCollisionManagerClass(PhysClass & parent) :
Parent(parent),
AnimationMode(ANIMATE_LOOP),
TargetFrame(0.0f),
LoopStart(0.0f),
LoopEnd(0.0f),
CurAnimation(NULL),
CurFrame(0.0f),
PrevAnimation(NULL),
PrevFrame(0.0f),
CollisionMode(COLLIDE_NONE),
PushList(NULL)
{
}
/***********************************************************************************************
* AnimCollisionManagerClass::~AnimCollisionManagerClass -- Destructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
AnimCollisionManagerClass::~AnimCollisionManagerClass(void)
{
WWASSERT(PushList == NULL);
REF_PTR_RELEASE(CurAnimation);
REF_PTR_RELEASE(PrevAnimation);
}
/***********************************************************************************************
* AnimCollisionManagerClass::Set_Animation_Mode -- set the current anim mode *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
void AnimCollisionManagerClass::Set_Animation_Mode(AnimModeType mode)
{
AnimationMode = mode;
}
/***********************************************************************************************
* AnimCollisionManagerClass::Get_Animation_Mode -- Get the current anim mode *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
AnimCollisionManagerClass::AnimModeType
AnimCollisionManagerClass::Get_Animation_Mode(void)
{
return AnimationMode;
}
/***********************************************************************************************
* AnimCollisionManagerClass::Set_Animation -- Set the current animation *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
void AnimCollisionManagerClass::Set_Animation(const char * anim_name)
{
Internal_Set_Animation(anim_name);
/*
** Automatically loop this animation by default
*/
LoopStart = 0;
if (CurAnimation != NULL) {
LoopEnd = TargetFrame = CurAnimation->Get_Num_Frames() - 1;
} else {
LoopEnd = TargetFrame = 0;
}
}
/***********************************************************************************************
* AnimCollisionManagerClass::Internal_Set_Animation -- Set the current animation pointer *
* *
* This function doesn't change the animation mode, target frame, or current frame *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/13/2000 gth : Created. *
*=============================================================================================*/
void AnimCollisionManagerClass::Internal_Set_Animation(const char * anim_name)
{
HAnimClass * anim = WW3DAssetManager::Get_Instance()->Get_HAnim(anim_name);
if ( anim == NULL && anim_name != NULL ) {
WWDEBUG_SAY(( "FAILED TO FIND ANIM IN AnimCollisionManagerClass::Internal_Set_Animation(\"%s\")\n", anim_name ));
}
REF_PTR_SET(CurAnimation,anim);
REF_PTR_RELEASE(anim);
if (Parent.Peek_Model() != NULL) {
Parent.Peek_Model()->Set_Animation(CurAnimation,CurFrame);
}
}
/***********************************************************************************************
* AnimCollisionManagerClass::Peek_Animation -- Get the current animation *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
HAnimClass * AnimCollisionManagerClass::Peek_Animation(void)
{
return CurAnimation;
}
/***********************************************************************************************
* AnimCollisionManagerClass::Set_Target_Frame -- Set the target animation frame *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
void AnimCollisionManagerClass::Set_Target_Frame(float frame)
{
TargetFrame = frame;
Parent.Enable_Is_State_Dirty(true);
}
/***********************************************************************************************
* AnimCollisionManagerClass::Set_Target_Frame_End -- Set the target frame to the end of the c *
* *
* Sets the target frame to the end of the current anim *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/13/2000 gth : Created. *
*=============================================================================================*/
void AnimCollisionManagerClass::Set_Target_Frame_End(void)
{
if (CurAnimation != NULL) {
Set_Target_Frame(CurAnimation->Get_Num_Frames() - 1);
}
}
/***********************************************************************************************
* AnimCollisionManagerClass::Get_Target_Frame -- Get the target animation frame *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
float AnimCollisionManagerClass::Get_Target_Frame(void)
{
return TargetFrame;
}
/***********************************************************************************************
* AnimCollisionManagerClass::Is_At_Target -- returns true if in TARGET mode and done animatin *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/14/2000 gth : Created. *
*=============================================================================================*/
bool AnimCollisionManagerClass::Is_At_Target(void)
{
return ((TargetFrame == CurFrame) && (AnimationMode == ANIMATE_TARGET));
}
/***********************************************************************************************
* AnimCollisionManagerClass::Set_Loop_Start -- Sets frame0 for an anim loop *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* This setting is only used if you are in AnimationMode==ANIMATE_LOOP *
* *
* HISTORY: *
* 8/29/2000 gth : Created. *
*=============================================================================================*/
void AnimCollisionManagerClass::Set_Loop_Start(float frame0)
{
LoopStart = frame0;
Parent.Enable_Is_State_Dirty(true);
}
/***********************************************************************************************
* AnimCollisionManagerClass::Set_Loop_End -- Sets frame1 for an anim loop *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* This setting is only used if you are in AnimationMode==ANIMATE_LOOP *
* *
* HISTORY: *
* 8/29/2000 gth : Created. *
*=============================================================================================*/
void AnimCollisionManagerClass::Set_Loop_End(float frame1)
{
LoopEnd = frame1;
Parent.Enable_Is_State_Dirty(true);
}
/***********************************************************************************************
* AnimCollisionManagerClass::Get_Loop_Start -- returns frame0 for an anim loop *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/29/2000 gth : Created. *
*=============================================================================================*/
float AnimCollisionManagerClass::Get_Loop_Start(void)
{
return LoopStart;
}
/***********************************************************************************************
* AnimCollisionManagerClass::Get_Loop_End -- returns frame1 for an anim loop *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/29/2000 gth : Created. *
*=============================================================================================*/
float AnimCollisionManagerClass::Get_Loop_End(void)
{
return LoopEnd;
}
/***********************************************************************************************
* AnimCollisionManagerClass::Set_Current_Frame -- Set the current frame *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
void AnimCollisionManagerClass::Set_Current_Frame(float frame)
{
CurFrame = frame;
Parent.Enable_Is_State_Dirty(true);
#if VERBOSE_LOGGING
WWASSERT(!Is_Intersecting());
#endif
}
/***********************************************************************************************
* AnimCollisionManagerClass::Get_Current_Frame -- Get the current frame *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
float AnimCollisionManagerClass::Get_Current_Frame(void)
{
return CurFrame;
}
/***********************************************************************************************
* AnimCollisionManagerClass::Set_Collision_Mode -- Set the current collision mode *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
void AnimCollisionManagerClass::Set_Collision_Mode(CollisionModeType mode)
{
CollisionMode = mode;
}
/***********************************************************************************************
* AnimCollisionManagerClass::Get_Collision_Mode -- Get the current collision mode *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
AnimCollisionManagerClass::CollisionModeType AnimCollisionManagerClass::Get_Collision_Mode(void)
{
return CollisionMode;
}
/***********************************************************************************************
* AnimCollisionManagerClass::Init -- Init this collision manager *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
void AnimCollisionManagerClass::Init(const AnimCollisionManagerDefClass & def)
{
/*
** Set the collision and animation mode
*/
CollisionMode = (CollisionModeType)def.CollisionMode;
AnimationMode = (AnimModeType)def.AnimationMode;
/*
** Plug in the animation
*/
if (Parent.Peek_Model()) {
Set_Current_Frame(0.0f);
/*
** Get the name of the animation from the definition
*/
StringClass anim_name = def.AnimationName;
/*
** If the user didn't specify an animation name, then
** build an animation name from the model name.
*/
if (anim_name.Is_Empty ()) {
StringClass model_name(Parent.Peek_Model()->Get_Name(),true);
anim_name = model_name;
anim_name+=".";
anim_name+=model_name;
}
Set_Animation(anim_name);
}
}
/***********************************************************************************************
* AnimCollisionManagerClass::Update_Cached_Model_Parameters -- find all of the collideable me *
* *
* This function mainly finds all of the animating collideable meshes. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
void AnimCollisionManagerClass::Update_Cached_Model_Parameters(void)
{
/*
** Build the array of collideable animated meshes
*/
int count = Recursive_Count_Collision_Models(Parent.Peek_Model());
CollisionMeshes.Delete_All();
CollisionMeshes.Resize(count);
Recursive_Collect_Collision_Models(Parent.Peek_Model());
}
/***********************************************************************************************
* AnimCollisionManagerClass::Is_Collision_Model -- classify a sub-object as collideable or no *
* *
* A sub object needs a collision object if it is attached to a bone other than the root *
* and it is collideable. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
bool AnimCollisionManagerClass::Is_Collision_Model(RenderObjClass * subobj)
{
/*
** If this sub-object is collideable and it is not attached to the RootTransform (0)
** then it is considered an animated collideable mesh.
*/
return ( (subobj->Get_Collision_Type() & COLLISION_TYPE_PHYSICAL) &&
(subobj->Get_Container() != NULL) &&
(subobj->Get_Container()->Get_Sub_Object_Bone_Index(subobj) != 0) );
}
/***********************************************************************************************
* AnimCollisionManagerClass::Recursive_Count_Collision_Models -- count the collideable meshes *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
int AnimCollisionManagerClass::Recursive_Count_Collision_Models(RenderObjClass * model)
{
int count = 0;
if (model == NULL) {
return 0;
}
for (int i=0; i<model->Get_Num_Sub_Objects(); i++) {
RenderObjClass * subobj = model->Get_Sub_Object(i);
if (subobj->Get_Num_Sub_Objects() > 0) {
count += Recursive_Count_Collision_Models(subobj); // recurse
} else {
if (Is_Collision_Model(model)) {
count++;
}
}
subobj->Release_Ref();
}
return count;
}
/***********************************************************************************************
* AnimCollisionManagerClass::Recursive_Collect_Collision_Models -- collect collideable meshes *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
void AnimCollisionManagerClass::Recursive_Collect_Collision_Models(RenderObjClass * model)
{
if (model == NULL) {
return;
}
for (int i=0; i<model->Get_Num_Sub_Objects(); i++) {
RenderObjClass * subobj = model->Get_Sub_Object(i);
if (subobj->Get_Num_Sub_Objects() > 0) {
Recursive_Collect_Collision_Models(subobj); // recurse
} else {
/*
** If this sub-object is collideable and it is not attached to the RootTransform (0)
** then it is a candidate for being a collideable object
*/
if (Is_Collision_Model(subobj)) {
/*
** Add this collideable mesh to our array of collision meshes
*/
CollideableObjClass colmesh(subobj);
CollisionMeshes.Add(colmesh);
}
}
subobj->Release_Ref();
}
}
/***********************************************************************************************
* AnimCollisionManagerClass::Timestep -- Update the state of this object *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
bool AnimCollisionManagerClass::Timestep(float dt)
{
WWPROFILE("AnimColMgr::Timestep");
VERBOSE_LOG(("\r\nTimestep for %s - begin\r\n",Parent.Peek_Model()->Get_Name()));
bool object_animated = false;
/*
** In the case where we're not going to do anything bail out immediately!
*/
if ((CurFrame == PrevFrame) && (CurAnimation == PrevAnimation)) {
if (AnimationMode == ANIMATE_MANUAL) {
return false;
}
if ((AnimationMode == ANIMATE_TARGET) && (CurFrame == TargetFrame)) {
return false;
}
}
/*
** Store the collision transform prior to updating the animation
*/
if (CollisionMode != COLLIDE_NONE) {
for (int i=0; i<CollisionMeshes.Count(); i++) {
CollisionMeshes[i].Cache_Start_Transform();
}
}
#if VERBOSE_LOGGING
bool started_intersecting = false;
if (CollisionMode != COLLIDE_NONE) {
started_intersecting = Is_Intersecting();
WWDEBUG_SAY(("Checking started_intersecting: %d\r\n",started_intersecting));
}
#endif
/*
** move animation forward
*/
if (CurAnimation != NULL) {
switch (AnimationMode) {
case ANIMATE_LOOP:
CurFrame += CurAnimation->Get_Frame_Rate() * dt;
if (CurFrame >= LoopEnd) {
CurFrame -= (LoopEnd - LoopStart);
}
break;
case ANIMATE_TARGET:
if (CurFrame < TargetFrame) {
CurFrame += CurAnimation->Get_Frame_Rate() * dt;
// if we overshoot targetframe, snap to targetframe
if (CurFrame >= TargetFrame) {
CurFrame = TargetFrame;
}
} else if (CurFrame > TargetFrame) {
CurFrame -= CurAnimation->Get_Frame_Rate() * dt;
// if we overshoot targetframe, snap to targetframe
if (CurFrame <= TargetFrame) {
CurFrame = TargetFrame;
}
}
break;
case ANIMATE_MANUAL:
break;
default:
WWASSERT_PRINT(0,("Invalid Animation Mode!\n"));
}
VERBOSE_LOG(("Cur-Frame: %f Target-Frame: %f\r\n",CurFrame,TargetFrame));
Parent.Peek_Model()->Set_Animation(CurAnimation,CurFrame);
}
/*
** Check for collisions.
** - cache a copy of the ending transform for each collideable object
** - revert all collideable objects back to their start transform
** - for each object:
** - install its ending transform and check for collision.
** - if collided, compute the movement vector and push objects.
** - if unable to push objects out of the way, set the revert flag and exit
*/
if ((CurAnimation != PrevAnimation) || (CurFrame != PrevFrame)) {
/*
** Add our riders to our push list
*/
object_animated = true;
bool intersecting = false;
if (CollisionMode != COLLIDE_NONE) {
int ci;
/*
** Save the transform as the "ending transform"
** and roll back to the starting transform
*/
for (ci=0; ci<CollisionMeshes.Count(); ci++) {
CollisionMeshes[ci].Cache_End_Transform();
CollisionMeshes[ci].Install_Start_Transform();
}
/*
** Move this mesh forward to the End Transform
** and see if it collides with anything
*/
for (ci=0; (ci<CollisionMeshes.Count()) && (!intersecting); ci++) {
CollisionMeshes[ci].Install_End_Transform();
intersecting |= Check_Collision(CollisionMeshes[ci]);
}
/*
** If we ended up intersecting, then we have to revert
*/
if (intersecting) {
Revert_Animation_State();
object_animated = false;
}
#if VERBOSE_LOGGING
WWDEBUG_SAY(("checking is_now_intersecting\r\n"));
bool is_now_intersecting = Is_Intersecting();
if ( (started_intersecting == false) && (is_now_intersecting == true)) {
WWDEBUG_SAY(("Reverting did not fix intersections!\r\n"));
}
#endif
}
}
/*
** Release the push records
*/
PushRecordClass::Delete_List(PushList);
PushList = NULL;
/*
** Ratchet our animation state forward for the next frame
*/
REF_PTR_RELEASE(PrevAnimation);
REF_PTR_SET(PrevAnimation,CurAnimation);
PrevFrame = CurFrame;
VERBOSE_LOG(("Timestep - done\r\n"));
return object_animated;
}
/***********************************************************************************************
* AnimCollisionManagerClass::Revert_Animation_State -- revert to previous anim frame *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
void AnimCollisionManagerClass::Revert_Animation_State(void)
{
REF_PTR_SET(CurAnimation,PrevAnimation);
CurFrame = PrevFrame;
Parent.Peek_Model()->Set_Animation(CurAnimation,CurFrame);
VERBOSE_LOG(("Reverted to frame: %f\r\n",CurFrame));
/*
** Force the transforms of the collideable meshes to update
*/
for (int ci=0; ci<CollisionMeshes.Count(); ci++) {
CollisionMeshes[ci].Cache_End_Transform();
CollisionMeshes[ci].Install_End_Transform();
}
/*
** Put everyone that we pushed back into their original state
*/
PushRecordClass::Revert_List(PushList);
}
/***********************************************************************************************
* AnimCollisionManagerClass::Check_Collision -- Check the given collision object *
* *
* This returns true if the object collided with something and could not move it out of the *
* way. (You must revert or kill the object in this case) *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
bool AnimCollisionManagerClass::Check_Collision(CollideableObjClass & collisionobj)
{
/*
** Algorithm:
** - Determine how each attached object should move due to our motion.
** - Ask all of our attached objects to apply the same delta.
** - Ask physics scene to do an intersection test between our mesh and all nearby dynamic objects
** - If (CollisionMode == STOP) REVERT animation and return
** - Else For each object intersecting the collision mesh:
** - If it is one of our attached objects, SQUISH and REVERT
** - Else
** - Determine how the collided object should move and try to move it
** - If still colliding, SQUISH and REVERT
** - Else OK
** - If no object caused a revert, ACCEPT new animation state and return.
** - Else REVERT and return
*/
/*
** Compute the change in the transform of the collision mesh. Note that this
** is currently only "supposed" to work with pure translational motion...
*/
Matrix3D delta_transform;
Matrix3D pinv;
collisionobj.Get_Start_Transform().Get_Orthogonal_Inverse(pinv);
Matrix3D::Multiply(collisionobj.Get_End_Transform(),pinv,&delta_transform);
/*
** Make all attached objects move with us (people stick on elevators, etc)
** Adding a push record for each rider in case it gets moved...
*/
int collision_bits = collisionobj.Clear_Collision_Bits();
NonRefPhysListIterator rider_iterator(RiderManager.Get_Rider_List());
VERBOSE_LOG((" rider_list: "));
while (!rider_iterator.Is_Done()) {
VERBOSE_LOG(("%s, ",rider_iterator.Peek_Obj()->Peek_Model()->Get_Name()));
PushRecordClass::Add_To_List(&PushList,rider_iterator.Peek_Obj());
rider_iterator.Next();
}
VERBOSE_LOG(("\r\n"));
RiderManager.Move_Riders(delta_transform,collisionobj.Peek_Collision_Object());
collisionobj.Restore_Collision_Bits(collision_bits);
/*
** Perform intersection detection
*/
VERBOSE_LOG(("Check Collision: %s\r\n",collisionobj.Peek_Collision_Object()->Get_Name()));
NonRefPhysListClass intersection_list;
collisionobj.Intersect_Scene(Parent.Get_Collision_Group(),&intersection_list);
/*
** If nothing is intersecting us, return.
*/
if (intersection_list.Is_Empty()) {
return false;
}
/*
** CollisionMode == COLLIDE_STOP
** If something is intersecting us and we just revert when that happens, revert and return
*/
if (CollisionMode == COLLIDE_STOP) {
return true;
}
/*
** CollisionMode == COLLIDE_KILL
** If something is intersecting us and we are in KILL mode, then we kill them!
*/
if (CollisionMode == COLLIDE_KILL) {
NonRefPhysListIterator it(&intersection_list);
for (it.First(); !it.Is_Done(); it.Next()) {
it.Peek_Obj()->Expire();
}
}
/*
** CollisionMode == COLLIDE_PUSH
** Handle collisions by pushing the objects out of our way
*/
bool revert = false;
NonRefPhysListIterator it(&intersection_list);
for (it.First();!it.Is_Done();it.Next()) {
PhysClass * obj = it.Peek_Obj();
VERBOSE_LOG(("intersecting object: %s\r\n",obj->Peek_Model()->Get_Name()));
if (RiderManager.Contains(obj) && (obj->Peek_Carrier_Sub_Object() == collisionobj.Peek_Collision_Object())) {
/*
** If an object that we hit is attached to us already, squish and revert
*/
//obj->Notify_Squished(this);
revert = true;
#pragma message ("(gth) commenting out rider squishing code for critical review!")
VERBOSE_LOG(("Squishing a rider!\r\n"));
} else {
/*
** Otherwise, try to push the object out of our way.
*/
int collision_bits = collisionobj.Clear_Collision_Bits();
if (Push_Collided_Object(obj,delta_transform) == false) {
VERBOSE_LOG(("SAPO %s Failed to push a rider\r\n",Parent.Peek_Model()->Get_Name()));
revert = true;
}
collisionobj.Restore_Collision_Bits(collision_bits);
}
}
return revert;
}
/***********************************************************************************************
* AnimCollisionManagerClass::Is_Intersecting -- intersection test *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
bool AnimCollisionManagerClass::Is_Intersecting(void)
{
NonRefPhysListClass intersection_list;
for (int ci=0; ci<CollisionMeshes.Count() && intersection_list.Is_Empty(); ci++) {
CollisionMeshes[ci].Intersect_Scene(Parent.Get_Collision_Group(),&intersection_list);
}
return !intersection_list.Is_Empty();
}
/***********************************************************************************************
* AnimCollisionManagerClass::Push_Collided_Object -- push an object we collided with *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
bool AnimCollisionManagerClass::Push_Collided_Object(PhysClass * obj,const Matrix3D & delta_transform)
{
bool result = false;
MoveablePhysClass * move_obj = obj->As_MoveablePhysClass();
if (move_obj != NULL) {
VERBOSE_LOG(("Pushing object: %s, vector: (%f, %f, %f)\r\n",move_obj->Peek_Model()->Get_Name(),
delta_transform.Get_Translation().X,
delta_transform.Get_Translation().Y,
delta_transform.Get_Translation().Z ));
PushRecordClass::Add_To_List(&PushList,obj);
result = move_obj->Push(delta_transform.Get_Translation());
}
return result;
}
/***********************************************************************************************
* AnimCollisionManagerClass::Save -- save! *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
bool AnimCollisionManagerClass::Save(ChunkSaveClass &csave)
{
csave.Begin_Chunk(ANIMCOLLISIONMANAGER_CHUNK_VARIABLES);
WRITE_MICRO_CHUNK(csave,ANIMCOLLISIONMANAGER_VARIABLE_COLLISIONMODE,CollisionMode);
WRITE_MICRO_CHUNK(csave,ANIMCOLLISIONMANAGER_VARIABLE_ANIMATIONMODE,AnimationMode);
WRITE_MICRO_CHUNK(csave,ANIMCOLLISIONMANAGER_VARIABLE_TARGETFRAME,TargetFrame);
WRITE_MICRO_CHUNK(csave,ANIMCOLLISIONMANAGER_VARIABLE_CURFRAME,CurFrame);
WRITE_MICRO_CHUNK(csave,ANIMCOLLISIONMANAGER_VARIABLE_PREVFRAME,PrevFrame);
WRITE_MICRO_CHUNK(csave,ANIMCOLLISIONMANAGER_VARIABLE_LOOPSTART,LoopStart);
WRITE_MICRO_CHUNK(csave,ANIMCOLLISIONMANAGER_VARIABLE_LOOPEND,LoopEnd);
if (CurAnimation != NULL) {
WRITE_MICRO_CHUNK_STRING(csave,ANIMCOLLISIONMANAGER_VARIABLE_ANIMATIONNAME,CurAnimation->Get_Name());
}
if (PrevAnimation != NULL) {
WRITE_MICRO_CHUNK_STRING(csave,ANIMCOLLISIONMANAGER_VARIABLE_PREVANIMATIONNAME,PrevAnimation->Get_Name());
}
csave.End_Chunk();
return true;
}
/***********************************************************************************************
* AnimCollisionManagerClass::Load -- Load! *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/9/2000 gth : Created. *
*=============================================================================================*/
bool AnimCollisionManagerClass::Load(ChunkLoadClass &cload)
{
StringClass anim_name;
StringClass prev_anim_name;
/*
** Read in the chunks from the file
*/
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID())
{
case ANIMCOLLISIONMANAGER_CHUNK_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK(cload,ANIMCOLLISIONMANAGER_VARIABLE_COLLISIONMODE,CollisionMode);
READ_MICRO_CHUNK(cload,ANIMCOLLISIONMANAGER_VARIABLE_ANIMATIONMODE,AnimationMode);
READ_MICRO_CHUNK(cload,ANIMCOLLISIONMANAGER_VARIABLE_TARGETFRAME,TargetFrame);
READ_MICRO_CHUNK(cload,ANIMCOLLISIONMANAGER_VARIABLE_CURFRAME,CurFrame);
READ_MICRO_CHUNK(cload,ANIMCOLLISIONMANAGER_VARIABLE_PREVFRAME,PrevFrame);
READ_MICRO_CHUNK_WWSTRING(cload,ANIMCOLLISIONMANAGER_VARIABLE_ANIMATIONNAME,anim_name);
READ_MICRO_CHUNK_WWSTRING(cload,ANIMCOLLISIONMANAGER_VARIABLE_PREVANIMATIONNAME,prev_anim_name);
READ_MICRO_CHUNK(cload,ANIMCOLLISIONMANAGER_VARIABLE_LOOPSTART,LoopStart);
READ_MICRO_CHUNK(cload,ANIMCOLLISIONMANAGER_VARIABLE_LOOPEND,LoopEnd);
}
cload.Close_Micro_Chunk();
}
break;
default:
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
break;
};
cload.Close_Chunk();
}
if (!anim_name.Is_Empty()) {
Internal_Set_Animation(anim_name);
}
if (!prev_anim_name.Is_Empty()) {
HAnimClass * anim = WW3DAssetManager::Get_Instance()->Get_HAnim(prev_anim_name);
if ( anim == NULL ) {
WWDEBUG_SAY(( "FAILED TO FIND PREV ANIM IN AnimCollisionManagerClass::Internal_Set_Animation(\"%s\")\n", prev_anim_name ));
}
REF_PTR_SET(PrevAnimation,anim);
}
return true;
}
/********************************************************************************************
**
** AnimCollisionManagerDefClass Implementation
** Note, AnimCollisionManagerDef is not a full-fleged definition class. It is meant to be
** embedded inside another real definition. (e.g. StaticAnimPhys)
**
********************************************************************************************/
enum
{
ANIMCOLLISIONMANAGERDEF_CHUNK_VARIABLES = 525000306,
ANIMCOLLISIONMANAGERDEF_VARIABLE_COLLISIONMODE = 0x00,
ANIMCOLLISIONMANAGERDEF_VARIABLE_ANIMATIONMODE,
ANIMCOLLISIONMANAGERDEF_VARIABLE_ANIMATIONNAME,
};
AnimCollisionManagerDefClass::AnimCollisionManagerDefClass(void) :
CollisionMode(AnimCollisionManagerClass::COLLIDE_PUSH),
AnimationMode(AnimCollisionManagerClass::ANIMATE_LOOP)
{
}
AnimCollisionManagerDefClass::~AnimCollisionManagerDefClass(void)
{
}
void AnimCollisionManagerDefClass::Validate_Parameters(void)
{
if (CollisionMode < 0) CollisionMode = 0;
if (CollisionMode > AnimCollisionManagerClass::ANIMATE_MANUAL) CollisionMode = AnimCollisionManagerClass::ANIMATE_MANUAL;
}
bool AnimCollisionManagerDefClass::Save(ChunkSaveClass &csave)
{
Validate_Parameters();
csave.Begin_Chunk(ANIMCOLLISIONMANAGERDEF_CHUNK_VARIABLES);
WRITE_MICRO_CHUNK(csave,ANIMCOLLISIONMANAGERDEF_VARIABLE_COLLISIONMODE,CollisionMode);
WRITE_MICRO_CHUNK(csave,ANIMCOLLISIONMANAGERDEF_VARIABLE_ANIMATIONMODE,AnimationMode);
WRITE_MICRO_CHUNK_WWSTRING(csave,ANIMCOLLISIONMANAGERDEF_VARIABLE_ANIMATIONNAME,AnimationName);
csave.End_Chunk();
return true;
}
bool AnimCollisionManagerDefClass::Load(ChunkLoadClass &cload)
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case ANIMCOLLISIONMANAGERDEF_CHUNK_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK(cload,ANIMCOLLISIONMANAGERDEF_VARIABLE_COLLISIONMODE,CollisionMode);
READ_MICRO_CHUNK(cload,ANIMCOLLISIONMANAGERDEF_VARIABLE_ANIMATIONMODE,AnimationMode);
READ_MICRO_CHUNK_WWSTRING(cload,ANIMCOLLISIONMANAGERDEF_VARIABLE_ANIMATIONNAME,AnimationName);
}
cload.Close_Micro_Chunk();
}
break;
default:
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
return true;
}