391 lines
11 KiB
C++
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;
|
|
}
|
|
|