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

391 lines
11 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/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<TempDecals.Length(); i++) {
TempDecals[i].Reset();
}
}
/*
** Resize the array
*/
TempDecals.Resize(count);
/*
** Make sure that our NextTempDecalIndex is valid, if not wrap it around
*/
if (NextTempDecalIndex >= (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<LogicalDecalClass> 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; i<Meshes.Count(); i++) {
Meshes[i]->Delete_Decal(DecalID);
}
Meshes.Delete_All();
DecalID = 0xFFFFFFFF;
}