/*
** Command & Conquer Generals(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 : ww3d *
* *
* $Archive:: /Commando/Code/ww3d2/meshmatdesc.cpp $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 7/13/01 1:38p $*
* *
* $Revision:: 20 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "meshmatdesc.h"
#include "texture.h"
#include "vertmaterial.h"
#include "realcrc.h"
#include "dx8wrapper.h"
#include "dx8caps.h"
/**************************************************************************************************
**
**
** MatBufferClass Implementation
**
**
**************************************************************************************************/
MatBufferClass::MatBufferClass(const MatBufferClass & that) :
ShareBufferClass(that)
{
// add a reference for each pointer that was copied...
for (int i=0; iAdd_Ref();
}
}
}
MatBufferClass::~MatBufferClass(void)
{
for (int i=0; iAdd_Ref();
}
return Array[index];
}
VertexMaterialClass * MatBufferClass::Peek_Element(int index)
{
return Array[index];
}
/**************************************************************************************************
**
**
** TexBufferClass Implementation
**
**
**************************************************************************************************/
TexBufferClass::TexBufferClass(const TexBufferClass & that) :
ShareBufferClass(that)
{
// add a reference for each pointer that was copied...
for (int i=0; iAdd_Ref();
}
}
}
TexBufferClass::~TexBufferClass(void)
{
for (int i=0;iAdd_Ref();
}
return Array[index];
}
TextureClass * TexBufferClass::Peek_Element(int index)
{
return Array[index];
}
/**************************************************************************************************
**
**
** UVBufferClass Implementation
**
**
**************************************************************************************************/
UVBufferClass::UVBufferClass(const UVBufferClass & that) :
ShareBufferClass(that)
{
CRC = that.CRC;
}
bool UVBufferClass::operator == (const UVBufferClass & that)
{
// NOTE: this only works if you've properly called Update_CRC after filling the array
return (CRC == that.CRC);
}
bool UVBufferClass::Is_Equal_To(const UVBufferClass & that)
{
// NOTE: this only works if you've properly called Update_CRC after filling the array
return (CRC == that.CRC);
}
void UVBufferClass::Update_CRC(void)
{
CRC = CRC_Memory((unsigned char *)Get_Array(),Get_Count() * sizeof(Vector2));
}
/**************************************************************************************************
**
**
** MeshMatDescClass Implementation
**
**
**************************************************************************************************/
ShaderClass MeshMatDescClass::NullShader(0); // Used to mark no shader data
MeshMatDescClass::MeshMatDescClass(void) :
PassCount(1),
VertexCount(0),
PolyCount(0)
{
for (int array=0;array < MAX_COLOR_ARRAYS; array++) {
ColorArray[array] = NULL;
}
for (int uvarray=0;uvarray,(*that.ShaderArray[pass]));
}
}
}
return *this;
}
MeshMatDescClass::~MeshMatDescClass(void)
{
Reset(0,0,0);
}
TextureClass * MeshMatDescClass::Get_Single_Texture(int pass,int stage) const
{
if (Texture[pass][stage]) {
Texture[pass][stage]->Add_Ref();
}
return Texture[pass][stage];
}
void MeshMatDescClass::Reset(int polycount,int vertcount,int passcount)
{
PolyCount = polycount;
VertexCount = vertcount;
PassCount = passcount;
for (int array=0; arrayGet_CRC() == UV[i]->Get_CRC()) {
found_index = i;
break;
}
}
// If we already have it, just set the source index. Otherwise add-ref it
// into a new slot in our uv array and set that index.
if (found_index != -1) {
UVSource[pass][stage] = found_index;
} else {
int new_index = Get_UV_Array_Count();
REF_PTR_SET(UV[new_index],default_materials.UV[default_uv_source]);
UVSource[pass][stage] = new_index;
}
}
} else {
UVSource[pass][stage] = alternate_materials.UVSource[pass][stage];
}
// Texture pointer(s): If alternate_materials has either a single texture or an array of textures,
// then add-ref only the texture data it contains. Otherwise, add-ref the data in default_materials.
if ((alternate_materials.Texture[pass][stage] != NULL) || (alternate_materials.TextureArray[pass][stage])) {
REF_PTR_SET(Texture[pass][stage] , alternate_materials.Texture[pass][stage]);
REF_PTR_SET(TextureArray[pass][stage] , alternate_materials.TextureArray[pass][stage]);
} else {
REF_PTR_SET(Texture[pass][stage] , default_materials.Texture[pass][stage]);
REF_PTR_SET(TextureArray[pass][stage] , default_materials.TextureArray[pass][stage]);
}
}
// UV Index array
// if (alternate_materials.UVIndex[pass] != NULL) {
// REF_PTR_SET(UVIndex[pass],alternate_materials.UVIndex[pass]);
// } else {
// REF_PTR_SET(UVIndex[pass],default_materials.UVIndex[pass]);
// }
// Vertex color configuration
if (alternate_materials.DCGSource[pass] == VertexMaterialClass::MATERIAL) {
DCGSource[pass] = default_materials.DCGSource[pass];
} else {
DCGSource[pass] = alternate_materials.DCGSource[pass];
}
// Shaders, currently I can't tell if the alternate data has a shader... Can't override the shader for now.
Shader[pass] = default_materials.Shader[pass];
REF_PTR_SET(ShaderArray[pass],default_materials.ShaderArray[pass]);
// Vertex Materials. If alternate_materials has either a single or array of materials, then copy them
if ((alternate_materials.Material[pass] != NULL) || (alternate_materials.MaterialArray[pass] != NULL)) {
REF_PTR_SET(Material[pass],alternate_materials.Material[pass]);
REF_PTR_SET(MaterialArray[pass],alternate_materials.MaterialArray[pass]);
} else {
// Dont share vertex materials! (because the UVSources can be different!)
if (default_materials.Material[pass]) {
Material[pass] = NEW_REF(VertexMaterialClass,(*(default_materials.Material[pass])));
} else {
if (default_materials.MaterialArray[pass]) {
WWDEBUG_SAY(("Unimplemented case: mesh has more than one default vertex material but no alternate vertex materials have been defined.\r\n"));
}
Material[pass] = NULL;
}
}
}
}
bool MeshMatDescClass::Is_Empty(void)
{
for (int array=0; arraySet_Element(vidx,vmat);
}
void MeshMatDescClass::Set_Shader(int pidx,ShaderClass shader,int pass)
{
ShaderClass * shaders = Get_Shader_Array(pass,true);
shaders[pidx] = shader;
}
void MeshMatDescClass::Set_Texture(int pidx,TextureClass * tex,int pass,int stage)
{
TexBufferClass * textures = Get_Texture_Array(pass,stage,true);
textures->Set_Element(pidx,tex);
}
VertexMaterialClass * MeshMatDescClass::Get_Material(int vidx,int pass) const
{
if (MaterialArray[pass]) {
return MaterialArray[pass]->Get_Element(vidx);
} else if (Material[pass] != NULL) {
Material[pass]->Add_Ref();
return Material[pass];
}
return NULL;
}
ShaderClass MeshMatDescClass::Get_Shader(int pidx,int pass) const
{
if (ShaderArray[pass]) {
return ShaderArray[pass]->Get_Element(pidx);
}
return Shader[pass];
}
TextureClass * MeshMatDescClass::Get_Texture(int pidx,int pass,int stage) const
{
if (TextureArray[pass][stage]) {
return TextureArray[pass][stage]->Get_Element(pidx);
} else if (Texture[pass][stage] != NULL) {
Texture[pass][stage]->Add_Ref();
return Texture[pass][stage];
}
return NULL;
}
VertexMaterialClass * MeshMatDescClass::Peek_Material(int vidx,int pass) const
{
if (MaterialArray[pass]) {
return MaterialArray[pass]->Peek_Element(vidx);
}
return Material[pass];
}
TextureClass * MeshMatDescClass::Peek_Texture(int pidx,int pass,int stage) const
{
if (TextureArray[pass][stage]) {
return TextureArray[pass][stage]->Peek_Element(pidx);
}
return Texture[pass][stage];
}
TexBufferClass * MeshMatDescClass::Get_Texture_Array(int pass,int stage,bool create)
{
if (create && TextureArray[pass][stage] == NULL) {
TextureArray[pass][stage] = NEW_REF(TexBufferClass,(PolyCount, "MeshMatDescClass::TextureArray"));
}
return TextureArray[pass][stage];
}
MatBufferClass * MeshMatDescClass::Get_Material_Array(int pass,bool create)
{
if (create && MaterialArray[pass] == NULL) {
MaterialArray[pass] = NEW_REF(MatBufferClass,(VertexCount, "MeshMatDescClass::MaterialArray"));
}
return MaterialArray[pass];
}
ShaderClass * MeshMatDescClass::Get_Shader_Array(int pass,bool create)
{
if (create && ShaderArray[pass] == NULL) {
ShaderArray[pass] = NEW_REF(ShareBufferClass,(PolyCount, "MeshMatDescClass::ShaderArray"));
ShaderArray[pass]->Clear();
}
if (ShaderArray[pass]) {
return ShaderArray[pass]->Get_Array();
}
return NULL;
}
void MeshMatDescClass::Make_UV_Array_Unique(int pass,int stage)
{
int uvindex = UVSource[pass][stage];
if (UV[uvindex]->Num_Refs() > 1) {
UVBufferClass * unique_uv = NEW_REF(UVBufferClass,(*UV[uvindex]));
UV[uvindex]->Release_Ref();
UV[uvindex] = unique_uv;
}
}
void MeshMatDescClass::Make_Color_Array_Unique(int array)
{
if ((ColorArray[array] != NULL) && (ColorArray[array]->Num_Refs() > 1)) {
ShareBufferClass * unique_color_array = NEW_REF(ShareBufferClass,(*ColorArray[array]));
ColorArray[array]->Release_Ref();
ColorArray[array] = unique_color_array;
}
}
void MeshMatDescClass::Install_UV_Array(int pass,int stage,Vector2 * uvs,int count)
{
/*
** Compute the crc of this uv array
*/
unsigned int crc = CRC_Memory((unsigned char *)uvs,count * sizeof(Vector2));
/*
** See if there is an existing uv-array that matches the one just loaded
*/
bool found = false;
for (int i=0; iGet_CRC() == crc) {
found = true;
Set_UV_Source(pass,stage,i);
break;
}
}
/*
** If there was no existing uv array, install this one
*/
if (found == false) {
/*
** Find the first empty UV-array slot
*/
int new_index = 0;
while ((UV[new_index] != NULL) && (new_index < MAX_UV_ARRAYS)) {
new_index++;
}
if (new_index < MAX_UV_ARRAYS) {
WWASSERT(UV[new_index] == NULL);
UV[new_index] = NEW_REF(UVBufferClass,(count, "MeshMatDescClass::UV"));
memcpy(UV[new_index]->Get_Array(),uvs,count * sizeof(Vector2));
UV[new_index]->Update_CRC(); // update the crc for future comparision
Set_UV_Source(pass,stage,new_index);
}
}
}
void MeshMatDescClass::Post_Load_Process(bool lighting_enabled)
{
/*
** Configure all vertex materials to source the uv coordinates and colors from the correct arrays
** Pre-multiply the vertex color arrays.
*/
for (int pass=0; passGet_Diffuse(&single_diffuse);
single_opacity = mtl->Get_Opacity();
mtl->Get_Ambient(&single_ambient);
mtl->Get_Emissive(&single_emissive);
if (single_diffuse.X || single_diffuse.Y || single_diffuse.Z) diffuse_used=true;
if (single_ambient.X || single_ambient.Y || single_ambient.Z) ambient_used=true;
if (single_emissive.X || single_emissive.Y || single_emissive.Z) emissive_used=true;
if (single_opacity!=1.0f) opacity_used=true;
}
for (int vidx=0; vidxGet_Diffuse(&mtl_diffuse);
mtl_opacity = mtl->Get_Opacity();
mtl->Get_Ambient(&mtl_ambient);
mtl->Get_Emissive(&mtl_emissive);
}
if (mtl_diffuse.X!=single_diffuse.X || mtl_diffuse.Y!=single_diffuse.Y || mtl_diffuse.Z!=single_diffuse.Z) {
single_diffuse_used=false;
}
if (mtl_ambient.X!=single_ambient.X || mtl_ambient.Y!=single_ambient.Y || mtl_ambient.Z!=single_ambient.Z) {
single_ambient_used=false;
}
if (mtl_emissive.X!=single_emissive.X || mtl_emissive.Y!=single_emissive.Y || mtl_emissive.Z!=single_emissive.Z) {
single_emissive_used=false;
}
if (mtl_opacity!=single_opacity) {
single_opacity_used=false;
}
if (mtl_diffuse.X || mtl_diffuse.Y || mtl_diffuse.Z) diffuse_used=true;
if (mtl_ambient.X || mtl_ambient.Y || mtl_ambient.Z) ambient_used=true;
if (mtl_emissive.X || mtl_emissive.Y || mtl_emissive.Z) emissive_used=true;
if (mtl_opacity!=1.0f) opacity_used=true;
}
// If both DCG and DIG arrays are submitted, multiply them together to DCG channel
if ((DCGSource[pass] != VertexMaterialClass::MATERIAL) && (ColorArray[0] != NULL) &&
(DIGSource[pass] != VertexMaterialClass::MATERIAL) && (ColorArray[1] != NULL)) {
unsigned * diffuse_array = ColorArray[0]->Get_Array();
unsigned * emissive_array = ColorArray[1]->Get_Array();
for (int vidx=0; vidxGet_Array();
Vector3 mtl_diffuse;
float mtl_opacity = 1.0f;
VertexMaterialClass * prev_mtl = NULL;
VertexMaterialClass * mtl = Peek_Material(0,pass);
for (int vidx=0; vidxGet_Diffuse(&mtl_diffuse);
mtl_opacity = mtl->Get_Opacity();
}
// If only diffuse is used apply diffuse to color channel and set diffuse source to color 1
if (diffuse_used && !ambient_used && !emissive_used) {
Vector4 diffuse=DX8Wrapper::Convert_Color(diffuse_array[vidx]);
diffuse.X *= mtl_diffuse.X;
diffuse.Y *= mtl_diffuse.Y;
diffuse.Z *= mtl_diffuse.Z;
diffuse.W *= mtl_opacity;
diffuse_array[vidx]=DX8Wrapper::Convert_Color(diffuse);
mtl->Set_Ambient_Color_Source(VertexMaterialClass::MATERIAL);
mtl->Set_Diffuse_Color_Source(VertexMaterialClass::COLOR1);
mtl->Set_Emissive_Color_Source(VertexMaterialClass::MATERIAL);
}
// If diffuse and ambient are used, apply diffuse to color channel and set diffuse
// and ambient sources to color 1. (this is not completely correct if diffuse and
// ambient are different but is probably the most reasonable thing to do. Why set
// diffuse and ambient differently anyway?)
if (diffuse_used && ambient_used && !emissive_used) {
Vector4 diffuse=DX8Wrapper::Convert_Color(diffuse_array[vidx]);
diffuse.X *= mtl_diffuse.X;
diffuse.Y *= mtl_diffuse.Y;
diffuse.Z *= mtl_diffuse.Z;
diffuse.W *= mtl_opacity;
diffuse_array[vidx]=DX8Wrapper::Convert_Color(diffuse);
mtl->Set_Ambient_Color_Source(VertexMaterialClass::COLOR1);
mtl->Set_Diffuse_Color_Source(VertexMaterialClass::COLOR1);
mtl->Set_Emissive_Color_Source(VertexMaterialClass::MATERIAL);
}
// If only ambient is used apply ambient to color channel and set ambient source to color 1
if (!diffuse_used && ambient_used && !emissive_used) {
Vector4 diffuse=DX8Wrapper::Convert_Color(diffuse_array[vidx]);
diffuse.X *= mtl_ambient.X;
diffuse.Y *= mtl_ambient.Y;
diffuse.Z *= mtl_ambient.Z;
diffuse.W *= mtl_opacity;
diffuse_array[vidx]=DX8Wrapper::Convert_Color(diffuse);
mtl->Set_Ambient_Color_Source(VertexMaterialClass::COLOR1);
mtl->Set_Diffuse_Color_Source(VertexMaterialClass::MATERIAL);
mtl->Set_Emissive_Color_Source(VertexMaterialClass::MATERIAL);
}
// If only emissive is used apply emissive to color channel, set diffuse source to color 1, and turn off lighting
if (!diffuse_used && !ambient_used && emissive_used) {
Vector4 diffuse=DX8Wrapper::Convert_Color(diffuse_array[vidx]);
diffuse.X *= mtl_emissive.X;
diffuse.Y *= mtl_emissive.Y;
diffuse.Z *= mtl_emissive.Z;
diffuse.W *= mtl_opacity;
diffuse_array[vidx]=DX8Wrapper::Convert_Color(diffuse);
mtl->Set_Ambient_Color_Source(VertexMaterialClass::MATERIAL);
mtl->Set_Diffuse_Color_Source(VertexMaterialClass::COLOR1);
mtl->Set_Emissive_Color_Source(VertexMaterialClass::MATERIAL);
//MW: Vegas guys asked me to disable this because it can cause z-fighting if lighting is disabled in multi-pass
// mtl->Set_Lighting(false);
}
}
}
/*
** If a DCG array is present, pre multiply the alpha value from the material into
** the vertex color array. Experimentation on GeForce hardware showed that we
** don't need to pre-multiply the color values; hopefully this is the behavior on
** other hardware as well!
*/
/* if ((DCGSource[pass] != VertexMaterialClass::MATERIAL) && (ColorArray[0] != NULL)) {
unsigned * diffuse_array = ColorArray[0]->Get_Array();
Vector3 mtl_diffuse;
float mtl_opacity = 1.0f;
VertexMaterialClass * prev_mtl = NULL;
VertexMaterialClass * mtl = Peek_Material(0,pass);
for (int vidx=0; vidxGet_Diffuse(&mtl_diffuse);
mtl_opacity = mtl->Get_Opacity();
}
Vector4 diffuse=DX8Wrapper::Convert_Color(diffuse_array[vidx]);
diffuse.X *= mtl_diffuse.X;
diffuse.Y *= mtl_diffuse.Y;
diffuse.Z *= mtl_diffuse.Z;
diffuse.W *= mtl_opacity;
diffuse_array[vidx]=DX8Wrapper::Convert_Color(diffuse);
}
}
*/ /*
** If needed, pre-multiply the emissive color array with the material color
*/
/* if ((DIGSource[pass] != VertexMaterialClass::MATERIAL) && (ColorArray[1] != NULL)) {
unsigned * emissive_array = ColorArray[1]->Get_Array();
Vector3 mtl_emissive;
VertexMaterialClass * prev_mtl = NULL;
VertexMaterialClass * mtl = Peek_Material(0,pass);
for (int vidx=0; vidxGet_Emissive(&mtl_emissive);
}
Vector4 emissive=DX8Wrapper::Convert_Color(emissive_array[vidx]);
emissive.X *= mtl_emissive.X;
emissive.Y *= mtl_emissive.Y;
emissive.Z *= mtl_emissive.Z;
emissive_array[vidx]=DX8Wrapper::Convert_Color(emissive);
}
}
*/
}
}
void MeshMatDescClass::Configure_Material(VertexMaterialClass * mtl,int pass,bool lighting_enabled)
{
mtl->Set_Diffuse_Color_Source(DCGSource[pass]);
mtl->Set_Emissive_Color_Source(DIGSource[pass]);
mtl->Set_Lighting(lighting_enabled);
for (int stage=0; stageSet_UV_Source(stage,src);
}
}
bool MeshMatDescClass::Do_Mappers_Need_Normals(void)
{
if (DX8Caps::Support_NPatches() && WW3D::Get_NPatches_Level()>1) return true;
for (int pass=0; passDo_Mappers_Need_Normals()) return true;
} else {
VertexMaterialClass * prev_mtl = NULL;
VertexMaterialClass * mtl = Peek_Material(pass,0);
for (int vidx=0; vidxDo_Mappers_Need_Normals()) return true;
prev_mtl = mtl;
}
}
}
}
return false;
}