/* ** 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/staticanimphys.cpp $* * * * Original Author:: Greg Hjelstrom * * * * $Author:: Greg_h $* * * * $Modtime:: 8/18/01 6:17p $* * * * $Revision:: 43 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "staticanimphys.h" #include "colmathaabox.h" #include "physinttest.h" #include "bitstream.h" #include "wwphysids.h" #include "persistfactory.h" #include "simpledefinitionfactory.h" #include "movephys.h" #include "hanim.h" #include "mesh.h" #include "boxrobj.h" #include "wwhack.h" #include "wwprofile.h" #include "assetmgr.h" #include "vertmaterial.h" #include "dx8wrapper.h" DECLARE_FORCE_LINK(staticanimphys); /******************************************************************************************** ** ** StaticAnimPhysClass Implementation ** ********************************************************************************************/ /* ** Declare a PersistFactory for StaticAnimPhysClass. This enables the class to save and load. */ SimplePersistFactoryClass _StaticAnimPhysFactory; /* ** Chunk ID's used by StaticAnimPhysClass */ enum { STATICANIMPHYS_CHUNK_STATICPHYS = 0x01170010, STATICANIMPHYS_CHUNK_VARIABLES, STATICANIMPHYS_CHUNK_ANIMMANAGER, OBSOLETE_STATICANIMPHYS_VARIABLE_DEFID = 0x00, STATICANIMPHYS_VARIABLE_COLLISIONMODE, }; StaticAnimPhysClass::StaticAnimPhysClass(void) : AnimManager(*this), ShadowProjector(NULL) { Set_Collision_Group(0xF); //Hardcoding this??? } StaticAnimPhysClass::~StaticAnimPhysClass(void) { if (ShadowProjector != NULL) { PhysicsSceneClass::Get_Instance()->Remove_Static_Texture_Projector(ShadowProjector); ShadowProjector->Release_Ref(); ShadowProjector = NULL; } } void StaticAnimPhysClass::Init(const StaticAnimPhysDefClass & def) { StaticPhysClass::Init(def); ProjectorManager.Init(def.ProjectorManagerDef,Model); AnimManager.Init(def.AnimManagerDef); } void StaticAnimPhysClass::Set_Model(RenderObjClass * model) { // call parent class StaticPhysClass::Set_Model(model); Update_Cached_Model_Parameters(); } void StaticAnimPhysClass::Vis_Render(SpecialRenderInfoClass & rinfo) { if (Model != NULL) { // static anim objects need to render their bounding box so temporarily make it visible: int was_hidden = 0; RenderObjClass * bbox = Model->Get_Sub_Object_By_Name("BoundingBox"); if (bbox != NULL) { was_hidden = bbox->Is_Hidden(); bbox->Set_Hidden(false); } StaticPhysClass::Vis_Render(rinfo); if (bbox != NULL) { bbox->Set_Hidden(was_hidden); bbox->Release_Ref(); } } } void StaticAnimPhysClass::Update_Cached_Model_Parameters(void) { // check if there is a collideable mesh in the model and cache a pointer to it. AnimManager.Update_Cached_Model_Parameters(); } void StaticAnimPhysClass::Update_Sun_Status(void) { float MAX_XYSIZE2 = 15.0f * 15.0f; // weird... well, I cant have all static objects check for the sun but // "small" "animated" ones should (like doors and elevators). So, leapfrog // past our parent class back to the default behavior. Ug... AABoxClass box; Model->Get_Obj_Space_Bounding_Box(box); float xysize2 = box.Extent.X * box.Extent.X + box.Extent.Y * box.Extent.Y; if (xysize2 < MAX_XYSIZE2) { PhysClass::Update_Sun_Status(); } else { Set_Flag(IS_IN_THE_SUN,true); } } void StaticAnimPhysClass::Set_Shadow(TexProjectClass * shadow) { if (ShadowProjector != NULL) { PhysicsSceneClass::Get_Instance()->Remove_Static_Texture_Projector(ShadowProjector); ShadowProjector->Release_Ref(); ShadowProjector = NULL; } ShadowProjector = shadow; if (ShadowProjector != NULL) { ShadowProjector->Add_Ref(); ShadowProjector->Enable_Affect_Static_Objects(false); PhysicsSceneClass::Get_Instance()->Add_Static_Texture_Projector(ShadowProjector); } } void StaticAnimPhysClass::Debug_Display_Shadow(const Vector2 & v0,const Vector2 & v1) { if (ShadowProjector != NULL) { TextureClass * tex = ShadowProjector->Peek_Texture(); if (tex != NULL) { ShaderClass shader = ShaderClass::_PresetOpaqueShader; VertexMaterialClass * vmtl = VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_NODIFFUSE); DX8Wrapper::Set_Shader(shader); DX8Wrapper::Set_Material(vmtl); Matrix4 view,proj; Matrix4 identity(true); DX8Wrapper::Get_Transform(D3DTS_VIEW,view); DX8Wrapper::Get_Transform(D3DTS_PROJECTION,proj); DX8Wrapper::Set_Transform(D3DTS_WORLD,identity); DX8Wrapper::Set_Transform(D3DTS_VIEW,identity); DX8Wrapper::Set_Transform(D3DTS_PROJECTION,identity); DX8Wrapper::Set_Texture(0,tex); DynamicVBAccessClass vbaccess(BUFFER_TYPE_DYNAMIC_DX8,dynamic_fvf_type,4); { DynamicVBAccessClass::WriteLockClass lock(&vbaccess); VertexFormatXYZNDUV2 * verts = lock.Get_Formatted_Vertex_Array(); verts[0].x = -1.0f; verts[0].y = 0.8f; verts[0].z = 0.0; verts[0].u1 = 0.0f; verts[0].v1 = 0.0f; verts[0].diffuse = 0xFFFFFFFF; verts[1].x = -1.0f; verts[1].y = 0.3f; verts[1].z = 0.0; verts[1].u1 = 0.0f; verts[1].v1 = 1.0f; verts[1].diffuse = 0xFFFFFFFF; verts[2].x = -0.5f; verts[2].y = 0.3f; verts[2].z = 0.0; verts[2].u1 = 1.0f; verts[2].v1 = 1.0f; verts[2].diffuse = 0xFFFFFFFF; verts[3].x = -0.5f; verts[3].y = 0.8f; verts[3].z = 0.0; verts[3].u1 = 1.0f; verts[3].v1 = 0.0f; verts[3].diffuse = 0xFFFFFFFF; } DynamicIBAccessClass ibaccess(BUFFER_TYPE_DYNAMIC_DX8,2*3); { DynamicIBAccessClass::WriteLockClass lock(&ibaccess); unsigned short * indices = lock.Get_Index_Array(); indices[0] = 0; indices[1] = 1; indices[2] = 2; indices[3] = 0; indices[4] = 2; indices[5] = 3; } DX8Wrapper::Set_Vertex_Buffer(vbaccess); DX8Wrapper::Set_Index_Buffer(ibaccess,0); DX8Wrapper::Draw_Triangles(0,2,0,4); DX8Wrapper::Set_Transform(D3DTS_VIEW,view); DX8Wrapper::Set_Transform(D3DTS_PROJECTION,proj); REF_PTR_RELEASE(vmtl); } } } bool StaticAnimPhysClass::Needs_Timestep(void) { return true; } void StaticAnimPhysClass::Timestep(float dt) { WWPROFILE("StaticAnim::Timestep"); /* ** Let the animation manager handle progressing the animation, checking ** for collisions, and managing any "riders" */ AnimManager.Timestep(dt); /* ** update projectors */ ProjectorManager.Update_From_Model(Model); } bool StaticAnimPhysClass::Internal_Link_Rider(PhysClass * rider) { AnimManager.Link_Rider(rider); return true; } bool StaticAnimPhysClass::Internal_Unlink_Rider(PhysClass * rider) { AnimManager.Unlink_Rider(rider); return true; } void StaticAnimPhysClass::Save_State(ChunkSaveClass & csave) { csave.Begin_Chunk(STATICANIMPHYS_CHUNK_ANIMMANAGER); AnimManager.Save(csave); csave.End_Chunk(); } void StaticAnimPhysClass::Load_State(ChunkLoadClass & cload) { while (cload.Open_Chunk()) { switch(cload.Cur_Chunk_ID()) { case STATICANIMPHYS_CHUNK_ANIMMANAGER: AnimManager.Load(cload); break; default: WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__)); break; } cload.Close_Chunk(); } } const PersistFactoryClass & StaticAnimPhysClass::Get_Factory(void) const { return _StaticAnimPhysFactory; } bool StaticAnimPhysClass::Save(ChunkSaveClass &csave) { csave.Begin_Chunk(STATICANIMPHYS_CHUNK_STATICPHYS); StaticPhysClass::Save(csave); csave.End_Chunk(); csave.Begin_Chunk(STATICANIMPHYS_CHUNK_ANIMMANAGER); AnimManager.Save(csave); csave.End_Chunk(); return true; } bool StaticAnimPhysClass::Load(ChunkLoadClass &cload) { int legacy_collision_mode = -1; /* ** Read in the chunks from the file */ while (cload.Open_Chunk()) { switch(cload.Cur_Chunk_ID()) { case STATICANIMPHYS_CHUNK_STATICPHYS: StaticPhysClass::Load(cload); break; case STATICANIMPHYS_CHUNK_VARIABLES: while (cload.Open_Micro_Chunk()) { switch(cload.Cur_Micro_Chunk_ID()) { READ_MICRO_CHUNK(cload,STATICANIMPHYS_VARIABLE_COLLISIONMODE,legacy_collision_mode); } cload.Close_Micro_Chunk(); } break; case STATICANIMPHYS_CHUNK_ANIMMANAGER: AnimManager.Load(cload); 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 (legacy_collision_mode != -1) { AnimManager.Set_Collision_Mode((AnimCollisionManagerClass::CollisionModeType)legacy_collision_mode); } SaveLoadSystemClass::Register_Post_Load_Callback(this); return true; } void StaticAnimPhysClass::On_Post_Load(void) { StaticPhysClass::On_Post_Load(); /* ** Always re-initialize any variables which depend on the model */ Update_Cached_Model_Parameters(); /* ** Re-Initialize our projector. */ StaticAnimPhysDefClass * definition = Get_StaticAnimPhysDef(); if (definition != NULL) { ProjectorManager.Init(definition->ProjectorManagerDef,Model); } } /******************************************************************************************** ** ** StaticAnimPhysDefClass Implementation ** ** Virtual constructor mechanism for StaticAnimPhysClass's and also the hook into the editor ** ********************************************************************************************/ /* ** Persist factory for StaticAnimPhysDefClass's */ SimplePersistFactoryClass _StaticAnimPhysDefFactory; /* ** Definition factory for StaticAnimPhysDefClass. This makes it show up in the editor */ DECLARE_DEFINITION_FACTORY(StaticAnimPhysDefClass, CLASSID_STATICANIMPHYSDEF, "StaticAnimPhys") _StaticAnimPhysDefDefFactory; /* ** Chunk ID's used by StaticAnimPhysDefClass */ enum { STATICANIMPHYSDEF_CHUNK_STATICPHYSDEF = 0x055110100, // (parent class) STATICANIMPHYSDEF_CHUNK_PROJECTORMANAGERDEF, STATICANIMPHYSDEF_CHUNK_VARIABLES, STATICANIMPHYSDEF_CHUNK_ANIMMANAGERDEF, STATICANIMPHYSDEF_VARIABLE_COLLISIONMODE = 0x00, STATICANIMPHYSDEF_VARIABLE_SHADOWDYNAMICOBJS, STATICANIMPHYSDEF_VARIABLE_SHADOWISADDITIVE, STATICANIMPHYSDEF_VARIABLE_SHADOWIGNORESZROTATION, STATICANIMPHYSDEF_VARIABLE_SHADOWFARZ, STATICANIMPHYSDEF_VARIABLE_SHADOWINTENSITY, STATICANIMPHYSDEF_VARIABLE_SHADOWNEARZ, STATICANIMPHYSDEF_VARIABLE_ANIMATIONNAME, STATICANIMPHYSDEF_VARIABLE_COLLIDEINPATHFIND, STATICANIMPHYSDEF_VARIABLE_ISCOSMETIC, }; StaticAnimPhysDefClass::StaticAnimPhysDefClass(void) : ShadowDynamicObjs(false), ShadowIsAdditive(false), ShadowIgnoresZRotation(true), ShadowNearZ(0.5f), ShadowFarZ(5.0f), ShadowIntensity(0.5f), DoesCollideInPathfind(false), IsCosmetic(false) { // Add the misc flags to the editable interface EDITABLE_PARAM(StaticAnimPhysDefClass, ParameterClass::TYPE_BOOL, IsCosmetic); EDITABLE_PARAM(StaticAnimPhysDefClass, ParameterClass::TYPE_BOOL, DoesCollideInPathfind); // Make the animation manager variables editable ANIMCOLLISIONMANAGERDEF_EDITABLE_PARAMS( StaticAnimPhysDefClass , AnimManagerDef ); // Make the projector manager variables editable PROJECTORMANAGERDEF_EDITABLE_PARAMS( StaticAnimPhysDefClass , ProjectorManagerDef ); // make the shadow parameters editable PARAM_SEPARATOR(StaticAnimPhysDefClass, "Shadow Settings"); EDITABLE_PARAM(StaticAnimPhysDefClass, ParameterClass::TYPE_BOOL, ShadowDynamicObjs); EDITABLE_PARAM(StaticAnimPhysDefClass, ParameterClass::TYPE_BOOL, ShadowIsAdditive); EDITABLE_PARAM(StaticAnimPhysDefClass, ParameterClass::TYPE_BOOL, ShadowIgnoresZRotation); FLOAT_EDITABLE_PARAM(StaticAnimPhysDefClass, ShadowNearZ, 1.0f, 100.0f); FLOAT_EDITABLE_PARAM(StaticAnimPhysDefClass, ShadowFarZ, 1.0f, 100.0f); FLOAT_EDITABLE_PARAM(StaticAnimPhysDefClass, ShadowIntensity, 0.0f, 1.0f); } uint32 StaticAnimPhysDefClass::Get_Class_ID (void) const { return CLASSID_STATICANIMPHYSDEF; } PersistClass * StaticAnimPhysDefClass::Create(void) const { StaticAnimPhysClass * obj = NEW_REF(StaticAnimPhysClass,()); obj->Init(*this); return obj; } const PersistFactoryClass & StaticAnimPhysDefClass::Get_Factory (void) const { return _StaticAnimPhysDefFactory; } bool StaticAnimPhysDefClass::Is_Type(const char * type_name) { if (stricmp(type_name,StaticAnimPhysDefClass::Get_Type_Name()) == 0) { return true; } else { return StaticPhysDefClass::Is_Type(type_name); } } bool StaticAnimPhysDefClass::Save(ChunkSaveClass &csave) { csave.Begin_Chunk(STATICANIMPHYSDEF_CHUNK_STATICPHYSDEF); StaticPhysDefClass::Save(csave); csave.End_Chunk(); csave.Begin_Chunk(STATICANIMPHYSDEF_CHUNK_PROJECTORMANAGERDEF); ProjectorManagerDef.Save(csave); csave.End_Chunk(); csave.Begin_Chunk(STATICANIMPHYSDEF_CHUNK_ANIMMANAGERDEF); AnimManagerDef.Save(csave); csave.End_Chunk(); csave.Begin_Chunk(STATICANIMPHYSDEF_CHUNK_VARIABLES); WRITE_MICRO_CHUNK(csave,STATICANIMPHYSDEF_VARIABLE_SHADOWDYNAMICOBJS,ShadowDynamicObjs); WRITE_MICRO_CHUNK(csave,STATICANIMPHYSDEF_VARIABLE_SHADOWISADDITIVE,ShadowIsAdditive); WRITE_MICRO_CHUNK(csave,STATICANIMPHYSDEF_VARIABLE_SHADOWIGNORESZROTATION,ShadowIgnoresZRotation); WRITE_MICRO_CHUNK(csave,STATICANIMPHYSDEF_VARIABLE_SHADOWFARZ,ShadowFarZ); WRITE_MICRO_CHUNK(csave,STATICANIMPHYSDEF_VARIABLE_SHADOWINTENSITY,ShadowIntensity); WRITE_MICRO_CHUNK(csave,STATICANIMPHYSDEF_VARIABLE_SHADOWNEARZ,ShadowNearZ); WRITE_MICRO_CHUNK(csave,STATICANIMPHYSDEF_VARIABLE_COLLIDEINPATHFIND,DoesCollideInPathfind); WRITE_MICRO_CHUNK(csave,STATICANIMPHYSDEF_VARIABLE_ISCOSMETIC,IsCosmetic); csave.End_Chunk(); return true; } bool StaticAnimPhysDefClass::Load(ChunkLoadClass &cload) { while (cload.Open_Chunk()) { switch(cload.Cur_Chunk_ID()) { case STATICANIMPHYSDEF_CHUNK_STATICPHYSDEF: StaticPhysDefClass::Load(cload); break; case STATICANIMPHYSDEF_CHUNK_PROJECTORMANAGERDEF: ProjectorManagerDef.Load(cload); break; case STATICANIMPHYSDEF_CHUNK_ANIMMANAGERDEF: AnimManagerDef.Load(cload); break; case STATICANIMPHYSDEF_CHUNK_VARIABLES: while (cload.Open_Micro_Chunk()) { switch(cload.Cur_Micro_Chunk_ID()) { // NOTE: these two variables/microchunks have been moved into the animation manager. READ_MICRO_CHUNK(cload,STATICANIMPHYSDEF_VARIABLE_COLLISIONMODE,AnimManagerDef.CollisionMode); READ_MICRO_CHUNK_WWSTRING(cload,STATICANIMPHYSDEF_VARIABLE_ANIMATIONNAME,AnimManagerDef.AnimationName); READ_MICRO_CHUNK(cload,STATICANIMPHYSDEF_VARIABLE_SHADOWDYNAMICOBJS,ShadowDynamicObjs); READ_MICRO_CHUNK(cload,STATICANIMPHYSDEF_VARIABLE_SHADOWISADDITIVE,ShadowIsAdditive); READ_MICRO_CHUNK(cload,STATICANIMPHYSDEF_VARIABLE_SHADOWIGNORESZROTATION,ShadowIgnoresZRotation); READ_MICRO_CHUNK(cload,STATICANIMPHYSDEF_VARIABLE_SHADOWFARZ,ShadowFarZ); READ_MICRO_CHUNK(cload,STATICANIMPHYSDEF_VARIABLE_SHADOWINTENSITY,ShadowIntensity); READ_MICRO_CHUNK(cload,STATICANIMPHYSDEF_VARIABLE_SHADOWNEARZ,ShadowNearZ); READ_MICRO_CHUNK(cload,STATICANIMPHYSDEF_VARIABLE_COLLIDEINPATHFIND,DoesCollideInPathfind); READ_MICRO_CHUNK(cload,STATICANIMPHYSDEF_VARIABLE_ISCOSMETIC,IsCosmetic); } 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; }