Line data Source code
1 : /**
2 : * @brief Handling of changing lights
3 : *
4 : * while the lighting system is dynamic, the changing state of the light entities
5 : * for the renderer to handle must be updated with new values that reflect the type
6 : * of light to be drawn
7 : *
8 : * this includes pulsating lights (which change in radius dynamically)
9 : * and multicolored lights (which change hue dynamically)
10 : */
11 : #include "../libprimis-headers/cube.h"
12 : #include "../../shared/geomexts.h"
13 :
14 : #include "dynlight.h"
15 : #include "physics.h"
16 :
17 : #include "interface/control.h"
18 :
19 : #include "render/rendergl.h"
20 : #include "render/renderva.h"
21 :
22 : //internally relevant functionality
23 : namespace
24 : {
25 : VARNP(dynlights, usedynlights, 0, 1, 1); //toggle using dynamic lights
26 : VARP(dynlightdist, 0, 1024, 10000); //distance after which dynamic lights are not rendered (1024 = 128m)
27 :
28 : class dynlight final
29 : {
30 : public:
31 : vec o;
32 : float curradius, dist;
33 : int expire;
34 : const physent *owner;
35 :
36 0 : dynlight(vec o, int expire, physent *owner, float radius, float initradius, vec color, vec initcolor, int fade, int peak, int flags, vec dir, int spot) :
37 0 : o(o), expire(expire), owner(owner), radius(radius), initradius(initradius), color(color), initcolor(initcolor), fade(fade), peak(peak), flags(flags), dir(dir), spot(spot)
38 : {
39 0 : }
40 :
41 0 : void calcradius()
42 : {
43 0 : if(fade + peak > 0)
44 : {
45 0 : int remaining = expire - lastmillis;
46 0 : if(flags&DynLight_Expand)
47 : {
48 0 : curradius = initradius + (radius - initradius) * (1.0f - remaining/static_cast<float>(fade + peak));
49 : }
50 0 : else if(!(flags&DynLight_Flash) && remaining > fade)
51 : {
52 0 : curradius = initradius + (radius - initradius) * (1.0f - static_cast<float>(remaining - fade)/peak);
53 : }
54 0 : else if(flags&DynLight_Shrink)
55 : {
56 0 : curradius = (radius*remaining)/fade;
57 : }
58 : else
59 : {
60 0 : curradius = radius;
61 : }
62 : }
63 : else
64 : {
65 0 : curradius = radius;
66 : }
67 0 : }
68 :
69 : /**
70 : * @brief Updates the color of this dynamic light.
71 : *
72 : * Sets the curcolor and intensity fields depending on the current timestamp
73 : * and the object's flags.
74 : */
75 0 : void calccolor()
76 : {
77 0 : if(flags&DynLight_Flash || peak <= 0)
78 : {
79 0 : curcolor = color;
80 : }
81 : else
82 : {
83 0 : int peaking = expire - lastmillis - fade;
84 0 : if(peaking <= 0)
85 : {
86 0 : curcolor = color;
87 : }
88 : else
89 : {
90 0 : curcolor.lerp(initcolor, color, 1.0f - static_cast<float>(peaking)/peak);
91 : }
92 : }
93 0 : float intensity = 1.0f;
94 0 : if(fade > 0)
95 : {
96 0 : int fading = expire - lastmillis;
97 0 : if(fading < fade)
98 : {
99 0 : intensity = static_cast<float>(fading)/fade;
100 : }
101 : }
102 0 : curcolor.mul(intensity);
103 0 : }
104 :
105 : /**
106 : * @brief gets information about this dynlight
107 : *
108 : * @param n the nth closest dynamic light
109 : * @param o a reference to set as the location of the specified dynlight
110 : * @param radius a reference to set as the radius of the specified dynlight
111 : * @param color a reference to set as the color of the specifeid dynlight
112 : * @param spot a reference to the spotlight information of the dynlight
113 : * @param dir a reference to set as the direction the dynlight is pointing
114 : * @param flags a reference to the flag bitmap for the dynlight
115 : */
116 0 : void dynlightinfo(vec &origin, float &radius, vec &color, vec &direction, int &spotlight, int &flagmask) const
117 : {
118 0 : origin = o;
119 0 : radius = curradius;
120 0 : color = curcolor;
121 0 : spotlight = spot;
122 0 : direction = dir;
123 0 : flagmask = flags & 0xFF;
124 0 : }
125 :
126 : private:
127 : float radius, initradius;
128 : vec color, initcolor, curcolor;
129 : int fade, peak, flags;
130 : vec dir;
131 : int spot;
132 : };
133 :
134 : std::vector<dynlight> dynlights;
135 : std::vector<const dynlight *> closedynlights;
136 :
137 : //cleans up dynlights, deletes dynlights contents once none have expire field
138 0 : void cleardynlights()
139 : {
140 0 : int faded = -1;
141 0 : for(size_t i = 0; i < dynlights.size(); i++)
142 : {
143 0 : if(lastmillis<dynlights[i].expire)
144 : {
145 0 : faded = i;
146 0 : break;
147 : }
148 : }
149 0 : if(faded<0) //if any light has lastmillis > expire field
150 : {
151 0 : dynlights.clear();
152 : }
153 0 : else if(faded>0)
154 : {
155 0 : dynlights.erase(dynlights.begin(), dynlights.begin() + faded);
156 : }
157 0 : }
158 : }
159 : //externally relevant functionality
160 :
161 : //adds a dynamic light object to the dynlights vector with the attributes indicated (radius, color, fade, peak, flags, etc..)
162 0 : void adddynlight(const vec &o, float radius, const vec &color, int fade, int peak, int flags, float initradius, const vec &initcolor, physent *owner, const vec &dir, int spot)
163 : {
164 0 : if(!usedynlights)
165 : {
166 0 : return;
167 : }
168 0 : if(o.dist(camera1->o) > dynlightdist || radius <= 0)
169 : {
170 0 : return;
171 : }
172 0 : int insert = 0,
173 0 : expire = fade + peak + lastmillis;
174 0 : for(int i = dynlights.size(); --i >=0;) //note reverse iteration
175 : {
176 0 : if(expire>=dynlights[i].expire)
177 : {
178 0 : insert = i+1;
179 0 : break;
180 : }
181 : }
182 0 : dynlight d(o, expire, owner, radius, initradius, color, initcolor, fade, peak, flags, dir, spot);
183 0 : dynlights.insert(dynlights.begin() + insert, d);
184 : }
185 :
186 0 : void removetrackeddynlights(const physent *owner)
187 : {
188 0 : for(int i = dynlights.size(); --i >=0;) //note reverse iteration
189 : {
190 0 : if(owner ? dynlights[i].owner == owner : dynlights[i].owner != nullptr)
191 : {
192 0 : dynlights.erase(dynlights.begin() + i);
193 : }
194 : }
195 0 : }
196 :
197 : //finds which dynamic lights are near enough and are visible to the player
198 : //returns the number of lights (and sets `closedynlights` vector contents to the appropriate nearby light ents)
199 0 : size_t finddynlights()
200 : {
201 0 : closedynlights.clear();
202 0 : if(!usedynlights)
203 : {
204 0 : return 0;
205 : }
206 0 : physent e;
207 0 : e.type = physent::PhysEnt_Camera;
208 0 : for(dynlight &d : dynlights)
209 : {
210 0 : if(d.curradius <= 0)
211 : {
212 0 : continue;
213 : }
214 0 : d.dist = camera1->o.dist(d.o) - d.curradius;
215 0 : if(d.dist > dynlightdist || view.isfoggedsphere(d.curradius, d.o))
216 : {
217 0 : continue;
218 : }
219 0 : e.o = d.o;
220 0 : e.radius = e.xradius = e.yradius = e.eyeheight = e.aboveeye = d.curradius;
221 0 : if(!collide(&e, nullptr, vec(0, 0, 0), 0))
222 : {
223 0 : continue;
224 : }
225 0 : int insert = 0;
226 0 : for(int i = closedynlights.size(); --i >=0;) //note reverse iteration
227 : {
228 0 : if(d.dist >= closedynlights[i]->dist)
229 : {
230 0 : insert = i+1;
231 0 : break;
232 : }
233 : }
234 0 : closedynlights.insert(closedynlights.begin() + insert, &d);
235 : }
236 0 : return closedynlights.size();
237 : }
238 :
239 0 : bool getdynlight(size_t n, vec &o, float &radius, vec &color, vec &dir, int &spot, int &flags)
240 : {
241 0 : if(!(closedynlights.size() > n))
242 : {
243 0 : return false;
244 : }
245 0 : const dynlight &d = *closedynlights[n];
246 0 : d.dynlightinfo(o, radius, color, dir, spot, flags);
247 0 : return true;
248 : }
249 :
250 0 : void updatedynlights()
251 : {
252 0 : cleardynlights();
253 0 : for(dynlight &d : dynlights)
254 : {
255 0 : d.calcradius();
256 0 : d.calccolor();
257 : }
258 0 : }
|