1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include "myGL.h"
4 #include "LightHandler.h"
5 #include "Game/GlobalUnsynced.h"
6 #include "Rendering/Shaders/Shader.h"
7 #include "Sim/Misc/GlobalSynced.h"
8 #include "Sim/Misc/LosHandler.h"
9 
10 static const float4 ZeroVector4 = float4(0.0f, 0.0f, 0.0f, 0.0f);
11 
Init(unsigned int cfgBaseLight,unsigned int cfgMaxLights)12 void GL::LightHandler::Init(unsigned int cfgBaseLight, unsigned int cfgMaxLights) {
13 	glGetIntegerv(GL_MAX_LIGHTS, reinterpret_cast<int*>(&maxLights));
14 
15 	baseLight = cfgBaseLight;
16 	maxLights -= baseLight;
17 	maxLights = std::min(maxLights, cfgMaxLights);
18 
19 	for (unsigned int i = 0; i < maxLights; i++) {
20 		const unsigned int lightID = GL_LIGHT0 + baseLight + i;
21 
22 		glEnable(lightID);
23 		glLightfv(lightID, GL_POSITION, &ZeroVector4.x);
24 		glLightfv(lightID, GL_AMBIENT,  &ZeroVector4.x);
25 		glLightfv(lightID, GL_DIFFUSE,  &ZeroVector4.x);
26 		glLightfv(lightID, GL_SPECULAR, &ZeroVector4.x);
27 		glLightfv(lightID, GL_SPOT_DIRECTION, &ZeroVector4.x);
28 		glLightf(lightID, GL_SPOT_CUTOFF, 180.0f);
29 		glLightf(lightID, GL_CONSTANT_ATTENUATION,  1.0f);
30 		glLightf(lightID, GL_LINEAR_ATTENUATION,    0.0f);
31 		glLightf(lightID, GL_QUADRATIC_ATTENUATION, 0.0f);
32 		glDisable(lightID);
33 
34 		// reserve free light ID's
35 		lightIDs.push_back(lightID);
36 	}
37 }
38 
39 
AddLight(const GL::Light & light)40 unsigned int GL::LightHandler::AddLight(const GL::Light& light) {
41 	if (light.GetTTL() == 0 || light.GetRadius() <= 0.0f) { return -1U; }
42 	if (light.GetIntensityWeight().SqLength() <= 0.01f) { return -1U; }
43 
44 	if (lights.size() >= maxLights || lightIDs.empty()) {
45 		unsigned int minPriorityValue = light.GetPriority();
46 		unsigned int minPriorityHandle = -1U;
47 
48 		for (std::map<unsigned int, GL::Light>::const_iterator it = lights.begin(); it != lights.end(); ++it) {
49 			const GL::Light& lgt = it->second;
50 
51 			if (lgt.GetPriority() < minPriorityValue) {
52 				minPriorityValue = lgt.GetPriority();
53 				minPriorityHandle = it->first;
54 			}
55 		}
56 
57 		if (minPriorityHandle != -1U) {
58 			lightIntensityWeight -= lights[minPriorityHandle].GetIntensityWeight();
59 			lightIDs.push_back(lights[minPriorityHandle].GetID());
60 			lights.erase(minPriorityHandle);
61 		} else {
62 			// no available light to replace
63 			return -1U;
64 		}
65 	}
66 
67 	lights[lightHandle] = light;
68 	lights[lightHandle].SetID(lightIDs.front());
69 	lights[lightHandle].SetRelativeTime(0);
70 	lights[lightHandle].SetAbsoluteTime(gs->frameNum);
71 
72 	lightIntensityWeight += light.GetIntensityWeight();
73 	lightIDs.pop_front();
74 
75 	return (lightHandle++);
76 }
77 
GetLight(unsigned int _lightHandle)78 GL::Light* GL::LightHandler::GetLight(unsigned int _lightHandle) {
79 	const std::map<unsigned int, GL::Light>::iterator it = lights.find(_lightHandle);
80 
81 	if (it != lights.end()) {
82 		return &(it->second);
83 	}
84 
85 	return NULL;
86 }
87 
88 
Update(Shader::IProgramObject * shader)89 void GL::LightHandler::Update(Shader::IProgramObject* shader) {
90 	if (lights.size() != numLights) {
91 		numLights = lights.size();
92 
93 		// update the active light-count (note: unused, number of lights
94 		// to iterate over needs to be known at shader compilation-time)
95 		// shader->SetUniform1i(uniformIndex, numLights);
96 	}
97 
98 	if (numLights == 0) {
99 		return;
100 	}
101 
102 	for (std::map<unsigned int, GL::Light>::iterator it = lights.begin(); it != lights.end(); ) {
103 		GL::Light& light = it->second;
104 
105 		if (light.GetAbsoluteTime() != gs->frameNum) {
106 			light.SetRelativeTime(light.GetRelativeTime() + 1);
107 			light.SetAbsoluteTime(gs->frameNum);
108 			light.DecayColors();
109 			light.ClampColors();
110 		}
111 
112 		const unsigned int lightID = light.GetID();
113 		const unsigned int lightHndle = it->first;
114 
115 		const float4  weightedAmbientCol  = (light.GetAmbientColor()  * light.GetIntensityWeight().x) / lightIntensityWeight.x;
116 		const float4  weightedDiffuseCol  = (light.GetDiffuseColor()  * light.GetIntensityWeight().y) / lightIntensityWeight.y;
117 		const float4  weightedSpecularCol = (light.GetSpecularColor() * light.GetIntensityWeight().z) / lightIntensityWeight.z;
118 		const float3* lightTrackPos       = light.GetTrackPosition();
119 		const float3* lightTrackDir       = light.GetTrackDirection();
120 		const float4& lightPos            = (lightTrackPos != NULL)? float4(*lightTrackPos, 1.0f): light.GetPosition();
121 		const float3& lightDir            = (lightTrackDir != NULL)? float3(*lightTrackDir      ): light.GetDirection();
122 		const bool    lightVisible        = (gu->spectatingFullView || light.GetIgnoreLOS() || losHandler->InLos(lightPos, gu->myAllyTeam));
123 
124 		++it;
125 
126 		if (light.GetRelativeTime() > light.GetTTL()) {
127 			lightIntensityWeight -= light.GetIntensityWeight();
128 			lightIDs.push_back(lightID);
129 			lights.erase(lightHndle);
130 
131 			// kill the contribution from this light
132 			glEnable(lightID);
133 			glLightfv(lightID, GL_AMBIENT,  &ZeroVector4.x);
134 			glLightfv(lightID, GL_DIFFUSE,  &ZeroVector4.x);
135 			glLightfv(lightID, GL_SPECULAR, &ZeroVector4.x);
136 			glDisable(lightID);
137 		} else {
138 			// communicate properties via the FFP to save uniforms
139 			// note: we want MV to be identity here
140 			glEnable(lightID);
141 			glLightfv(lightID, GL_POSITION, &lightPos.x);
142 
143 			if (lightVisible) {
144 				glLightfv(lightID, GL_AMBIENT,  &weightedAmbientCol.x);
145 				glLightfv(lightID, GL_DIFFUSE,  &weightedDiffuseCol.x);
146 				glLightfv(lightID, GL_SPECULAR, &weightedSpecularCol.x);
147 			} else {
148 				// zero contribution from this light if not in LOS
149 				// (whether or not camera can see it is irrelevant
150 				// since the light always takes up a slot anyway)
151 				glLightfv(lightID, GL_AMBIENT,  &ZeroVector4.x);
152 				glLightfv(lightID, GL_DIFFUSE,  &ZeroVector4.x);
153 				glLightfv(lightID, GL_SPECULAR, &ZeroVector4.x);
154 			}
155 
156 			glLightfv(lightID, GL_SPOT_DIRECTION, &lightDir.x);
157 			glLightf(lightID, GL_SPOT_CUTOFF, light.GetFOV());
158 			glLightf(lightID, GL_CONSTANT_ATTENUATION, light.GetRadius()); //!
159 			#if (OGL_SPEC_ATTENUATION == 1)
160 			glLightf(lightID, GL_CONSTANT_ATTENUATION,  light.GetAttenuation().x);
161 			glLightf(lightID, GL_LINEAR_ATTENUATION,    light.GetAttenuation().y);
162 			glLightf(lightID, GL_QUADRATIC_ATTENUATION, light.GetAttenuation().z);
163 			#endif
164 			glDisable(lightID);
165 		}
166 	}
167 }
168