/* ** 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 : ww3d * * * * $Archive:: /Commando/Code/ww3d2/meshmatdesc.cpp $* * * * Original Author:: Greg Hjelstrom * * * * $Author:: Greg_h $* * * * $Modtime:: 1/18/02 8:03p $* * * * $Revision:: 28 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "meshmatdesc.h" #include "texture.h" #include "vertmaterial.h" #include "realcrc.h" #include "dx8wrapper.h" #include "dx8caps.h" #include "meshmdl.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]); } } // 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)); } return TextureArray[pass][stage]; } MatBufferClass * MeshMatDescClass::Get_Material_Array(int pass,bool create) { if (create && MaterialArray[pass] == NULL) { MaterialArray[pass] = NEW_REF(MatBufferClass,(VertexCount)); } return MaterialArray[pass]; } ShaderClass * MeshMatDescClass::Get_Shader_Array(int pass,bool create) { if (create && ShaderArray[pass] == NULL) { ShaderArray[pass] = NEW_REF(ShareBufferClass,(PolyCount)); 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)); 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,MeshModelClass * parent) { /* ** Configure all vertex materials to source the uv coordinates and colors from the correct arrays ** Pre-multiply the vertex color arrays. */ bool set_lighting_to_false=true; 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); // mtl->Set_Lighting(false); } else { if (PassCount!=1) { set_lighting_to_false=false; // Lighting can only be set to false if ALL passes and ALL materials are requesting it } } } } } /* ** HACK: Kill BUMPENV passes on hardware that doesn't support BUMPENV ** HACK: Set lighting to false on all passes if all passes are of type NO DIFFUSE, NO AMBIENT, YES EMISSIVE */ for (pass=0; passSupport_Bump_Envmap() == false) ) { kill_pass = true; } if ( (Shader[pass].Get_Primary_Gradient() == ShaderClass::GRADIENT_BUMPENVMAPLUMINANCE) && (!DX8Wrapper::Is_Initted() || DX8Wrapper::Get_Current_Caps()->Support_Bump_Envmap_Luminance() == false) ) { kill_pass = true; } if (kill_pass) { if (Material[pass] != NULL) { Material[pass]->Set_Ambient(0,0,0); Material[pass]->Set_Diffuse(0,0,0); Material[pass]->Set_Emissive(0,0,0); Material[pass]->Set_Specular(0,0,0); } Shader[pass].Set_Texturing(ShaderClass::TEXTURING_DISABLE); Shader[pass].Set_Post_Detail_Color_Func(ShaderClass::DETAILCOLOR_DISABLE); Shader[pass].Set_Post_Detail_Alpha_Func(ShaderClass::DETAILALPHA_DISABLE); } // Set lighting to false if requested in all passes... else if (set_lighting_to_false) { Vector3 single_diffuse(0.0f,0.0f,0.0f); Vector3 single_ambient(0.0f,0.0f,0.0f); Vector3 single_emissive(0.0f,0.0f,0.0f); bool diffuse_used=false; bool ambient_used=false; bool emissive_used=false; Vector3 mtl_diffuse; Vector3 mtl_ambient; Vector3 mtl_emissive; VertexMaterialClass * prev_mtl = NULL; VertexMaterialClass * mtl = Peek_Material(0, pass); if (mtl) { mtl->Get_Diffuse(&single_diffuse); 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; } for (int vidx=0; vidxGet_Diffuse(&mtl_diffuse); mtl->Get_Ambient(&mtl_ambient); mtl->Get_Emissive(&mtl_emissive); } 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 ((DCGSource[pass] != VertexMaterialClass::MATERIAL) && (ColorArray[0] != NULL)) { VertexMaterialClass * prev_mtl = NULL; VertexMaterialClass * mtl = Peek_Material(0,pass); for (int vidx=0; vidxSet_Lighting(false); } } } } } } // HACK: force meshes which are named b_wire and using texture razorw.tga to use alpha-test // These meshes will follow the given pattern: // - there will be a single vertex material, shader, and texture for the first pass // - the texture will be named razorw.tga // - the mesh name will contain b_wire #pragma message("(gth) Renegade-specific hack, forcing b_wire mesh to use alpha-test...") if ( (parent != NULL) && (PassCount == 1) && (Has_Shader_Array(0) == false) && (Has_Texture_Array(0,0) == false) && (Has_Material_Array(0) == false)) { if (strstr(parent->Get_Name(),"B_WIRE")) { TextureClass * tex = Peek_Single_Texture(0,0); if (tex && stricmp(tex->Get_Texture_Name(),"razorw.tga") == 0) { ShaderClass shader = Get_Single_Shader(0); shader.Set_Alpha_Test(ShaderClass::ALPHATEST_ENABLE); shader.Set_Dst_Blend_Func(ShaderClass::DSTBLEND_ZERO); shader.Set_Src_Blend_Func(ShaderClass::SRCBLEND_ONE); shader.Set_Depth_Mask(ShaderClass::DEPTH_WRITE_ENABLE); Set_Single_Shader(shader,0); parent->Set_Flag(MeshGeometryClass::SORT,false); } } } } 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 (DX8Wrapper::Is_Initted() && DX8Wrapper::Get_Current_Caps()->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; }