400 lines
12 KiB
C++
400 lines
12 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 : ENBAssetMgr *
|
||
|
* *
|
||
|
* $Archive:: /VSS_Sync/ww3d2/hueshift/enbassetmgr.cpp $*
|
||
|
* *
|
||
|
* Original Author:: Hector Yee *
|
||
|
* *
|
||
|
* $Author:: Vss_sync $*
|
||
|
* *
|
||
|
* $Modtime:: 8/29/01 10:35p $*
|
||
|
* *
|
||
|
* $Revision:: 1 $*
|
||
|
* *
|
||
|
*---------------------------------------------------------------------------------------------*
|
||
|
* Functions: *
|
||
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||
|
|
||
|
#include "always.h"
|
||
|
#include "enbassetmgr.h"
|
||
|
#include "proto.h"
|
||
|
#include "rendobj.h"
|
||
|
#include "vector3.h"
|
||
|
#include "mesh.h"
|
||
|
#include "hlod.h"
|
||
|
#include "matinfo.h"
|
||
|
#include "meshmdl.h"
|
||
|
#include "part_emt.h"
|
||
|
#include "vertmaterial.h"
|
||
|
#include "dx8wrapper.h"
|
||
|
#include "texture.h"
|
||
|
#include "surfaceclass.h"
|
||
|
#include "textureloader.h"
|
||
|
#include "ww3dformat.h"
|
||
|
#include "colorspace.h"
|
||
|
#include <stdio.h>
|
||
|
|
||
|
//---------------------------------------------------------------------
|
||
|
// Constants
|
||
|
//---------------------------------------------------------------------
|
||
|
|
||
|
const float ident_scale(1.0f);
|
||
|
const Vector3 ident_HSV(0,0,0);
|
||
|
|
||
|
//---------------------------------------------------------------------
|
||
|
// ENBPrototype
|
||
|
//---------------------------------------------------------------------
|
||
|
|
||
|
class ENBPrototypeClass : public PrototypeClass
|
||
|
{
|
||
|
public:
|
||
|
ENBPrototypeClass(RenderObjClass * proto,const char* name);
|
||
|
virtual ~ENBPrototypeClass(void);
|
||
|
|
||
|
virtual const char * Get_Name(void) const;
|
||
|
virtual int Get_Class_ID(void) const;
|
||
|
virtual RenderObjClass * Create(void);
|
||
|
RenderObjClass * Proto;
|
||
|
char * Name;
|
||
|
};
|
||
|
|
||
|
ENBPrototypeClass::ENBPrototypeClass(RenderObjClass * proto, const char *name)
|
||
|
{
|
||
|
Name=strdup(name);
|
||
|
Proto = proto;
|
||
|
assert(Proto);
|
||
|
Proto->Add_Ref();
|
||
|
}
|
||
|
|
||
|
ENBPrototypeClass::~ENBPrototypeClass(void)
|
||
|
{
|
||
|
delete [] Name;
|
||
|
if (Proto) {
|
||
|
Proto->Release_Ref();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const char * ENBPrototypeClass::Get_Name(void) const
|
||
|
{
|
||
|
return Name;
|
||
|
}
|
||
|
|
||
|
int ENBPrototypeClass::Get_Class_ID(void) const
|
||
|
{
|
||
|
return Proto->Class_ID();
|
||
|
}
|
||
|
|
||
|
RenderObjClass * ENBPrototypeClass::Create(void)
|
||
|
{
|
||
|
return (RenderObjClass *)( SET_REF_OWNER( Proto->Clone() ) );
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------
|
||
|
// ENBAssetManager
|
||
|
//---------------------------------------------------------------------
|
||
|
|
||
|
ENBAssetManager::ENBAssetManager(void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
ENBAssetManager::~ENBAssetManager(void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
RenderObjClass * ENBAssetManager::Create_Render_Obj(const char * name,float Scale,Vector3 &HSV_shift)
|
||
|
{
|
||
|
char newname[512];
|
||
|
bool scale,hsv_shift;
|
||
|
scale=(Scale!=ident_scale);
|
||
|
hsv_shift=(HSV_shift!=ident_HSV);
|
||
|
|
||
|
// base case, no scale or hue shifting
|
||
|
if (!scale && !hsv_shift) return WW3DAssetManager::Create_Render_Obj(name);
|
||
|
|
||
|
sprintf(newname,"#%s!%gH%gS%gV%g",name,Scale,HSV_shift.X,HSV_shift.Y,HSV_shift.Z);
|
||
|
|
||
|
// see if we got a cached version
|
||
|
RenderObjClass *rendobj=NULL;
|
||
|
rendobj=WW3DAssetManager::Create_Render_Obj(newname);
|
||
|
if (rendobj) return rendobj;
|
||
|
|
||
|
// create a new one based on
|
||
|
// exisiting prototype
|
||
|
rendobj=WW3DAssetManager::Create_Render_Obj(name);
|
||
|
if (!rendobj) return NULL;
|
||
|
|
||
|
Make_Unique(rendobj,scale,hsv_shift);
|
||
|
if (scale) rendobj->Scale(Scale);
|
||
|
if (hsv_shift) Recolor_Asset(rendobj,HSV_shift);
|
||
|
|
||
|
ENBPrototypeClass *proto=new ENBPrototypeClass(rendobj,newname);
|
||
|
Add_Prototype(proto);
|
||
|
|
||
|
return rendobj;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------
|
||
|
// Uniqing
|
||
|
//---------------------------------------------------------------------
|
||
|
|
||
|
void ENBAssetManager::Make_Mesh_Unique(RenderObjClass *robj,bool geometry, bool colors)
|
||
|
{
|
||
|
int i;
|
||
|
MeshClass *mesh=(MeshClass*) robj;
|
||
|
mesh->Make_Unique();
|
||
|
MeshModelClass * model = mesh->Get_Model();
|
||
|
|
||
|
if (colors) {
|
||
|
// make all vertex materials unique
|
||
|
MaterialInfoClass *material = mesh->Get_Material_Info();
|
||
|
for (i=0; i<material->Vertex_Material_Count(); i++)
|
||
|
material->Peek_Vertex_Material(i)->Make_Unique();
|
||
|
REF_PTR_RELEASE(material);
|
||
|
// make all color arrays unique
|
||
|
model->Make_Color_Array_Unique(0);
|
||
|
model->Make_Color_Array_Unique(1);
|
||
|
// do not do textures yet
|
||
|
// because we want to do the color conversion
|
||
|
// for the top mip level and then
|
||
|
// mip filter instead of converting all mip levels
|
||
|
}
|
||
|
|
||
|
if (geometry) {
|
||
|
// make geometry unique
|
||
|
model->Make_Geometry_Unique();
|
||
|
}
|
||
|
|
||
|
REF_PTR_RELEASE(model);
|
||
|
}
|
||
|
|
||
|
void ENBAssetManager::Make_HLOD_Unique(RenderObjClass *robj,bool geometry, bool colors)
|
||
|
{
|
||
|
int num_sub = robj->Get_Num_Sub_Objects();
|
||
|
for(int i = 0; i < num_sub; i++) {
|
||
|
RenderObjClass *sub_obj = robj->Get_Sub_Object(i);
|
||
|
Make_Unique(sub_obj,geometry,colors);
|
||
|
REF_PTR_RELEASE(sub_obj);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ENBAssetManager::Make_Unique(RenderObjClass *robj,bool geometry, bool colors)
|
||
|
{
|
||
|
switch (robj->Class_ID()) {
|
||
|
case RenderObjClass::CLASSID_MESH:
|
||
|
Make_Mesh_Unique(robj,geometry,colors);
|
||
|
break;
|
||
|
case RenderObjClass::CLASSID_HLOD:
|
||
|
Make_HLOD_Unique(robj,geometry,colors);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------
|
||
|
// Coloring
|
||
|
//---------------------------------------------------------------------
|
||
|
|
||
|
void ENBAssetManager::Recolor_Vertex_Material(VertexMaterialClass *vmat,Vector3 &hsv_shift)
|
||
|
{
|
||
|
Vector3 rgb;
|
||
|
|
||
|
vmat->Get_Ambient(&rgb);
|
||
|
Recolor(rgb,hsv_shift);
|
||
|
vmat->Set_Ambient(rgb);
|
||
|
|
||
|
vmat->Get_Diffuse(&rgb);
|
||
|
Recolor(rgb,hsv_shift);
|
||
|
vmat->Set_Diffuse(rgb);
|
||
|
|
||
|
vmat->Get_Emissive(&rgb);
|
||
|
Recolor(rgb,hsv_shift);
|
||
|
vmat->Set_Emissive(rgb);
|
||
|
|
||
|
vmat->Get_Specular(&rgb);
|
||
|
Recolor(rgb,hsv_shift);
|
||
|
vmat->Set_Specular(rgb);
|
||
|
}
|
||
|
|
||
|
void ENBAssetManager::Recolor_Vertices(unsigned int *color, int count, Vector3 &hsv_shift)
|
||
|
{
|
||
|
int i;
|
||
|
Vector4 rgba;
|
||
|
|
||
|
for (i=0; i<count; i++)
|
||
|
{
|
||
|
rgba=DX8Wrapper::Convert_Color(color[i]);
|
||
|
Recolor(reinterpret_cast<Vector3&>(rgba),hsv_shift);
|
||
|
color[i]=DX8Wrapper::Convert_Color_Clamp(rgba);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TextureClass * ENBAssetManager::Recolor_Texture(TextureClass *texture, Vector3 &hsv_shift)
|
||
|
{
|
||
|
const char *name=texture->Get_Name();
|
||
|
|
||
|
// if texture is procedural return NULL
|
||
|
if (name && name[0]=='!') return NULL;
|
||
|
|
||
|
// make sure texture is loaded
|
||
|
if (!texture->Is_Initialized())
|
||
|
TextureLoader::Request_High_Priority_Loading(texture,0,(TextureClass::MipCountType) texture->Get_Mip_Level_Count());
|
||
|
|
||
|
SurfaceClass::SurfaceDescription desc;
|
||
|
SurfaceClass *newsurf, *oldsurf, *smallsurf;
|
||
|
texture->Get_Level_Description(desc);
|
||
|
|
||
|
// if texture is monochrome and no value shifting
|
||
|
// return NULL
|
||
|
smallsurf=texture->Get_Surface_Level((TextureClass::MipCountType)texture->Get_Mip_Level_Count()-1);
|
||
|
if (hsv_shift.Z==0.0f && smallsurf->Is_Monochrome())
|
||
|
{
|
||
|
REF_PTR_RELEASE(smallsurf);
|
||
|
return NULL;
|
||
|
}
|
||
|
REF_PTR_RELEASE(smallsurf);
|
||
|
|
||
|
oldsurf=texture->Get_Surface_Level();
|
||
|
|
||
|
// see if we have a cached copy
|
||
|
char newname[512];
|
||
|
StringClass lower_case_name(texture->Get_Name(),true);
|
||
|
_strlwr(lower_case_name.Peek_Buffer());
|
||
|
sprintf(newname,"#%s!H%gS%gV%g",lower_case_name,hsv_shift.X,hsv_shift.Y,hsv_shift.Z);
|
||
|
TextureClass* newtex = TextureHash.Get(newname);
|
||
|
if (newtex)
|
||
|
{
|
||
|
newtex->Add_Ref();
|
||
|
REF_PTR_RELEASE(oldsurf);
|
||
|
return newtex;
|
||
|
}
|
||
|
|
||
|
// no cached copy, make new recolorized texture
|
||
|
newsurf=NEW_REF(SurfaceClass,(desc.Width,desc.Height,desc.Format));
|
||
|
newsurf->Copy(0,0,0,0,desc.Width,desc.Height,oldsurf);
|
||
|
newsurf->Hue_Shift(hsv_shift);
|
||
|
newtex=NEW_REF(TextureClass,(newsurf,(TextureClass::MipCountType)texture->Get_Mip_Level_Count()));
|
||
|
newtex->Set_Mag_Filter(texture->Get_Mag_Filter());
|
||
|
newtex->Set_Min_Filter(texture->Get_Min_Filter());
|
||
|
newtex->Set_Mip_Mapping(texture->Get_Mip_Mapping());
|
||
|
newtex->Set_Name(newname);
|
||
|
newtex->Set_U_Addr_Mode(texture->Get_U_Addr_Mode());
|
||
|
newtex->Set_V_Addr_Mode(texture->Get_V_Addr_Mode());
|
||
|
|
||
|
TextureHash.Insert(newname,newtex);
|
||
|
newtex->Add_Ref();
|
||
|
|
||
|
REF_PTR_RELEASE(oldsurf);
|
||
|
REF_PTR_RELEASE(newsurf);
|
||
|
|
||
|
return newtex;
|
||
|
}
|
||
|
|
||
|
void ENBAssetManager::Recolor_Mesh(RenderObjClass *robj,Vector3 &hsv_shift)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
MeshClass *mesh=(MeshClass*) robj;
|
||
|
MeshModelClass * model = mesh->Get_Model();
|
||
|
MaterialInfoClass *material = mesh->Get_Material_Info();
|
||
|
|
||
|
// recolor vertex material
|
||
|
for (i=0; i<material->Vertex_Material_Count(); i++)
|
||
|
Recolor_Vertex_Material(material->Peek_Vertex_Material(i),hsv_shift);
|
||
|
|
||
|
// recolor color arrays
|
||
|
unsigned int * color;
|
||
|
color=model->Get_Color_Array(0,false);
|
||
|
if (color) Recolor_Vertices(color,model->Get_Vertex_Count(),hsv_shift);
|
||
|
color=model->Get_Color_Array(1,false);
|
||
|
if (color) Recolor_Vertices(color,model->Get_Vertex_Count(),hsv_shift);
|
||
|
|
||
|
// recolor textures
|
||
|
|
||
|
TextureClass *newtex,*oldtex;
|
||
|
for (i=0; i<material->Texture_Count(); i++)
|
||
|
{
|
||
|
oldtex=material->Peek_Texture(i);
|
||
|
newtex=Recolor_Texture(oldtex,hsv_shift);
|
||
|
if (newtex)
|
||
|
{
|
||
|
model->Replace_Texture(oldtex,newtex);
|
||
|
material->Replace_Texture(i,newtex);
|
||
|
REF_PTR_RELEASE(newtex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
REF_PTR_RELEASE(material);
|
||
|
REF_PTR_RELEASE(model);
|
||
|
}
|
||
|
|
||
|
void ENBAssetManager::Recolor_HLOD(RenderObjClass *robj,Vector3 &hsv_shift)
|
||
|
{
|
||
|
int num_sub = robj->Get_Num_Sub_Objects();
|
||
|
for(int i = 0; i < num_sub; i++) {
|
||
|
RenderObjClass *sub_obj = robj->Get_Sub_Object(i);
|
||
|
Recolor_Asset(sub_obj,hsv_shift);
|
||
|
REF_PTR_RELEASE(sub_obj);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ENBAssetManager::Recolor_ParticleEmitter(RenderObjClass *robj,Vector3 &hsv_shift)
|
||
|
{
|
||
|
unsigned int i;
|
||
|
|
||
|
ParticleEmitterClass* emit=(ParticleEmitterClass*) robj;
|
||
|
ParticlePropertyStruct<Vector3> colors;
|
||
|
|
||
|
emit->Get_Color_Key_Frames(colors);
|
||
|
Recolor(colors.Start,hsv_shift);
|
||
|
Recolor(colors.Rand,hsv_shift);
|
||
|
for (i=0; i<colors.NumKeyFrames; i++)
|
||
|
Recolor(colors.Values[i],hsv_shift);
|
||
|
emit->Reset_Colors(colors);
|
||
|
|
||
|
delete colors.Values;
|
||
|
delete colors.KeyTimes;
|
||
|
|
||
|
TextureClass *tex=emit->Get_Texture();
|
||
|
TextureClass *newtex=Recolor_Texture(tex,hsv_shift);
|
||
|
if (newtex)
|
||
|
{
|
||
|
emit->Set_Texture(newtex);
|
||
|
REF_PTR_RELEASE(newtex);
|
||
|
}
|
||
|
REF_PTR_RELEASE(tex);
|
||
|
}
|
||
|
|
||
|
void ENBAssetManager::Recolor_Asset(RenderObjClass *robj,Vector3 &hsv_shift)
|
||
|
{
|
||
|
switch (robj->Class_ID()) {
|
||
|
case RenderObjClass::CLASSID_MESH:
|
||
|
Recolor_Mesh(robj,hsv_shift);
|
||
|
break;
|
||
|
case RenderObjClass::CLASSID_HLOD:
|
||
|
Recolor_HLOD(robj,hsv_shift);
|
||
|
break;
|
||
|
case RenderObjClass::CLASSID_PARTICLEEMITTER:
|
||
|
Recolor_ParticleEmitter(robj,hsv_shift);
|
||
|
break;
|
||
|
}
|
||
|
}
|