Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
lightenvironment.cpp
Go to the documentation of this file.
1/*
2** Command & Conquer Generals Zero Hour(tm)
3** Copyright 2025 Electronic Arts Inc.
4**
5** This program is free software: you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation, either version 3 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/***********************************************************************************************
20 *** 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 ***
21 ***********************************************************************************************
22 * *
23 * Project Name : WWPhys *
24 * *
25 * $Archive:: /Commando/Code/ww3d2/lightenvironment.cpp $*
26 * *
27 * Original Author:: Greg Hjelstrom *
28 * *
29 * $Author:: Greg_h $*
30 * *
31 * $Modtime:: 2/01/01 5:40p $*
32 * *
33 * $Revision:: 3 $*
34 * *
35 *---------------------------------------------------------------------------------------------*
36 * Functions: *
37 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
38
39
40#include "lightenvironment.h"
41#include "matrix3d.h"
42#include "camera.h"
43#include "light.h"
44#include "colorspace.h"
45
46/*
47** Constants
48*/
49const float DIFFUSE_TO_AMBIENT_FRACTION = 1.0f;
50
51
52/*
53** Static variables
54*/
55static _LightingLODCutoff = 0.5f;
56static _LightingLODCutoff2 = 0.5f * 0.5f;
57
58
59/************************************************************************************************
60**
61** LightEnvironmentClass::InputLightStruct Implementation
62**
63************************************************************************************************/
64
66(
67 const LightClass & light,
68 const Vector3 & object_center
69)
70{
71 m_point = false;
72 switch(light.Get_Type())
73 {
76 Init_From_Point_Or_Spot_Light(light,object_center);
77 break;
79 Init_From_Directional_Light(light,object_center);
80 break;
81 };
82}
83
85(
86 const LightClass & light,
87 const Vector3 & object_center
88)
89{
90 /*
91 ** Compute the direction vector and the distance to the light
92 */
93 Direction = light.Get_Position() - object_center;
94 float dist = Direction.Length();
95 if (dist > 0.0f) {
96 Direction /= dist;
97 }
98
99 /*
100 ** Compute the attenuation factor
101 */
102 float atten = 1.0f;
103 double atten_start,atten_end;
104 light.Get_Far_Attenuation_Range(atten_start,atten_end);
105
107
108 if (WWMath::Fabs(atten_end - atten_start) < WWMATH_EPSILON) {
109
110 /*
111 ** Start and end are equal, attenuation is a "step" function
112 */
113 if (dist > atten_start) {
114 atten = 0.0f;
115 }
116
117 } else {
118
119 /*
120 ** Compute the attenuation
121 */
122 atten = 1.0f - (dist - atten_start) / (atten_end - atten_start);
123 atten = WWMath::Clamp(atten,0.0f,1.0f);
124 }
125 }
126
127
128 if (light.Get_Type() == LightClass::SPOT) {
129
130 Vector3 spot_dir;
131 light.Get_Spot_Direction(spot_dir);
132 Matrix3D::Rotate_Vector(light.Get_Transform(),spot_dir,&spot_dir);
133
134 float spot_angle_cos = light.Get_Spot_Angle_Cos();
135 atten *= (Vector3::Dot_Product(-spot_dir,Direction) - spot_angle_cos) / (1.0f - spot_angle_cos);
136 atten = WWMath::Clamp(atten,0.0f,1.0f);
137 }
138
139 /*
140 ** Compute the ambient and diffuse values. Rejecting the diffuse
141 ** component and folding it into the ambient component if it is below
142 ** the LOD cutoff
143 */
144 light.Get_Ambient(&Ambient);
145 light.Get_Diffuse(&Diffuse);
146 Ambient *= light.Get_Intensity(); //(gth) CNC3 obey the intensity parameter
147 Diffuse *= light.Get_Intensity();
148
149 m_point = (light.Get_Type() == LightClass::POINT);
150 m_center = light.Get_Position();
151 m_innerRadius = atten_start;
152 m_outerRadius = atten_end;
155
156 if (Diffuse.Length2() > _LightingLODCutoff2) {
157
158 DiffuseRejected = false;
159 Ambient *= atten;
160 Diffuse *= atten;
161
162 } else {
163
164 DiffuseRejected = true;
165 Ambient *= atten;
167 Diffuse.Set(0,0,0);
168
169 }
170}
171
172
174(
175 const LightClass & light,
176 const Vector3 & object_center
177)
178{
180
181 DiffuseRejected = false;
182 light.Get_Ambient(&Ambient);
183 light.Get_Diffuse(&Diffuse);
184}
185
186
188{
189 return Diffuse.Length2();
190}
191
192
193/************************************************************************************************
194**
195** LightEnvironmentClass::OutputLightStruct Implementation
196**
197************************************************************************************************/
198
200(
201 const InputLightStruct & input,
202 const Matrix3D & camera_tm
203)
204{
205 Diffuse = input.Diffuse;
207
208 // Guard against a direction that is invalid
209 if(Direction.Length2() == 0.0f) {
210 Direction.X = 1.0f;
211 }
212}
213
214
215
216/************************************************************************************************
217**
218** LightEnvironmentClass Implementation
219**
220************************************************************************************************/
221
223 LightCount(0),
224 ObjectCenter(0,0,0),
225 OutputAmbient(0,0,0),
226 FillLight(),
227 FillIntensity(0.0f)
228{
229}
230
231
235
236
237void LightEnvironmentClass::Reset(const Vector3 & object_center,const Vector3 & ambient)
238{
239 LightCount = 0;
240 ObjectCenter = object_center;
241 OutputAmbient = ambient;
242}
243
244
246{
247 // Jani: Don't accept lights that are almost black
248 Vector3 diff;
249 light.Get_Diffuse(&diff);
250 if (diff[0]<0.05f && diff[1]<0.05f && diff[2]<0.05f) {
251 return;
252 }
253
254 /*
255 ** Compute the equivalent directional + ambient light
256 */
257
258 InputLightStruct new_light;
259 new_light.Init(light, ObjectCenter);
260
261 // If we have the fill light set, we also want to the diffuse light to be modified by the intensity of the light source
262 if(FillIntensity) new_light.Diffuse *= light.Get_Intensity();
263
264 /*
265 ** Add in the ambient component
266 */
267 OutputAmbient += new_light.Ambient;
268
269 /*
270 ** If not rejected, add the directional component to the active lights
271 */
272 if (new_light.DiffuseRejected == false || new_light.m_point) {
273
274 // Insert the light into the sorted list of InputLights if it's contribution is greater than the any of the current number of lights
275 for (int light_index=0; light_index < LightCount; light_index++) {
276 if (new_light.Contribution() > InputLights[light_index].Contribution()) {
277
278 // Move back the lights in the InputLights Array to make space for the new light.
279 // The last light might be discarded if it moves off the array as it is the weakest light in the list.
280 for (int i = LightCount; i > light_index; --i) {
281 if (i < MAX_LIGHTS) {
282 InputLights[i] = InputLights[i - 1];
283 }
284 }
285
286 // Add the new light into the InputLights List where it belongs
287 InputLights[light_index] = new_light;
288
289 // Increment the light count if we have not reach the maximum lights limit yet
290 LightCount = min(LightCount + 1, (int)MAX_LIGHTS);
291
292 // Since we have inserted a new light, we are done for this function
293 return;
294 }
295 }
296
297 // 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
298 if (LightCount < MAX_LIGHTS) {
299 InputLights[LightCount] = new_light;
300 ++LightCount;
301 }
302 }
303}
304
306{
307 /*
308 ** Calculate and set up the fill light for the object
309 */
311
312 /*
313 ** Transform each light into camera space
314 ** and add up the ambient effect of each light
315 */
316 for (int light_index=0; light_index<LightCount; light_index++) {
317 OutputLights[light_index].Init(InputLights[light_index],camera_tm);
318 }
319 // Clamp ambient.
323}
324
326{
327 _LightingLODCutoff = inten;
328 _LightingLODCutoff2 = _LightingLODCutoff * _LightingLODCutoff;
329}
330
332{
333 return _LightingLODCutoff;
334}
335
336/************************************************************************************************
337**
338** LightEnvironmentClass::Add_Fill_Light Implementation
339** The fill light is inserted in the InputLights list as ont of the lights, and if the
340** list is already full, it preempts the last and weakest light in that list.
341**
342************************************************************************************************/
344{
345 // Don't add black (or almost black) lights!
346 if (FillLight.Diffuse[0]<0.05f && FillLight.Diffuse[1]<0.05f && FillLight.Diffuse[2]<0.05f) {
347 OutputAmbient += FillLight.Ambient;
348 return;
349 }
350
351 // Get the 1st empty light slot or the very last slot regardless of whether the last slot is empty or not
352 int slot = 0;
353 if (LightCount == MAX_LIGHTS) {
354 slot = MAX_LIGHTS - 1;
355 } else {
356 slot = LightCount;
357 ++LightCount;
358 }
359
360 /*
361 ** Add in the ambient component
362 */
363 OutputAmbient += FillLight.Ambient;
364
365 /*
366 ** Insert the fill light into the calculated slot of the InputLights
367 */
368 InputLights[slot] = FillLight;
369}
370
371/************************************************************************************************
372**
373** LightEnvironmentClass::Calculate_Fill_Light Implementation
374** The fill light takes up to the top 3 lights in the InputList and averages them into 1 light source.
375** The averaged light source is then flipped in direction and location as well as in HUE of the color.
376** This final light is used to support the top 3 lights by providing a calulated fill to augment the lights.
377**
378************************************************************************************************/
380{
381 // Early exit if we have no lights at all or if the fill light intensity is zero
382 if (LightCount == 0 || FillIntensity == 0.0f) return;
383
384 // Initialize the averaged light to the primary light source (light with the most contribution)
385 float primary_contribution = InputLights[0].Contribution();
386 InputLightStruct average_light = InputLights[0];
387
388 // Loop through the remaining lights on the list (up to 2) and add their contributions to the averaged light
389 int num_lights = min(LightCount, MAX_LIGHTS - 1);
390 for (int i = 1; i < num_lights; ++i) {
391
392 // The ratio is the percentage of the remaining light's contribution compared to the primary light source
393 float ratio = InputLights[i].Contribution() / primary_contribution;
394
395 average_light.Direction += (InputLights[i].Direction * ratio);
396 average_light.Ambient += (InputLights[i].Ambient * ratio);
397 average_light.Diffuse += (InputLights[i].Diffuse * ratio);
398 }
399
400 // Normalize the averaged light direction
401 average_light.Direction.Normalize();
402
403 // Now we have the averaged light, we should derive the fill light
404 // Convert from RGB to HSV to get the reserve hue and with the value modified by the fill intensity
405 Vector3 temp;
406 RGB_To_HSV(temp, average_light.Diffuse);
407 temp.X += 180.0f; // Get the opposite hue from the averaged light
408 if(temp.X > 360.0f) {
409 temp.X -= 360.0f;
410 }
411 temp.Z *= FillIntensity; // fraction of the intensity
412 HSV_To_RGB(FillLight.Diffuse, temp);
413
414 // Zero out the fill ambient
415 FillLight.Ambient.Set(0.0f, 0.0f, 0.0f);
416
417 // now we set the fill light direction to be opposite the average light
418 FillLight.Direction = average_light.Direction * (-1.0f);
419 FillLight.DiffuseRejected = false;
420
421 // Add the fill light into the InputLights list
423}
424
425
#define min(x, y)
Definition BaseType.h:101
#define WWMATH_EPSILON
Definition wwmath.h:54
float Get_Intensity(void) const
Definition light.h:108
void Get_Far_Attenuation_Range(double &fStart, double &fEnd) const
Definition light.h:120
void Get_Spot_Direction(Vector3 &dir) const
Definition light.h:142
void Get_Ambient(Vector3 *set_c) const
Definition light.h:111
float Get_Spot_Angle_Cos(void) const
Definition light.h:140
@ POINT
Definition light.h:63
@ DIRECTIONAL
Definition light.h:64
@ FAR_ATTENUATION
Definition light.h:71
LightType Get_Type()
Definition light.h:105
int Get_Flag(FlagsType flag) const
Definition light.h:130
void Get_Diffuse(Vector3 *set_c) const
Definition light.h:114
InputLightStruct InputLights[MAX_LIGHTS]
void Pre_Render_Update(const Matrix3D &camera_tm)
static float Get_Lighting_LOD_Cutoff(void)
static void Set_Lighting_LOD_Cutoff(float inten)
OutputLightStruct OutputLights[MAX_LIGHTS]
void Reset(const Vector3 &object_center, const Vector3 &scene_ambient)
void Add_Light(const LightClass &light)
InputLightStruct FillLight
Vector3 Rotate_Vector(const Vector3 &vect) const
Definition matrix3d.cpp:300
WWINLINE Vector3 Get_Z_Vector() const
Definition matrix3d.h:308
Vector3 Inverse_Rotate_Vector(const Vector3 &vect) const
Definition matrix3d.cpp:322
Vector3 Get_Position(void) const
Definition rendobj.cpp:508
const Matrix3D & Get_Transform(void) const
Definition rendobj.h:617
static WWINLINE float Dot_Product(const Vector3 &a, const Vector3 &b)
Definition vector3.h:293
float X
Definition vector3.h:90
float Z
Definition vector3.h:92
void Normalize(void)
Definition vector3.h:417
static float Clamp(float val, float min=0.0f, float max=1.0f)
Definition wwmath.h:208
static WWINLINE float Fabs(float val)
Definition wwmath.h:113
void HSV_To_RGB(Vector3 &rgb, const Vector3 &hsv)
Definition colorspace.h:87
void RGB_To_HSV(Vector3 &hsv, const Vector3 &rgb)
Definition colorspace.h:57
const float DIFFUSE_TO_AMBIENT_FRACTION
void Init_From_Point_Or_Spot_Light(const LightClass &light, const Vector3 &object_center)
void Init(const LightClass &light, const Vector3 &object_center)
void Init_From_Directional_Light(const LightClass &light, const Vector3 &object_center)
void Init(const InputLightStruct &input, const Matrix3D &camera_tm)