/* ** 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/physdecalsys.cpp $* * * * Original Author:: Greg Hjelstrom * * * * $Author:: Ian_l $* * * * $Modtime:: 7/17/01 9:52p $* * * * $Revision:: 20 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "physdecalsys.h" #include "colmathaabox.h" #include "pscene.h" #include "phys.h" #include "camera.h" #include "mesh.h" #include "decalmsh.h" #include "assetmgr.h" #include "wwdebug.h" #include "robjlist.h" #include "texture.h" #define DEBUG_DECALS 0 const float DECAL_BACKFACE_THRESHHOLD = 0.4f; // backface rejection threshhold const float DECAL_HALF_SLAB_THICKNESS = 0.2f; // thickness of the bounding volume for decals PhysDecalSysClass::PhysDecalSysClass(PhysicsSceneClass * parent_scene) : ParentScene(parent_scene), CreatePermanentDecals(false), NextTempDecalIndex(0), DecalMaterial(NULL), DecalShader(0) { allocate_resources(); Set_Temporary_Decal_Pool_Size(50); } PhysDecalSysClass::~PhysDecalSysClass(void) { release_resources(); } void PhysDecalSysClass::Update_Decal_Fade_Distances(const CameraClass & camera) { /* ** Since we don't have decal fading any more, set the decal fade distance to the far clip plane */ float znear,zfar; camera.Get_Clip_Planes(znear,zfar); WW3D::Set_Decal_Rejection_Distance(zfar); } int PhysDecalSysClass::Create_Decal ( const Matrix3D & tm, const char * texture_name, float radius, bool is_permanent, bool apply_to_translucent_meshes, PhysClass * only_this_obj ) { /* ** Allocate the decal generator */ CreatePermanentDecals = is_permanent; DecalGeneratorClass * gen = Lock_Decal_Generator(); WWASSERT(gen != NULL); /* ** Set up the transform, projection, and bounding volume parameters ** I want a thin bounding volume. Since the transform given is right at the impact point, ** we need to back-up some amount and then set the near and far z depths such that ** they bound the point */ float backup_dist = DECAL_HALF_SLAB_THICKNESS + 0.01f; Matrix3D transform = tm; transform.Translate_Z(backup_dist); gen->Set_Transform(transform); gen->Set_Ortho_Projection(-radius,radius,-radius,radius,0.01f,backup_dist + 2.0f * DECAL_HALF_SLAB_THICKNESS); gen->Set_Backface_Threshhold(DECAL_BACKFACE_THRESHHOLD); gen->Apply_To_Translucent_Meshes(apply_to_translucent_meshes); #if DEBUG_DECALS ParentScene->Add_Debug_OBBox(gen->Get_Bounding_Volume(),Vector3(0,0,1)); ParentScene->Add_Debug_Axes(tm,Vector3(1,1,1)); ParentScene->Add_Debug_Axes(transform,Vector3(1,1,1)); #endif /* ** Set up the material settings. Just plug in the standard alpha shader and the ** vertex material which all decals use. Then grab the texture which the user ** specified... */ MaterialPassClass * material = gen->Get_Material(); material->Set_Shader(DecalShader); material->Set_Material(DecalMaterial); TextureClass * tex = WW3DAssetManager::Get_Instance()->Get_Texture(texture_name,TextureClass::MIP_LEVELS_ALL); tex->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP); tex->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP); material->Set_Texture(tex); tex->Release_Ref(); material->Release_Ref(); /* ** Collect objects to apply the decal to */ NonRefPhysListClass list; if (only_this_obj == NULL) { ParentScene->Collect_Objects(gen->Get_Bounding_Volume(),true,false,&list); } else { list.Add(only_this_obj); } /* ** Apply! */ NonRefPhysListIterator it(&list); for (it.First(); !it.Is_Done(); it.Next()) { it.Peek_Obj()->Peek_Model()->Create_Decal(gen); } /* ** Done. Return the logical id of the decal if we actually created anything, ** otherwise return -1 */ int return_id = -1; if (gen->Get_Mesh_List().Is_Empty() == false) { return_id = gen->Get_Decal_ID(); } Unlock_Decal_Generator(gen); return return_id; } bool PhysDecalSysClass::Remove_Decal(uint32 id) { return internal_remove_decal(id,NULL); } void PhysDecalSysClass::Unlock_Decal_Generator(DecalGeneratorClass * generator) { /* ** If the generator actually created decal polygons, add the results ** to our internal pools */ if (generator->Get_Mesh_List().Is_Empty() == false) { if (is_decal_id_permanent(generator->Get_Decal_ID())) { LogicalDecalClass * newdecal = new LogicalDecalClass; newdecal->Init(generator); PermanentDecals.Add(newdecal); } else { /* ** the id should match the "next temp decal index" */ WWASSERT(generator->Get_Decal_ID() == NextTempDecalIndex); TempDecals[NextTempDecalIndex].Init(generator); /* ** bump the index forward */ NextTempDecalIndex = (NextTempDecalIndex + 1) % TempDecals.Length(); } } DecalSystemClass::Unlock_Decal_Generator(generator); } void PhysDecalSysClass::Decal_Mesh_Destroyed(uint32 decal_id,DecalMeshClass * mesh) { /* ** Must remove this mesh from any decals */ internal_remove_decal(decal_id,mesh->Peek_Parent()); } void PhysDecalSysClass::Set_Temporary_Decal_Pool_Size(int count) { WWASSERT(count > 0); /* ** If we are shrinking, remove all decals in the slots that are going away */ if (count < TempDecals.Length()) { for (int i=count; i= (uint32)TempDecals.Length()) { NextTempDecalIndex = 0; } } int PhysDecalSysClass::Get_Temporary_Decal_Pool_Size(void) { return TempDecals.Length(); } uint32 PhysDecalSysClass::Generate_Decal_Id(void) { uint32 id; if (CreatePermanentDecals) { id = Generate_Unique_Global_Decal_Id() & 0x7FFFFFFF; id |= 0x80000000; } else { id = NextTempDecalIndex; } return id; } bool PhysDecalSysClass::is_decal_id_permanent(uint32 id) { if (id & 0x80000000) { return true; } else { return false; } } void PhysDecalSysClass::allocate_resources(void) { WWASSERT(DecalMaterial == NULL); DecalMaterial = NEW_REF(VertexMaterialClass,()); DecalMaterial->Set_Ambient(0,0,0); DecalMaterial->Set_Diffuse(0,0,0); DecalMaterial->Set_Specular(0,0,0); DecalMaterial->Set_Emissive(1,1,1); DecalMaterial->Set_Opacity(1.0f); DecalMaterial->Set_Shininess(0.0f); DecalShader = ShaderClass::_PresetAlphaShader; } void PhysDecalSysClass::release_resources(void) { REF_PTR_RELEASE(DecalMaterial); } bool PhysDecalSysClass::internal_remove_decal(uint32 id,MeshClass * deleted_mesh) { bool success = false; if (is_decal_id_permanent(id)) { /* ** Find the decal with the given id in the Permanent decal array */ MultiListIterator it(&PermanentDecals); for (it.First(); !it.Is_Done(); it.Next()) { LogicalDecalClass * decal = it.Get_Obj(); if (decal->DecalID == id) { /* ** Just remove the deleted mesh from our list. We don't need to delete ** its decals since it is telling us that it has been deleted already. */ if (deleted_mesh != NULL) { decal->Meshes.Delete(deleted_mesh); } /* ** Now destroy this logical decal, this results in all decal-meshes that ** are referenced by this logical decal being told to remove this decal id. */ PermanentDecals.Remove(decal); delete decal; success = true; break; } } } else { /* ** Temporary decals use their index as the id */ WWASSERT(id >= 0); WWASSERT(id < (uint32)TempDecals.Length()); if (deleted_mesh != NULL) { TempDecals[id].Meshes.Delete(deleted_mesh); } TempDecals[id].Reset(); success = true; } return success; } /* ** LogicalDecalClass Implementation */ PhysDecalSysClass::LogicalDecalClass::LogicalDecalClass(void) : DecalID(0xFFFFFFFF) { } PhysDecalSysClass::LogicalDecalClass::~LogicalDecalClass(void) { Reset(); } void PhysDecalSysClass::LogicalDecalClass::Init(DecalGeneratorClass * gen) { /* ** Reset ourselves in case we were being used to track a previous decal. ** This causes that decal to get removed from the system. */ Reset(); /* ** Record the data for this decal */ DecalID = gen->Get_Decal_ID(); NonRefRenderObjListIterator it(&(gen->Get_Mesh_List())); for (it.First(); !it.Is_Done(); it.Next()) { MeshClass * mesh = (MeshClass *)it.Get_Obj(); WWASSERT(mesh->Class_ID()==RenderObjClass::CLASSID_MESH); Meshes.Add(mesh); } } void PhysDecalSysClass::LogicalDecalClass::Reset(void) { for (int i=0; iDelete_Decal(DecalID); } Meshes.Delete_All(); DecalID = 0xFFFFFFFF; }