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 0 : void calccolor()
70 : {
71 0 : if(flags&DynLight_Flash || peak <= 0)
72 : {
73 0 : curcolor = color;
74 : }
75 : else
76 : {
77 0 : int peaking = expire - lastmillis - fade;
78 0 : if(peaking <= 0)
79 : {
80 0 : curcolor = color;
81 : }
82 : else
83 : {
84 0 : curcolor.lerp(initcolor, color, 1.0f - static_cast<float>(peaking)/peak);
85 : }
86 : }
87 0 : float intensity = 1.0f;
88 0 : if(fade > 0)
89 : {
90 0 : int fading = expire - lastmillis;
91 0 : if(fading < fade)
92 : {
93 0 : intensity = static_cast<float>(fading)/fade;
94 : }
95 : }
96 0 : curcolor.mul(intensity);
97 0 : }
98 :
99 : /**
100 : * @brief gets information about this dynlight
101 : *
102 : * @param n the nth closest dynamic light
103 : * @param o a reference to set as the location of the specified dynlight
104 : * @param radius a reference to set as the radius of the specified dynlight
105 : * @param color a reference to set as the color of the specifeid dynlight
106 : * @param spot a reference to the spotlight information of the dynlight
107 : * @param dir a reference to set as the direction the dynlight is pointing
108 : * @param flags a reference to the flag bitmap for the dynlight
109 : */
110 0 : void dynlightinfo(vec &origin, float &radius, vec &color, vec &direction, int &spotlight, int &flagmask) const
111 : {
112 0 : origin = o;
113 0 : radius = curradius;
114 0 : color = curcolor;
115 0 : spotlight = spot;
116 0 : direction = dir;
117 0 : flagmask = flags & 0xFF;
118 0 : }
119 :
120 : private:
121 : float radius, initradius;
122 : vec color, initcolor, curcolor;
123 : int fade, peak, flags;
124 : vec dir;
125 : int spot;
126 : };
127 :
128 : std::vector<dynlight> dynlights;
129 : std::vector<const dynlight *> closedynlights;
130 :
131 : //cleans up dynlights, deletes dynlights contents once none have expire field
132 0 : void cleardynlights()
133 : {
134 0 : int faded = -1;
135 0 : for(size_t i = 0; i < dynlights.size(); i++)
136 : {
137 0 : if(lastmillis<dynlights[i].expire)
138 : {
139 0 : faded = i;
140 0 : break;
141 : }
142 : }
143 0 : if(faded<0) //if any light has lastmillis > expire field
144 : {
145 0 : dynlights.clear();
146 : }
147 0 : else if(faded>0)
148 : {
149 0 : dynlights.erase(dynlights.begin(), dynlights.begin() + faded);
150 : }
151 0 : }
152 : }
153 : //externally relevant functionality
154 :
155 : //adds a dynamic light object to the dynlights vector with the attributes indicated (radius, color, fade, peak, flags, etc..)
156 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)
157 : {
158 0 : if(!usedynlights)
159 : {
160 0 : return;
161 : }
162 0 : if(o.dist(camera1->o) > dynlightdist || radius <= 0)
163 : {
164 0 : return;
165 : }
166 0 : int insert = 0,
167 0 : expire = fade + peak + lastmillis;
168 0 : for(int i = dynlights.size(); --i >=0;) //note reverse iteration
169 : {
170 0 : if(expire>=dynlights[i].expire)
171 : {
172 0 : insert = i+1;
173 0 : break;
174 : }
175 : }
176 0 : dynlight d(o, expire, owner, radius, initradius, color, initcolor, fade, peak, flags, dir, spot);
177 0 : dynlights.insert(dynlights.begin() + insert, d);
178 : }
179 :
180 0 : void removetrackeddynlights(const physent *owner)
181 : {
182 0 : for(int i = dynlights.size(); --i >=0;) //note reverse iteration
183 : {
184 0 : if(owner ? dynlights[i].owner == owner : dynlights[i].owner != nullptr)
185 : {
186 0 : dynlights.erase(dynlights.begin() + i);
187 : }
188 : }
189 0 : }
190 :
191 : //finds which dynamic lights are near enough and are visible to the player
192 : //returns the number of lights (and sets `closedynlights` vector contents to the appropriate nearby light ents)
193 0 : size_t finddynlights()
194 : {
195 0 : closedynlights.clear();
196 0 : if(!usedynlights)
197 : {
198 0 : return 0;
199 : }
200 0 : physent e;
201 0 : e.type = physent::PhysEnt_Camera;
202 0 : for(dynlight &d : dynlights)
203 : {
204 0 : if(d.curradius <= 0)
205 : {
206 0 : continue;
207 : }
208 0 : d.dist = camera1->o.dist(d.o) - d.curradius;
209 0 : if(d.dist > dynlightdist || view.isfoggedsphere(d.curradius, d.o))
210 : {
211 0 : continue;
212 : }
213 0 : e.o = d.o;
214 0 : e.radius = e.xradius = e.yradius = e.eyeheight = e.aboveeye = d.curradius;
215 0 : if(!collide(&e, nullptr, vec(0, 0, 0), 0))
216 : {
217 0 : continue;
218 : }
219 0 : int insert = 0;
220 0 : for(int i = closedynlights.size(); --i >=0;) //note reverse iteration
221 : {
222 0 : if(d.dist >= closedynlights[i]->dist)
223 : {
224 0 : insert = i+1;
225 0 : break;
226 : }
227 : }
228 0 : closedynlights.insert(closedynlights.begin() + insert, &d);
229 : }
230 0 : return closedynlights.size();
231 : }
232 :
233 0 : bool getdynlight(size_t n, vec &o, float &radius, vec &color, vec &dir, int &spot, int &flags)
234 : {
235 0 : if(!(closedynlights.size() > n))
236 : {
237 0 : return false;
238 : }
239 0 : const dynlight &d = *closedynlights[n];
240 0 : d.dynlightinfo(o, radius, color, dir, spot, flags);
241 0 : return true;
242 : }
243 :
244 0 : void updatedynlights()
245 : {
246 0 : cleardynlights();
247 0 : for(dynlight &d : dynlights)
248 : {
249 0 : d.calcradius();
250 0 : d.calccolor();
251 : }
252 0 : }
|