/* ** 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 : G * * * * $Archive:: /VSS_Sync/ww3d2/metalmap.cpp $* * * * $Author:: Vss_sync $* * * * $Modtime:: 10/26/01 2:56p $* * * * $Revision:: 4 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * MMMC::MetalMapManagerClass -- Create metal map manager according to given metal parameters* * MMMC::MetalMapManagerClass -- Create metal map manager from INI * * MMMC::~MetalMapManagerClass -- MetalMapManagerClass destructor * * MMMC::Get_Metal_Map -- Get the texture for a metal map by id number * * MMMC::Metal_Map_Count -- Get the number of metal maps in the manager * * MMMC::Update_Lighting -- Update the lighting parameters used for generating the maps * * MMMC::Update_Textures -- Update metal map textures (call once/frame before rendering) * * MMMC::initialize_normal_table -- Utility function to initialize the normal table * * MMMC::initialize_metal_params -- Utility function (shared CTor code) * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "metalmap.h" #include "texture.h" #include "ww3dformat.h" #include "ww3d.h" #include #include #include #include #include #include #include /* ** Class static members: */ Vector3 * MetalMapManagerClass::_NormalTable = 0; /*********************************************************************************************** * MMMC::MetalMapManagerClass -- Create metal map manager from INI * * * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 11/23/1999 NH : Created. * *=============================================================================================*/ MetalMapManagerClass::MetalMapManagerClass(INIClass &ini) : MapCount(0), Textures(0), MetalParameters(0), CurrentAmbient(0.0f, 0.0f, 0.0f), CurrentMainLightColor(0.0f, 0.0f, 0.0f), CurrentMainLightDir(1.0f, 0.0f, 0.0f), CurrentCameraDir(1.0f,0.0f,0.0f), Use16Bit(false) { // If the static normal table has not been initialized yet, initialize it if (!_NormalTable) { initialize_normal_table(); } // Determine how many metals are in this file char section[255]; for (int lp = 0; ; lp++) { sprintf(section, "Metal%02d", lp); if (!ini.Find_Section(section)) { break; // NAK - Mar 8, 2000: changed to a break to fix off by one error in lp } } if (lp > 0) { // Create metal params structs and fill from INI: MetalParams *metal_params = new MetalParams[lp]; TPoint3D white_tpoint(255.0f, 255.0f, 255.0f); Vector3 white(1.0f, 1.0f, 1.0f); Vector3 black(0.0f, 0.0f, 0.0f); for (int i = 0; i < lp; i++) { sprintf(section, "Metal%02d", i); static const float cf = 0.003921568627451f; // 1 / 255 TPoint3D color; color = ini.Get_Point(section, "AmbientColor", white_tpoint); metal_params[i].AmbientColor.Set(color.X * cf, color.Y * cf, color.Z * cf); metal_params[i].AmbientColor.Update_Min(white); metal_params[i].AmbientColor.Update_Max(black); color = ini.Get_Point(section, "DiffuseColor", white_tpoint); metal_params[i].DiffuseColor.Set(color.X * cf, color.Y * cf, color.Z * cf); metal_params[i].AmbientColor.Update_Min(white); metal_params[i].AmbientColor.Update_Max(black); color = ini.Get_Point(section, "SpecularColor", white_tpoint); metal_params[i].SpecularColor.Set(color.X * cf, color.Y * cf, color.Z * cf); metal_params[i].AmbientColor.Update_Min(white); metal_params[i].AmbientColor.Update_Max(black); float shininess = ini.Get_Float(section, "Shininess", 0.0f); metal_params[i].Shininess = WWMath::Clamp(shininess, 0.0f, 127.0f); } initialize_metal_params(lp, metal_params); delete [] metal_params; } else { assert(0); } int w,h,bits; bool windowed; WW3D::Get_Device_Resolution(w,h,bits,windowed); Use16Bit=(bits<=16); WW3DFormat format=(Use16Bit?WW3D_FORMAT_A4R4G4B4:WW3D_FORMAT_A8R8G8B8); for (int i = 0; i < lp; i++) { Textures[i]=NEW_REF(TextureClass,(METALMAP_SIZE,METALMAP_SIZE,format,TextureClass::MIP_LEVELS_1)); Textures[i]->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP); Textures[i]->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP); StringClass tex_name; tex_name.Format("!m%02d.tga", i); Textures[i]->Set_Texture_Name(tex_name); } } /*********************************************************************************************** * MMMC::~MetalMapManagerClass -- MetalMapManagerClass destructor * * * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 11/19/1999 NH : Created. * *=============================================================================================*/ MetalMapManagerClass::~MetalMapManagerClass(void) { if (Textures) { for (int i = 0; i < MapCount; i++) { REF_PTR_RELEASE(Textures[i]); } delete [] Textures; Textures = 0; } if (MetalParameters) { delete [] MetalParameters; MetalParameters = 0; } } /*********************************************************************************************** * MMMC::Get_Metal_Map -- Get the texture for a metal map by id number * * * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 11/19/1999 NH : Created. * *=============================================================================================*/ TextureClass * MetalMapManagerClass::Get_Metal_Map(int id) { if (id < 0 || id >= MapCount) return 0; Textures[id]->Add_Ref(); return Textures[id]; } /*********************************************************************************************** * MMMC::Metal_Map_Count -- Get the number of metal maps in the manager * * * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 11/19/1999 NH : Created. * *=============================================================================================*/ int MetalMapManagerClass::Metal_Map_Count(void) { return MapCount; } /*********************************************************************************************** * MMMC::Update_Lighting -- Update the lighting parameters used for generating the maps * * * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 11/19/1999 NH : Created. * *=============================================================================================*/ void MetalMapManagerClass::Update_Lighting(const Vector3& ambient, const Vector3& main_light_color, const Vector3& main_light_dir, const Vector3& camera_dir) { CurrentAmbient = ambient; CurrentMainLightColor = main_light_color; CurrentMainLightDir = main_light_dir; CurrentCameraDir= camera_dir; } /*********************************************************************************************** * MMMC::Update_Textures -- Update metal map textures (call once/frame before rendering) * * * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 11/19/1999 NH : Created. * *=============================================================================================*/ void MetalMapManagerClass::Update_Textures(void) { // Currently the lighting is done using a simple Phong (actually Blinn) model. Vector3 &l = CurrentMainLightDir; Vector3 &v = CurrentCameraDir; // Calculate halfway vector Vector3 h = l+v; h.Normalize(); // NOTE: when our lighting equation gets more complicated we might want to do some testing to // detect zero components... // Calculate quantities which are the same for all metal maps float n_dot_l[METALMAP_SIZE_2]; float n_dot_h[METALMAP_SIZE_2]; VectorProcessorClass::DotProduct(n_dot_l,l,_NormalTable,METALMAP_SIZE_2); VectorProcessorClass::ClampMin(n_dot_l, n_dot_l, 0.0f, METALMAP_SIZE_2); VectorProcessorClass::DotProduct(n_dot_h,h,_NormalTable, METALMAP_SIZE_2); VectorProcessorClass::ClampMin(n_dot_h, n_dot_h, 0.0f, METALMAP_SIZE_2); // Loop over each metal map and update it for (int i = 0; i < MapCount; i++) { MetalParams &cur_params = MetalParameters[i]; // If shinyness > 1, apply it to specular value array float *specular = 0; float temp_specular[METALMAP_SIZE_2]; float shinyness = cur_params.Shininess; if (shinyness > 1.0f) { VectorProcessorClass::Power(temp_specular, n_dot_h, shinyness, METALMAP_SIZE_2); specular = &(temp_specular[0]); } else { specular = &(n_dot_h[0]); } // Generate metal map row by row Vector3 specular_color(cur_params.SpecularColor.X * CurrentMainLightColor.X, cur_params.SpecularColor.Y * CurrentMainLightColor.Y, cur_params.SpecularColor.Z * CurrentMainLightColor.Z); Vector3 diffuse_color(cur_params.DiffuseColor.X * CurrentMainLightColor.X, cur_params.DiffuseColor.Y * CurrentMainLightColor.Y, cur_params.DiffuseColor.Z * CurrentMainLightColor.Z); Vector3 ambient_color(cur_params.AmbientColor.X * CurrentAmbient.X, cur_params.AmbientColor.Y * CurrentAmbient.Y, cur_params.AmbientColor.Z * CurrentAmbient.Z); Vector3 white(1.0f, 1.0f, 1.0f); SurfaceClass * metal_map_surface = Textures[i]->Get_Surface_Level(0); int pitch; unsigned char *map=(unsigned char *) metal_map_surface->Lock(&pitch); int idx=0; for (int y = 0; y < METALMAP_SIZE; y++) { for (int x = 0; x < METALMAP_SIZE; x++) { Vector3 result = ambient_color + (diffuse_color * n_dot_l[idx]) + (specular_color * specular[idx]); result.Update_Min(white); // Clamp to white unsigned char b,g,r,a; b= (unsigned char)WWMath::Floor(result.Z * 255.99f); // B g= (unsigned char)WWMath::Floor(result.Y * 255.99f); // G r= (unsigned char)WWMath::Floor(result.X * 255.99f); // R a= 0xFF; // A if (Use16Bit) { unsigned short tmp; tmp=(a&0xf0)<<8; tmp|=(r&0xf0)<<4; tmp|=(g&0xf0); tmp|=(b&0xf0)>>4; *(unsigned short*)&map[2*x]=tmp; } else { map[4*x]=b; map[4*x+1]=g; map[4*x+2]=r; map[4*x+3]=a; } idx++; } map+=pitch; } metal_map_surface->Unlock(); REF_PTR_RELEASE(metal_map_surface); } // for i } /*********************************************************************************************** * MMMC::initialize_normal_table -- Utility function to initialize the normal table * * * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 11/19/1999 NH : Created. * *=============================================================================================*/ void MetalMapManagerClass::initialize_normal_table(void) { // NOTE: changing the actual static _NormalTable member must be the last thing this function // does to avoid synchronization errors. static Vector3 _normal_table[METALMAP_SIZE_2]; // Calculate vectors (area outside sphere should be filled with a radial fill of the vectors at // the sphere's edge to avoid aliasing artifacts) float step = 2.0f / (float)METALMAP_SIZE; int idx = 0; for (int y = 0; y < METALMAP_SIZE; y++) { for (int x = 0; x < METALMAP_SIZE; x++) { Vector3 &normal = _normal_table[idx]; // Set vector to point to surface of unit sphere normal.Set((step * (float)x) - 1.0f, (step * (float)y) - 1.0f, 0.0f); float z2 = 1 - ((normal.X * normal.X) + (normal.Y * normal.Y)); z2 = MAX(z2, 0.0f); // If outside the sphere, treat as if on its edge normal.Z = sqrt(z2); normal.Normalize(); // Needed for "outside sphere" case and for safety's sake idx++; } } _NormalTable = &(_normal_table[0]); } /*********************************************************************************************** * MMMC::initialize_metal_params -- Utility function (shared CTor code) * * * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 11/23/1999 NH : Created. * *=============================================================================================*/ void MetalMapManagerClass::initialize_metal_params(int map_count, MetalParams *metal_params) { MapCount = map_count; if (MapCount > 0) { Textures = new TextureClass *[MapCount]; MetalParameters = new MetalParams[MapCount]; for (int i = 0; i < MapCount; i++) { // Copy metal parameters (assert if invalid) MetalParameters[i] = metal_params[i]; assert(MetalParameters[i].AmbientColor.X >= 0.0f && MetalParameters[i].AmbientColor.X <= 1.0f); assert(MetalParameters[i].AmbientColor.Y >= 0.0f && MetalParameters[i].AmbientColor.Y <= 1.0f); assert(MetalParameters[i].AmbientColor.Z >= 0.0f && MetalParameters[i].AmbientColor.Z <= 1.0f); assert(MetalParameters[i].DiffuseColor.X >= 0.0f && MetalParameters[i].DiffuseColor.X <= 1.0f); assert(MetalParameters[i].DiffuseColor.Y >= 0.0f && MetalParameters[i].DiffuseColor.Y <= 1.0f); assert(MetalParameters[i].DiffuseColor.Z >= 0.0f && MetalParameters[i].DiffuseColor.Z <= 1.0f); assert(MetalParameters[i].SpecularColor.X >= 0.0f && MetalParameters[i].SpecularColor.X <= 1.0f); assert(MetalParameters[i].SpecularColor.Y >= 0.0f && MetalParameters[i].SpecularColor.Y <= 1.0f); assert(MetalParameters[i].SpecularColor.Z >= 0.0f && MetalParameters[i].SpecularColor.Z <= 1.0f); assert(MetalParameters[i].Shininess > 0.0f); } } }