/* ** Command & Conquer Generals Zero Hour(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 : WWPhys * * * * $Archive:: /Commando/Code/ww3d2/lightenvironment.cpp $* * * * Original Author:: Greg Hjelstrom * * * * $Author:: Greg_h $* * * * $Modtime:: 2/01/01 5:40p $* * * * $Revision:: 3 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "lightenvironment.h" #include "matrix3d.h" #include "camera.h" #include "light.h" #include "colorspace.h" /* ** Constants */ const float DIFFUSE_TO_AMBIENT_FRACTION = 1.0f; /* ** Static variables */ static _LightingLODCutoff = 0.5f; static _LightingLODCutoff2 = 0.5f * 0.5f; /************************************************************************************************ ** ** LightEnvironmentClass::InputLightStruct Implementation ** ************************************************************************************************/ void LightEnvironmentClass::InputLightStruct::Init ( const LightClass & light, const Vector3 & object_center ) { m_point = false; switch(light.Get_Type()) { case LightClass::POINT: case LightClass::SPOT: Init_From_Point_Or_Spot_Light(light,object_center); break; case LightClass::DIRECTIONAL: Init_From_Directional_Light(light,object_center); break; }; } void LightEnvironmentClass::InputLightStruct::Init_From_Point_Or_Spot_Light ( const LightClass & light, const Vector3 & object_center ) { /* ** Compute the direction vector and the distance to the light */ Direction = light.Get_Position() - object_center; float dist = Direction.Length(); if (dist > 0.0f) { Direction /= dist; } /* ** Compute the attenuation factor */ float atten = 1.0f; double atten_start,atten_end; light.Get_Far_Attenuation_Range(atten_start,atten_end); if (light.Get_Flag(LightClass::FAR_ATTENUATION)) { if (WWMath::Fabs(atten_end - atten_start) < WWMATH_EPSILON) { /* ** Start and end are equal, attenuation is a "step" function */ if (dist > atten_start) { atten = 0.0f; } } else { /* ** Compute the attenuation */ atten = 1.0f - (dist - atten_start) / (atten_end - atten_start); atten = WWMath::Clamp(atten,0.0f,1.0f); } } if (light.Get_Type() == LightClass::SPOT) { Vector3 spot_dir; light.Get_Spot_Direction(spot_dir); Matrix3D::Rotate_Vector(light.Get_Transform(),spot_dir,&spot_dir); float spot_angle_cos = light.Get_Spot_Angle_Cos(); atten *= (Vector3::Dot_Product(-spot_dir,Direction) - spot_angle_cos) / (1.0f - spot_angle_cos); atten = WWMath::Clamp(atten,0.0f,1.0f); } /* ** Compute the ambient and diffuse values. Rejecting the diffuse ** component and folding it into the ambient component if it is below ** the LOD cutoff */ light.Get_Ambient(&Ambient); light.Get_Diffuse(&Diffuse); Ambient *= light.Get_Intensity(); //(gth) CNC3 obey the intensity parameter Diffuse *= light.Get_Intensity(); m_point = (light.Get_Type() == LightClass::POINT); m_center = light.Get_Position(); m_innerRadius = atten_start; m_outerRadius = atten_end; m_ambient = Ambient; m_diffuse = Diffuse; if (Diffuse.Length2() > _LightingLODCutoff2) { DiffuseRejected = false; Ambient *= atten; Diffuse *= atten; } else { DiffuseRejected = true; Ambient *= atten; Ambient += atten * DIFFUSE_TO_AMBIENT_FRACTION * Diffuse; Diffuse.Set(0,0,0); } } void LightEnvironmentClass::InputLightStruct::Init_From_Directional_Light ( const LightClass & light, const Vector3 & object_center ) { Direction = -light.Get_Transform().Get_Z_Vector(); DiffuseRejected = false; light.Get_Ambient(&Ambient); light.Get_Diffuse(&Diffuse); } float LightEnvironmentClass::InputLightStruct::Contribution(void) { return Diffuse.Length2(); } /************************************************************************************************ ** ** LightEnvironmentClass::OutputLightStruct Implementation ** ************************************************************************************************/ void LightEnvironmentClass::OutputLightStruct::Init ( const InputLightStruct & input, const Matrix3D & camera_tm ) { Diffuse = input.Diffuse; Matrix3D::Inverse_Rotate_Vector(camera_tm,input.Direction,&Direction); // Guard against a direction that is invalid if(Direction.Length2() == 0.0f) { Direction.X = 1.0f; } } /************************************************************************************************ ** ** LightEnvironmentClass Implementation ** ************************************************************************************************/ LightEnvironmentClass::LightEnvironmentClass(void) : LightCount(0), ObjectCenter(0,0,0), OutputAmbient(0,0,0), FillLight(), FillIntensity(0.0f) { } LightEnvironmentClass::~LightEnvironmentClass(void) { } void LightEnvironmentClass::Reset(const Vector3 & object_center,const Vector3 & ambient) { LightCount = 0; ObjectCenter = object_center; OutputAmbient = ambient; } void LightEnvironmentClass::Add_Light(const LightClass & light) { // Jani: Don't accept lights that are almost black Vector3 diff; light.Get_Diffuse(&diff); if (diff[0]<0.05f && diff[1]<0.05f && diff[2]<0.05f) { return; } /* ** Compute the equivalent directional + ambient light */ InputLightStruct new_light; new_light.Init(light, ObjectCenter); // If we have the fill light set, we also want to the diffuse light to be modified by the intensity of the light source if(FillIntensity) new_light.Diffuse *= light.Get_Intensity(); /* ** Add in the ambient component */ OutputAmbient += new_light.Ambient; /* ** If not rejected, add the directional component to the active lights */ if (new_light.DiffuseRejected == false || new_light.m_point) { // Insert the light into the sorted list of InputLights if it's contribution is greater than the any of the current number of lights for (int light_index=0; light_index < LightCount; light_index++) { if (new_light.Contribution() > InputLights[light_index].Contribution()) { // Move back the lights in the InputLights Array to make space for the new light. // The last light might be discarded if it moves off the array as it is the weakest light in the list. for (int i = LightCount; i > light_index; --i) { if (i < MAX_LIGHTS) { InputLights[i] = InputLights[i - 1]; } } // Add the new light into the InputLights List where it belongs InputLights[light_index] = new_light; // Increment the light count if we have not reach the maximum lights limit yet LightCount = min(LightCount + 1, (int)MAX_LIGHTS); // Since we have inserted a new light, we are done for this function return; } } // If the light was not inserted but there are still spots empty in the InputLights list, insert the lights at the end of the list if (LightCount < MAX_LIGHTS) { InputLights[LightCount] = new_light; ++LightCount; } } } void LightEnvironmentClass::Pre_Render_Update(const Matrix3D & camera_tm) { /* ** Calculate and set up the fill light for the object */ Calculate_Fill_Light(); /* ** Transform each light into camera space ** and add up the ambient effect of each light */ for (int light_index=0; light_index 360.0f) { temp.X -= 360.0f; } temp.Z *= FillIntensity; // fraction of the intensity HSV_To_RGB(FillLight.Diffuse, temp); // Zero out the fill ambient FillLight.Ambient.Set(0.0f, 0.0f, 0.0f); // now we set the fill light direction to be opposite the average light FillLight.Direction = average_light.Direction * (-1.0f); FillLight.DiffuseRejected = false; // Add the fill light into the InputLights list Add_Fill_Light(); }