Line data Source code
1 : /**
2 : * @file renderlights.cpp
3 : * @brief render lights to deferred buffers
4 : *
5 : * light entities and sunlight in the game is rendered to deferred buffers
6 : * "g-buffers" which are used to compose a scene
7 : * lights are cached using a shadow map to allow rendering less than once per
8 : * frame, improving performance and light count allowed
9 : */
10 : #include "../libprimis-headers/cube.h"
11 : #include "../../shared/geomexts.h"
12 : #include "../../shared/glemu.h"
13 : #include "../../shared/glexts.h"
14 :
15 : #include "aa.h"
16 : #include "ao.h"
17 : #include "csm.h"
18 : #include "hdr.h"
19 : #include "lightsphere.h"
20 : #include "octarender.h"
21 : #include "postfx.h"
22 : #include "radiancehints.h"
23 : #include "rendergl.h"
24 : #include "renderlights.h"
25 : #include "rendermodel.h"
26 : #include "rendersky.h"
27 : #include "rendertimers.h"
28 : #include "renderva.h"
29 : #include "renderwindow.h"
30 : #include "shader.h"
31 : #include "shaderparam.h"
32 : #include "stain.h"
33 : #include "texture.h"
34 :
35 : #include "interface/control.h"
36 : #include "interface/console.h"
37 :
38 : #include "world/dynlight.h"
39 : #include "world/light.h"
40 : #include "world/material.h"
41 : #include "world/octaedit.h"
42 : #include "world/octaworld.h"
43 : #include "world/world.h"
44 :
45 : int vieww = -1,
46 : viewh = -1;
47 :
48 : int gw = -1,
49 : gh = -1;
50 :
51 : GBuffer gbuf;
52 :
53 : int hdrclear = 0;
54 :
55 : int spotlights = 0,
56 : volumetriclights = 0,
57 : nospeclights = 0;
58 : std::vector<vec2> msaapositions;
59 :
60 : //`g`-buffer `scale`
61 0 : static VARFP(gscale, 25, 100, 100, gbuf.cleanupgbuffer()); //size of g buffer, approximately correlates to g buffer linear dimensions
62 0 : static VARFP(gscalecubic, 0, 0, 1, gbuf.cleanupgbuffer()); //g-buffer scale cubic: use cubic interpolation for g buffer upscaling to screen output
63 0 : static VARFP(gscalenearest, 0, 0, 1, gbuf.cleanupgbuffer()); //g buffer nearest neighbor interpolation
64 :
65 : matrix4 worldmatrix, screenmatrix;
66 :
67 : static std::array<Shader *, 2> bilateralshader = { nullptr, nullptr };
68 :
69 0 : static Shader *loadbilateralshader(int pass)
70 : {
71 0 : if(!aobilateral)
72 : {
73 0 : return nullshader;
74 : }
75 0 : std::string opts;
76 0 : bool linear = aoreducedepth && (aoreduce || aoreducedepth > 1),
77 0 : upscale = aoreduce && aobilateralupscale,
78 0 : reduce = aoreduce && (upscale || (!linear && !aopackdepth));
79 0 : if(reduce)
80 : {
81 0 : opts.push_back('r');
82 0 : opts.push_back('0' + aoreduce);
83 : }
84 0 : if(upscale)
85 : {
86 0 : opts.push_back('u');
87 : }
88 0 : else if(linear)
89 : {
90 0 : opts.push_back('l');
91 :
92 : }
93 0 : if(aopackdepth)
94 : {
95 0 : opts.push_back('p');
96 : }
97 :
98 0 : DEF_FORMAT_STRING(name, "bilateral%c%s%d", 'x' + pass, opts.c_str(), aobilateral);
99 0 : return generateshader(name, "bilateralshader \"%s\" %d %d", opts.c_str(), aobilateral, reduce ? aoreduce : 0);
100 0 : }
101 :
102 0 : void loadbilateralshaders()
103 : {
104 0 : for(int k = 0; k < 2; ++k)
105 : {
106 0 : bilateralshader[k] = loadbilateralshader(k);
107 : }
108 0 : }
109 :
110 0 : void clearbilateralshaders()
111 : {
112 0 : bilateralshader.fill(nullptr);
113 0 : }
114 :
115 0 : static void setbilateralparams(int radius, float depth)
116 : {
117 0 : float sigma = blursigma*2*radius;
118 0 : LOCALPARAMF(bilateralparams, 1.0f/(M_LN2*2*sigma*sigma), 1.0f/(M_LN2*depth*depth));
119 0 : }
120 :
121 0 : void setbilateralshader(int radius, int pass, float depth)
122 : {
123 0 : bilateralshader[pass]->set();
124 0 : setbilateralparams(radius, depth);
125 0 : }
126 :
127 : //debug commands
128 : //for individual debug commands, see respective functions lower in the file
129 : VAR(debugfullscreen, 0, 0, 1); //used in header
130 :
131 0 : void GBuffer::cleanupscale()
132 : {
133 0 : for(GLuint &i : scalefbo)
134 : {
135 0 : if(i)
136 : {
137 0 : glDeleteFramebuffers(1, &i);
138 0 : i = 0;
139 : }
140 : }
141 0 : for(GLuint &i : scaletex)
142 : {
143 0 : if(i)
144 : {
145 0 : glDeleteTextures(1, &i);
146 0 : i = 0;
147 : }
148 : }
149 0 : scalew = scaleh = -1;
150 0 : }
151 :
152 0 : void GBuffer::setupscale(int sw, int sh, int w, int h)
153 : {
154 0 : scalew = w;
155 0 : scaleh = h;
156 :
157 0 : for(int i = 0; i < (gscalecubic ? 2 : 1); ++i)
158 : {
159 0 : if(!scaletex[i])
160 : {
161 0 : glGenTextures(1, &scaletex[i]);
162 : }
163 0 : if(!scalefbo[i])
164 : {
165 0 : glGenFramebuffers(1, &scalefbo[i]);
166 : }
167 0 : glBindFramebuffer(GL_FRAMEBUFFER, scalefbo[i]);
168 :
169 0 : createtexture(scaletex[i], sw, i ? h : sh, nullptr, 3, gscalecubic || !gscalenearest ? 1 : 0, GL_RGB, GL_TEXTURE_2D);
170 :
171 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, scaletex[i], 0);
172 0 : if(!i)
173 : {
174 0 : bindgdepth();
175 : }
176 0 : if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
177 : {
178 0 : fatal("failed allocating scale buffer!");
179 : }
180 : }
181 :
182 0 : glBindFramebuffer(GL_FRAMEBUFFER, 0);
183 :
184 0 : if(gscalecubic)
185 : {
186 0 : useshaderbyname("scalecubicx");
187 0 : useshaderbyname("scalecubicy");
188 : }
189 0 : }
190 :
191 0 : GLuint GBuffer::shouldscale() const
192 : {
193 0 : return scalefbo[0];
194 : }
195 :
196 0 : void GBuffer::doscale(GLuint outfbo) const
197 : {
198 0 : if(!scaletex[0])
199 : {
200 0 : return;
201 : }
202 0 : timer *scaletimer = begintimer("scaling");
203 0 : if(gscalecubic)
204 : {
205 0 : glBindFramebuffer(GL_FRAMEBUFFER, scalefbo[1]);
206 0 : glViewport(0, 0, gw, hudh());
207 0 : glBindTexture(GL_TEXTURE_2D, scaletex[0]);
208 0 : SETSHADER(scalecubicy);
209 0 : screenquad(1, 1);
210 0 : glBindFramebuffer(GL_FRAMEBUFFER, outfbo);
211 0 : glViewport(0, 0, hudw(), hudh());
212 0 : glBindTexture(GL_TEXTURE_2D, scaletex[1]);
213 0 : SETSHADER(scalecubicx);
214 0 : screenquad(1, 1);
215 : }
216 : else
217 : {
218 0 : glBindFramebuffer(GL_FRAMEBUFFER, outfbo);
219 0 : glViewport(0, 0, hudw(), hudh());
220 0 : glBindTexture(GL_TEXTURE_2D, scaletex[0]);
221 0 : SETSHADER(scalelinear);
222 0 : screenquad(1, 1);
223 : }
224 :
225 0 : endtimer(scaletimer);
226 : }
227 :
228 0 : VARFP(glineardepth, 0, 0, 3, initwarning("g-buffer setup", Init_Load, Change_Shaders)); // g-buffer linear depth buffer
229 : VAR(gdepthformat, 1, 0, 0); // g-buffer depth buffer format
230 0 : VARF(gstencil, 0, 0, 1, initwarning("g-buffer setup", Init_Load, Change_Shaders)); // g-buffer stenciling
231 0 : VARF(gdepthstencil, 0, 2, 2, initwarning("g-buffer setup", Init_Load, Change_Shaders)); // g-buffer depth buffer stenciling
232 : VAR(ghasstencil, 1, 0, 0); // g buffer has stencil
233 0 : static VARFP(msaa, 0, 0, 16, initwarning("MSAA setup", Init_Load, Change_Shaders)); // multi-sample antialiasing
234 0 : static VARF(msaadepthstencil, 0, 2, 2, initwarning("MSAA setup", Init_Load, Change_Shaders)); // multi-sample antialiasing depth buffer stenciling
235 0 : static VARF(msaastencil, 0, 0, 1, initwarning("MSAA setup", Init_Load, Change_Shaders)); // multi-sample antialiasing stenciling
236 0 : VARF(msaaedgedetect, 0, 1, 1, gbuf.cleanupgbuffer()); // multi-sample antialiasing edge detection
237 0 : VARFP(msaalineardepth, -1, -1, 3, initwarning("MSAA setup", Init_Load, Change_Shaders)); // multi-sample antialiasing linear depth
238 0 : VARFP(msaatonemap, 0, 0, 1, gbuf.cleanupgbuffer()); // multi-sample antialiasing tone mapping
239 : static VAR(msaamaxsamples, 1, 0, 0); // multi-sample antialiasing maximum samples
240 : static VAR(msaamaxdepthtexsamples, 1, 0, 0); // multi-sample antialiasing maximum depth buffer texture sample count
241 : static VAR(msaamaxcolortexsamples, 1, 0, 0); // multi-sample antialiasing maximum color buffer texture sample count
242 : static VAR(msaaminsamples, 1, 0, 0); // multi-sample antialiasing minimum sample count
243 : VAR(msaasamples, 1, 0, 0); // multi-sample antialiasing sampling
244 : VAR(msaalight, 1, 0, 0); // multi-sample antialias lights
245 0 : static VARF(msaapreserve, -1, 0, 1, initwarning("MSAA setup", Init_Load, Change_Shaders)); // preserve multi-sample antialiasing
246 :
247 0 : static void checkmsaasamples()
248 : {
249 : GLuint tex;
250 0 : glGenTextures(1, &tex);
251 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, tex);
252 :
253 : GLint samples;
254 0 : glTexImage2DMultisample_(GL_TEXTURE_2D_MULTISAMPLE, msaaminsamples, GL_RGBA8, 1, 1, GL_TRUE);
255 0 : glGetTexLevelParameteriv(GL_TEXTURE_2D_MULTISAMPLE, 0, GL_TEXTURE_SAMPLES, &samples);
256 0 : msaasamples = samples;
257 :
258 0 : glDeleteTextures(1, &tex);
259 0 : }
260 :
261 0 : void initgbuffer()
262 : {
263 0 : msaamaxsamples = msaamaxdepthtexsamples = msaamaxcolortexsamples = msaaminsamples = msaasamples = msaalight = 0;
264 0 : msaapositions.clear();
265 :
266 : GLint val;
267 0 : glGetIntegerv(GL_MAX_SAMPLES, &val);
268 0 : msaamaxsamples = val;
269 0 : glGetIntegerv(GL_MAX_DEPTH_TEXTURE_SAMPLES, &val);
270 0 : msaamaxdepthtexsamples = val;
271 0 : glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &val);
272 0 : msaamaxcolortexsamples = val;
273 :
274 0 : int maxsamples = std::min(msaamaxsamples, msaamaxcolortexsamples),
275 0 : reqsamples = std::min(msaa, maxsamples);
276 0 : if(reqsamples >= 2)
277 : {
278 0 : msaaminsamples = 2;
279 0 : while(msaaminsamples*2 <= reqsamples)
280 : {
281 0 : msaaminsamples *= 2;
282 : }
283 : }
284 :
285 0 : int lineardepth = glineardepth;
286 0 : if(msaaminsamples)
287 : {
288 0 : if(msaamaxdepthtexsamples < msaaminsamples)
289 : {
290 0 : if(msaalineardepth > 0)
291 : {
292 0 : lineardepth = msaalineardepth;
293 : }
294 0 : else if(!lineardepth)
295 : {
296 0 : lineardepth = 1;
297 : }
298 : }
299 0 : else if(msaalineardepth >= 0)
300 : {
301 0 : lineardepth = msaalineardepth;
302 : }
303 : }
304 0 : gdepthformat = lineardepth;
305 0 : if(msaaminsamples)
306 : {
307 0 : ghasstencil = (msaadepthstencil > 1 || (msaadepthstencil && gdepthformat)) ? 2 : (msaastencil ? 1 : 0);
308 0 : checkmsaasamples();
309 0 : if(msaapreserve >= 0)
310 : {
311 0 : msaalight = 3;
312 : }
313 : }
314 : else
315 : {
316 0 : ghasstencil = (gdepthstencil > 1 || (gdepthstencil && gdepthformat)) ? 2 : (gstencil ? 1 : 0);
317 : }
318 0 : initao();
319 0 : }
320 :
321 0 : static VARF(forcepacknorm, 0, 0, 1, initwarning("g-buffer setup", Init_Load, Change_Shaders));
322 :
323 : static bool useavatarmask();
324 :
325 1 : bool usepacknorm()
326 : {
327 1 : return forcepacknorm || msaasamples || !useavatarmask();
328 : }
329 :
330 0 : void maskgbuffer(const char *mask)
331 : {
332 : std::array<GLenum, 4> drawbufs;
333 0 : int numbufs = 0;
334 0 : while(*mask)
335 : {
336 0 : switch(*mask++)
337 : {
338 0 : case 'c':
339 : {
340 0 : drawbufs[numbufs++] = GL_COLOR_ATTACHMENT0;
341 0 : break;
342 : }
343 0 : case 'n':
344 : {
345 0 : drawbufs[numbufs++] = GL_COLOR_ATTACHMENT1;
346 0 : break;
347 : }
348 0 : case 'd':
349 : {
350 0 : if(gdepthformat)
351 : {
352 0 : drawbufs[numbufs++] = GL_COLOR_ATTACHMENT3;
353 : }
354 0 : break;
355 : }
356 0 : case 'g':
357 : {
358 0 : drawbufs[numbufs++] = GL_COLOR_ATTACHMENT2;
359 0 : break;
360 : }
361 : }
362 : }
363 0 : glDrawBuffers(numbufs, drawbufs.data());
364 0 : }
365 :
366 0 : void GBuffer::cleanupmsbuffer()
367 : {
368 0 : if(msfbo) { glDeleteFramebuffers(1, &msfbo); msfbo = 0; }
369 0 : if(msdepthtex) { glDeleteTextures(1, &msdepthtex); msdepthtex = 0; }
370 0 : if(mscolortex) { glDeleteTextures(1, &mscolortex); mscolortex = 0; }
371 0 : if(msnormaltex) { glDeleteTextures(1, &msnormaltex); msnormaltex = 0; }
372 0 : if(msglowtex) { glDeleteTextures(1, &msglowtex); msglowtex = 0; }
373 0 : if(msstencilrb) { glDeleteRenderbuffers(1, &msstencilrb); msstencilrb = 0; }
374 0 : if(msdepthrb) { glDeleteRenderbuffers(1, &msdepthrb); msdepthrb = 0; }
375 0 : if(mshdrfbo) { glDeleteFramebuffers(1, &mshdrfbo); mshdrfbo = 0; }
376 0 : if(mshdrtex) { glDeleteTextures(1, &mshdrtex); mshdrtex = 0; }
377 0 : if(msrefractfbo) { glDeleteFramebuffers(1, &msrefractfbo); msrefractfbo = 0; }
378 0 : if(msrefracttex) { glDeleteTextures(1, &msrefracttex); msrefracttex = 0; }
379 0 : }
380 :
381 0 : void GBuffer::bindmsdepth() const
382 : {
383 0 : if(gdepthformat)
384 : {
385 0 : glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, msdepthrb);
386 0 : if(ghasstencil > 1)
387 : {
388 0 : glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, msdepthrb);
389 : }
390 0 : else if(msaalight && ghasstencil)
391 : {
392 0 : glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, msstencilrb);
393 : }
394 : }
395 : else
396 : {
397 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, msdepthtex, 0);
398 0 : if(ghasstencil > 1)
399 : {
400 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, msdepthtex, 0);
401 : }
402 0 : else if(msaalight && ghasstencil)
403 : {
404 0 : glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, msstencilrb);
405 : }
406 : }
407 0 : }
408 :
409 0 : void GBuffer::setupmsbuffer(int w, int h)
410 : {
411 0 : if(!msfbo)
412 : {
413 0 : glGenFramebuffers(1, &msfbo);
414 : }
415 :
416 0 : glBindFramebuffer(GL_FRAMEBUFFER, msfbo);
417 :
418 0 : stencilformat = ghasstencil > 1 ? GL_DEPTH24_STENCIL8 : (ghasstencil ? GL_STENCIL_INDEX8 : 0);
419 :
420 0 : if(gdepthformat)
421 : {
422 0 : if(!msdepthrb)
423 : {
424 0 : glGenRenderbuffers(1, &msdepthrb);
425 : }
426 0 : glBindRenderbuffer(GL_RENDERBUFFER, msdepthrb);
427 0 : glRenderbufferStorageMultisample_(GL_RENDERBUFFER, msaasamples, ghasstencil > 1 ? stencilformat : GL_DEPTH_COMPONENT24, w, h);
428 0 : glBindRenderbuffer(GL_RENDERBUFFER, 0);
429 : }
430 0 : if(msaalight && ghasstencil == 1)
431 : {
432 0 : if(!msstencilrb)
433 : {
434 0 : glGenRenderbuffers(1, &msstencilrb);
435 : }
436 0 : glBindRenderbuffer(GL_RENDERBUFFER, msstencilrb);
437 0 : glRenderbufferStorageMultisample_(GL_RENDERBUFFER, msaasamples, GL_STENCIL_INDEX8, w, h);
438 0 : glBindRenderbuffer(GL_RENDERBUFFER, 0);
439 : }
440 :
441 0 : if(!msdepthtex)
442 : {
443 0 : glGenTextures(1, &msdepthtex);
444 : }
445 0 : if(!mscolortex)
446 : {
447 0 : glGenTextures(1, &mscolortex);
448 : }
449 0 : if(!msnormaltex)
450 : {
451 0 : glGenTextures(1, &msnormaltex);
452 : }
453 :
454 0 : maskgbuffer(msaalight ? "cndg" : "cnd");
455 :
456 : static const GLenum depthformats[] = { GL_RGBA8, GL_R16F, GL_R32F };
457 0 : GLenum depthformat = gdepthformat ? depthformats[gdepthformat-1] : (ghasstencil > 1 ? stencilformat : GL_DEPTH_COMPONENT24);
458 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
459 0 : glTexImage2DMultisample_(GL_TEXTURE_2D_MULTISAMPLE, msaasamples, depthformat, w, h, GL_TRUE);
460 :
461 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mscolortex);
462 0 : glTexImage2DMultisample_(GL_TEXTURE_2D_MULTISAMPLE, msaasamples, GL_RGBA8, w, h, GL_TRUE);
463 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msnormaltex);
464 0 : glTexImage2DMultisample_(GL_TEXTURE_2D_MULTISAMPLE, msaasamples, GL_RGBA8, w, h, GL_TRUE);
465 0 : if(msaalight)
466 : {
467 0 : if(!msglowtex)
468 : {
469 0 : glGenTextures(1, &msglowtex);
470 : }
471 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msglowtex);
472 0 : glTexImage2DMultisample_(GL_TEXTURE_2D_MULTISAMPLE, msaasamples, hdrformat, w, h, GL_TRUE);
473 : }
474 :
475 0 : bindmsdepth();
476 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, mscolortex, 0);
477 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D_MULTISAMPLE, msnormaltex, 0);
478 0 : if(msaalight)
479 : {
480 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D_MULTISAMPLE, msglowtex, 0);
481 : }
482 0 : if(gdepthformat)
483 : {
484 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D_MULTISAMPLE, msdepthtex, 0);
485 : }
486 :
487 0 : if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
488 : {
489 0 : if(msaalight)
490 : {
491 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msglowtex);
492 0 : glTexImage2DMultisample_(GL_TEXTURE_2D_MULTISAMPLE, msaasamples, GL_RGBA8, w, h, GL_TRUE);
493 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D_MULTISAMPLE, msglowtex, 0);
494 0 : if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
495 : {
496 0 : fatal("failed allocating MSAA g-buffer!");
497 : }
498 : }
499 : else
500 : {
501 0 : fatal("failed allocating MSAA g-buffer!");
502 : }
503 : }
504 :
505 0 : glClearColor(0, 0, 0, 0);
506 0 : glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | (ghasstencil ? GL_STENCIL_BUFFER_BIT : 0));
507 :
508 0 : msaapositions.clear();
509 0 : for(int i = 0; i < msaasamples; ++i)
510 : {
511 : GLfloat vals[2];
512 0 : glGetMultisamplefv(GL_SAMPLE_POSITION, i, vals);
513 0 : msaapositions.emplace_back(vec2(vals[0], vals[1]));
514 : }
515 :
516 0 : if(msaalight)
517 : {
518 0 : if(!mshdrtex)
519 : {
520 0 : glGenTextures(1, &mshdrtex);
521 : }
522 0 : if(!mshdrfbo)
523 : {
524 0 : glGenFramebuffers(1, &mshdrfbo);
525 : }
526 0 : glBindFramebuffer(GL_FRAMEBUFFER, mshdrfbo);
527 0 : bindmsdepth();
528 0 : hdrformat = 0;
529 0 : for(int prec = hdrprec; prec >= 0; prec--)
530 : {
531 0 : GLenum format = gethdrformat(prec);
532 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mshdrtex);
533 0 : glGetError();
534 0 : glTexImage2DMultisample_(GL_TEXTURE_2D_MULTISAMPLE, msaasamples, format, w, h, GL_TRUE);
535 0 : if(glGetError() == GL_NO_ERROR)
536 : {
537 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, mshdrtex, 0);
538 0 : if(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
539 : {
540 0 : hdrformat = format;
541 0 : break;
542 : }
543 : }
544 : }
545 :
546 0 : if(!hdrformat || glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
547 : {
548 0 : fatal("failed allocating MSAA HDR buffer!");
549 : }
550 0 : if(!msrefracttex)
551 : {
552 0 : glGenTextures(1, &msrefracttex);
553 : }
554 0 : if(!msrefractfbo)
555 : {
556 0 : glGenFramebuffers(1, &msrefractfbo);
557 : }
558 0 : glBindFramebuffer(GL_FRAMEBUFFER, msrefractfbo);
559 :
560 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msrefracttex);
561 0 : glTexImage2DMultisample_(GL_TEXTURE_2D_MULTISAMPLE, msaasamples, GL_RGB, w, h, GL_TRUE);
562 :
563 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, msrefracttex, 0);
564 0 : bindmsdepth();
565 :
566 0 : if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
567 : {
568 0 : fatal("failed allocating MSAA refraction buffer!");
569 : }
570 : }
571 :
572 0 : glBindFramebuffer(GL_FRAMEBUFFER, 0);
573 :
574 0 : useshaderbyname("msaaedgedetect");
575 0 : useshaderbyname("msaaresolve");
576 0 : useshaderbyname("msaareducew");
577 0 : useshaderbyname("msaareduce");
578 0 : if(!msaalight)
579 : {
580 0 : useshaderbyname("msaaresolvedepth");
581 : }
582 0 : if(msaalight > 1 && msaatonemap)
583 : {
584 0 : useshaderbyname("msaatonemap");
585 0 : if(msaalight > 2)
586 : {
587 0 : useshaderbyname("msaatonemapsample");
588 : }
589 : }
590 0 : }
591 :
592 0 : void GBuffer::bindgdepth() const
593 : {
594 0 : if(gdepthformat || msaalight)
595 : {
596 0 : glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, gdepthrb);
597 0 : if(ghasstencil > 1)
598 : {
599 0 : glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gdepthrb);
600 : }
601 0 : else if(!msaalight || ghasstencil)
602 : {
603 0 : glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gstencilrb);
604 : }
605 : }
606 : else
607 : {
608 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_RECTANGLE, gdepthtex, 0);
609 0 : if(ghasstencil > 1)
610 : {
611 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_RECTANGLE, gdepthtex, 0);
612 : }
613 0 : else if(ghasstencil)
614 : {
615 0 : glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gstencilrb);
616 : }
617 : }
618 0 : }
619 :
620 0 : void GBuffer::setupgbuffer()
621 : {
622 : //start with screen resolution
623 0 : int sw = renderw(),
624 0 : sh = renderh();
625 : //scale sw and sh if gscale (g-buffer scale) is not 100%
626 0 : if(gscale != 100)
627 : {
628 0 : sw = std::max((renderw()*gscale + 99)/100, 1);
629 0 : sh = std::max((renderh()*gscale + 99)/100, 1);
630 : }
631 :
632 0 : if(gw == sw && gh == sh && ((sw >= hudw() && sh >= hudh() && !scalefbo[0]) || (scalew == hudw() && scaleh == hudh())))
633 : {
634 0 : return;
635 : }
636 : //clean up various buffers & info with them
637 0 : cleanupscale();
638 0 : cleanupbloom();
639 0 : cleanupao();
640 0 : cleanupvolumetric();
641 0 : cleanupaa();
642 0 : cleanuppostfx();
643 :
644 0 : gw = sw;
645 0 : gh = sh;
646 :
647 0 : hdrformat = gethdrformat(hdrprec);
648 0 : stencilformat = ghasstencil > 1 ? GL_DEPTH24_STENCIL8 : (ghasstencil ? GL_STENCIL_INDEX8 : 0);
649 :
650 0 : if(msaasamples)
651 : {
652 0 : setupmsbuffer(gw, gh);
653 : }
654 0 : hdrfloat = floatformat(hdrformat);
655 0 : hdrclear = 3;
656 0 : gdepthinit = false;
657 :
658 0 : if(gdepthformat || msaalight)
659 : {
660 0 : if(!gdepthrb)
661 : {
662 0 : glGenRenderbuffers(1, &gdepthrb);
663 : }
664 0 : glBindRenderbuffer(GL_RENDERBUFFER, gdepthrb);
665 0 : glRenderbufferStorage(GL_RENDERBUFFER, ghasstencil > 1 ? stencilformat : GL_DEPTH_COMPONENT24, gw, gh);
666 0 : glBindRenderbuffer(GL_RENDERBUFFER, 0);
667 : }
668 0 : if(!msaalight && ghasstencil == 1)
669 : {
670 0 : if(!gstencilrb)
671 : {
672 0 : glGenRenderbuffers(1, &gstencilrb);
673 : }
674 0 : glBindRenderbuffer(GL_RENDERBUFFER, gstencilrb);
675 0 : glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, gw, gh);
676 0 : glBindRenderbuffer(GL_RENDERBUFFER, 0);
677 : }
678 :
679 0 : if(!msaalight)
680 : {
681 0 : if(!gdepthtex)
682 : {
683 0 : glGenTextures(1, &gdepthtex);
684 : }
685 0 : if(!gcolortex)
686 : {
687 0 : glGenTextures(1, &gcolortex);
688 : }
689 0 : if(!gnormaltex)
690 : {
691 0 : glGenTextures(1, &gnormaltex);
692 : }
693 0 : if(!gglowtex)
694 : {
695 0 : glGenTextures(1, &gglowtex);
696 : }
697 0 : if(!gfbo)
698 : {
699 0 : glGenFramebuffers(1, &gfbo);
700 : }
701 :
702 0 : glBindFramebuffer(GL_FRAMEBUFFER, gfbo);
703 :
704 0 : maskgbuffer("cndg");
705 :
706 : static const GLenum depthformats[] = { GL_RGBA8, GL_R16F, GL_R32F };
707 0 : GLenum depthformat = gdepthformat ? depthformats[gdepthformat-1] : (ghasstencil > 1 ? stencilformat : GL_DEPTH_COMPONENT24);
708 0 : createtexture(gdepthtex, gw, gh, nullptr, 3, 0, depthformat, GL_TEXTURE_RECTANGLE);
709 :
710 0 : createtexture(gcolortex, gw, gh, nullptr, 3, 0, GL_RGBA8, GL_TEXTURE_RECTANGLE);
711 0 : createtexture(gnormaltex, gw, gh, nullptr, 3, 0, GL_RGBA8, GL_TEXTURE_RECTANGLE);
712 0 : createtexture(gglowtex, gw, gh, nullptr, 3, 0, hdrformat, GL_TEXTURE_RECTANGLE);
713 :
714 0 : bindgdepth();
715 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, gcolortex, 0);
716 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_RECTANGLE, gnormaltex, 0);
717 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_RECTANGLE, gglowtex, 0);
718 0 : if(gdepthformat)
719 : {
720 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_RECTANGLE, gdepthtex, 0);
721 : }
722 0 : if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
723 : {
724 0 : createtexture(gglowtex, gw, gh, nullptr, 3, 0, GL_RGBA8, GL_TEXTURE_RECTANGLE);
725 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_RECTANGLE, gglowtex, 0);
726 0 : if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
727 : {
728 0 : fatal("failed allocating g-buffer!");
729 : }
730 : }
731 :
732 0 : glClearColor(0, 0, 0, 0);
733 0 : glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | (ghasstencil ? GL_STENCIL_BUFFER_BIT : 0));
734 : }
735 :
736 0 : if(!hdrtex)
737 : {
738 0 : glGenTextures(1, &hdrtex);
739 : }
740 0 : if(!hdrfbo)
741 : {
742 0 : glGenFramebuffers(1, &hdrfbo);
743 : }
744 :
745 0 : glBindFramebuffer(GL_FRAMEBUFFER, hdrfbo);
746 :
747 0 : createtexture(hdrtex, gw, gh, nullptr, 3, 1, hdrformat, GL_TEXTURE_RECTANGLE);
748 :
749 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, hdrtex, 0);
750 0 : bindgdepth();
751 :
752 0 : if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
753 : {
754 0 : fatal("failed allocating HDR buffer!");
755 : }
756 :
757 0 : if(!msaalight || (msaalight > 2 && msaatonemap && msaatonemapblit))
758 : {
759 0 : if(!refracttex)
760 : {
761 0 : glGenTextures(1, &refracttex);
762 : }
763 0 : if(!refractfbo)
764 : {
765 0 : glGenFramebuffers(1, &refractfbo);
766 : }
767 0 : glBindFramebuffer(GL_FRAMEBUFFER, refractfbo);
768 0 : createtexture(refracttex, gw, gh, nullptr, 3, 0, GL_RGB, GL_TEXTURE_RECTANGLE);
769 :
770 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, refracttex, 0);
771 0 : bindgdepth();
772 :
773 0 : if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
774 : {
775 0 : fatal("failed allocating refraction buffer!");
776 : }
777 : }
778 :
779 0 : glBindFramebuffer(GL_FRAMEBUFFER, 0);
780 :
781 0 : if(gw < hudw() || gh < hudh())
782 : {
783 0 : setupscale(gw, gh, hudw(), hudh());
784 : }
785 : }
786 :
787 0 : void GBuffer::cleanupgbuffer()
788 : {
789 0 : if(gfbo) { glDeleteFramebuffers(1, &gfbo); gfbo = 0; }
790 0 : if(gdepthtex) { glDeleteTextures(1, &gdepthtex); gdepthtex = 0; }
791 0 : if(gcolortex) { glDeleteTextures(1, &gcolortex); gcolortex = 0; }
792 0 : if(gnormaltex) { glDeleteTextures(1, &gnormaltex); gnormaltex = 0; }
793 0 : if(gglowtex) { glDeleteTextures(1, &gglowtex); gglowtex = 0; }
794 0 : if(gstencilrb) { glDeleteRenderbuffers(1, &gstencilrb); gstencilrb = 0; }
795 0 : if(gdepthrb) { glDeleteRenderbuffers(1, &gdepthrb); gdepthrb = 0; }
796 0 : if(hdrfbo) { glDeleteFramebuffers(1, &hdrfbo); hdrfbo = 0; }
797 0 : if(hdrtex) { glDeleteTextures(1, &hdrtex); hdrtex = 0; }
798 0 : if(refractfbo) { glDeleteFramebuffers(1, &refractfbo); refractfbo = 0; }
799 0 : if(refracttex) { glDeleteTextures(1, &refracttex); refracttex = 0; }
800 0 : gw = gh = -1;
801 0 : cleanupscale();
802 0 : cleanupmsbuffer();
803 0 : cleardeferredlightshaders();
804 0 : }
805 :
806 0 : void GBuffer::resolvemsaadepth(int w, int h) const
807 : {
808 0 : if(!msaasamples || msaalight)
809 : {
810 0 : return;
811 : }
812 :
813 0 : timer *resolvetimer = drawtex ? nullptr : begintimer("msaa depth resolve");
814 :
815 0 : if(gdepthformat)
816 : {
817 0 : glBindFramebuffer(GL_FRAMEBUFFER, gfbo);
818 0 : glViewport(0, 0, w, h);
819 0 : maskgbuffer("d");
820 :
821 0 : if(ghasstencil)
822 : {
823 0 : glStencilFunc(GL_ALWAYS, 0, ~0);
824 0 : glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
825 0 : glEnable(GL_STENCIL_TEST);
826 : }
827 0 : glDepthFunc(GL_ALWAYS);
828 0 : SETSHADER(msaaresolvedepth);
829 :
830 :
831 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
832 0 : screenquad();
833 0 : maskgbuffer("cnd");
834 0 : if(ghasstencil)
835 : {
836 0 : glDisable(GL_STENCIL_TEST);
837 : }
838 0 : glDepthFunc(GL_LESS);
839 : }
840 :
841 0 : endtimer(resolvetimer);
842 : }
843 :
844 0 : void GBuffer::resolvemsaacolor(int w, int h)
845 : {
846 0 : if(!msaalight)
847 : {
848 0 : return;
849 : }
850 0 : timer *resolvetimer = drawtex ? nullptr : begintimer("msaa resolve");
851 :
852 0 : glBindFramebuffer(GL_READ_FRAMEBUFFER, mshdrfbo);
853 0 : glBindFramebuffer(GL_DRAW_FRAMEBUFFER, hdrfbo);
854 0 : glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
855 :
856 0 : glBindFramebuffer(GL_FRAMEBUFFER, hdrfbo);
857 :
858 0 : endtimer(resolvetimer);
859 : }
860 :
861 : float ldrscale = 1.0f;
862 :
863 0 : float ldrscaleb()
864 : {
865 0 : return ldrscale/255;
866 : }
867 :
868 : static VAR(debugdepth, 0, 0, 1); //toggles showing depth buffer onscreen
869 :
870 0 : void GBuffer::viewdepth() const
871 : {
872 0 : int w = (debugfullscreen) ? hudw() : std::min(hudw(), hudh())/2, //if debugfullscreen, set to hudw/hudh size; if not, do small size
873 0 : h = (debugfullscreen) ? hudh() : (w*hudh())/hudw();
874 0 : SETSHADER(hudrect);
875 0 : gle::colorf(1, 1, 1);
876 0 : glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
877 0 : debugquad(0, 0, w, h, 0, 0, gw, gh);
878 0 : }
879 :
880 : static VAR(debugstencil, 0, 0, 0xFF);
881 :
882 0 : void viewstencil()
883 : {
884 0 : if(!ghasstencil || !hdrfbo)
885 : {
886 0 : return;
887 : }
888 0 : glBindFramebuffer(GL_FRAMEBUFFER, hdrfbo);
889 0 : glViewport(0, 0, gw, gh);
890 :
891 0 : glClearColor(0, 0, 0, 0);
892 0 : glClear(GL_COLOR_BUFFER_BIT);
893 :
894 0 : glStencilFunc(GL_NOTEQUAL, 0, debugstencil);
895 0 : glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
896 0 : glEnable(GL_STENCIL_TEST);
897 0 : SETSHADER(hudnotexture);
898 0 : gle::colorf(1, 1, 1);
899 0 : debugquad(0, 0, hudw(), hudh(), 0, 0, gw, gh);
900 0 : glDisable(GL_STENCIL_TEST);
901 :
902 0 : glBindFramebuffer(GL_FRAMEBUFFER, 0);
903 0 : glViewport(0, 0, hudw(), hudh());
904 :
905 0 : int w = debugfullscreen ? hudw() : std::min(hudw(), hudh())/2, //if debugfullscreen, set to hudw/hudh size; if not, do small size
906 0 : h = debugfullscreen ? hudh() : (w*hudh())/hudw();
907 0 : SETSHADER(hudrect);
908 0 : gle::colorf(1, 1, 1);
909 0 : glBindTexture(GL_TEXTURE_RECTANGLE, hdrtex);
910 0 : debugquad(0, 0, w, h, 0, 0, gw, gh);
911 : }
912 :
913 : static VAR(debugrefract, 0, 0, 1);
914 :
915 0 : void GBuffer::viewrefract()
916 : {
917 0 : int w = debugfullscreen ? hudw() : std::min(hudw(), hudh())/2, //if debugfullscreen, set to hudw/hudh size; if not, do small size
918 0 : h = debugfullscreen ? hudh() : (w*hudh())/hudw();
919 0 : SETSHADER(hudrect);
920 0 : gle::colorf(1, 1, 1);
921 0 : glBindTexture(GL_TEXTURE_RECTANGLE, refracttex);
922 0 : debugquad(0, 0, w, h, 0, 0, gw, gh);
923 0 : }
924 :
925 : PackNode shadowatlaspacker(0, 0, shadowatlassize, shadowatlassize);
926 :
927 : VAR(smminradius, 0, 16, 10000);
928 :
929 : class lightinfo final
930 : {
931 : public:
932 : int ent, shadowmap, flags;
933 : vec o, color;
934 : float radius, dist;
935 : vec dir, spotx, spoty;
936 : int spot;
937 : float sx1, sy1, sx2, sy2, sz1, sz2;
938 : occludequery *query;
939 :
940 : lightinfo() : query(nullptr)
941 : {
942 : }
943 :
944 0 : lightinfo(const vec &o, const vec &color, float radius, int flags = 0, const vec &dir = vec(0, 0, 0), int spot = 0)
945 0 : : ent(-1), shadowmap(-1), flags(flags),
946 0 : o(o), color(color), radius(radius), dist(camera1->o.dist(o)),
947 0 : dir(dir), spot(spot), query(nullptr)
948 : {
949 0 : if(spot > 0)
950 : {
951 0 : calcspot();
952 : }
953 0 : calcscissor();
954 0 : }
955 :
956 0 : lightinfo(int i, const extentity &e)
957 0 : : ent(i), shadowmap(-1), flags(e.attr5),
958 0 : o(e.o), color(vec(e.attr2, e.attr3, e.attr4).max(0)), radius(e.attr1), dist(camera1->o.dist(e.o)),
959 0 : dir(0, 0, 0), spot(0), query(nullptr)
960 : {
961 0 : if(e.attached && e.attached->type == EngineEnt_Spotlight)
962 : {
963 0 : dir = vec(e.attached->o).sub(e.o).normalize();
964 0 : spot = std::clamp(static_cast<int>(e.attached->attr1), 1, 89);
965 0 : calcspot();
966 : }
967 0 : calcscissor();
968 0 : }
969 :
970 0 : bool noshadow() const
971 : {
972 0 : return flags&LightEnt_NoShadow || radius <= smminradius;
973 : }
974 :
975 0 : bool nospec() const
976 : {
977 0 : return (flags&LightEnt_NoSpecular) != 0;
978 : }
979 :
980 0 : bool volumetric() const
981 : {
982 0 : return (flags&LightEnt_Volumetric) != 0;
983 : }
984 :
985 0 : void addscissor(float &dx1, float &dy1, float &dx2, float &dy2) const
986 : {
987 0 : dx1 = std::min(dx1, sx1);
988 0 : dy1 = std::min(dy1, sy1);
989 0 : dx2 = std::max(dx2, sx2);
990 0 : dy2 = std::max(dy2, sy2);
991 0 : }
992 :
993 0 : void addscissor(float &dx1, float &dy1, float &dx2, float &dy2, float &dz1, float &dz2) const
994 : {
995 0 : addscissor(dx1, dy1, dx2, dy2);
996 0 : dz1 = std::min(dz1, sz1);
997 0 : dz2 = std::max(dz2, sz2);
998 0 : }
999 :
1000 0 : bool validscissor() const
1001 : {
1002 0 : return sx1 < sx2 && sy1 < sy2 && sz1 < sz2;
1003 : }
1004 :
1005 0 : bool checkquery() const
1006 : {
1007 0 : return query && query->owner == this && occlusionengine.checkquery(query);
1008 : }
1009 :
1010 0 : void calcbb(vec &bbmin, vec &bbmax) const
1011 : {
1012 0 : if(spot > 0)
1013 : {
1014 0 : float spotscale = radius * tan360(spot);
1015 0 : vec up = vec(spotx).mul(spotscale).abs(),
1016 0 : right = vec(spoty).mul(spotscale).abs(),
1017 0 : center = vec(dir).mul(radius).add(o);
1018 0 : bbmin = bbmax = center;
1019 0 : bbmin.sub(up).sub(right);
1020 0 : bbmax.add(up).add(right);
1021 0 : bbmin.min(o);
1022 0 : bbmax.max(o);
1023 : }
1024 : else
1025 : {
1026 0 : bbmin = vec(o).sub(radius);
1027 0 : bbmax = vec(o).add(radius);
1028 : }
1029 0 : }
1030 :
1031 : private:
1032 0 : void calcspot()
1033 : {
1034 0 : quat orient(dir, vec(0, 0, dir.z < 0 ? -1 : 1));
1035 0 : spotx = orient.invertedrotate(vec(1, 0, 0));
1036 0 : spoty = orient.invertedrotate(vec(0, 1, 0));
1037 0 : }
1038 :
1039 0 : void calcscissor()
1040 : {
1041 0 : sx1 = sy1 = sz1 = -1;
1042 0 : sx2 = sy2 = sz2 = 1;
1043 0 : if(spot > 0)
1044 : {
1045 0 : calcspotscissor(o, radius, dir, spot, spotx, spoty, sx1, sy1, sx2, sy2, sz1, sz2);
1046 : }
1047 : else
1048 : {
1049 0 : calcspherescissor(o, radius, sx1, sy1, sx2, sy2, sz1, sz2);
1050 : }
1051 0 : }
1052 : };
1053 :
1054 : struct ShadowCacheKey final
1055 : {
1056 : vec o;
1057 : float radius;
1058 : vec dir;
1059 : int spot;
1060 :
1061 0 : bool operator==(const ShadowCacheKey &y) const
1062 : {
1063 0 : return o == y.o && radius == y.radius && dir == y.dir && spot == y.spot;
1064 : }
1065 :
1066 : ShadowCacheKey() {}
1067 0 : ShadowCacheKey(const lightinfo &l) : o(l.o), radius(l.radius), dir(l.dir), spot(l.spot) {}
1068 : };
1069 :
1070 : template <>
1071 : struct std::hash<ShadowCacheKey>
1072 : {
1073 0 : size_t operator()(const ShadowCacheKey &k) const
1074 : {
1075 : auto vechash = std::hash<vec>();
1076 0 : return vechash(k.o);
1077 : }
1078 : };
1079 :
1080 : struct shadowcacheval final
1081 : {
1082 : ushort x, y, size, sidemask;
1083 :
1084 :
1085 : static inline bool htcmp(const ShadowCacheKey &x, const ShadowCacheKey &y)
1086 : {
1087 : return x.o == y.o && x.radius == y.radius && x.dir == y.dir && x.spot == y.spot;
1088 : }
1089 :
1090 0 : shadowcacheval() {}
1091 0 : shadowcacheval(const ShadowMapInfo &sm) : x(sm.x), y(sm.y), size(sm.size), sidemask(sm.sidemask) {}
1092 : };
1093 :
1094 : class ShadowAtlas final
1095 : {
1096 : public:
1097 : GLuint fbo = 0;
1098 : std::unordered_map<ShadowCacheKey, shadowcacheval> cache;
1099 : bool full = false;
1100 :
1101 : void cleanup();
1102 : void view();
1103 : void setup();
1104 : void setcomparemode(); //will call one of setsm(non)comparemode()
1105 : void bind();
1106 :
1107 : private:
1108 : GLuint tex = 0;
1109 : GLenum target = GL_NONE;
1110 :
1111 : void setsmnoncomparemode();
1112 : void setsmcomparemode();
1113 : bool usesmcomparemode();
1114 :
1115 : };
1116 :
1117 0 : void ShadowAtlas::cleanup()
1118 : {
1119 0 : if(tex)
1120 : {
1121 0 : glDeleteTextures(1, &tex);
1122 0 : tex = 0;
1123 : }
1124 0 : if(fbo)
1125 : {
1126 0 : glDeleteFramebuffers(1, &fbo);
1127 0 : fbo = 0;
1128 : }
1129 0 : clearshadowcache();
1130 0 : }
1131 :
1132 0 : void ShadowAtlas::bind()
1133 : {
1134 0 : glBindTexture(target, tex);
1135 0 : }
1136 :
1137 : ShadowAtlas shadowatlas;
1138 :
1139 : //`s`hadow `m`ap vars
1140 : static FVAR(smpolyfactor, -1e3f, 1, 1e3f);
1141 : static FVAR(smpolyoffset, -1e3f, 0, 1e3f);
1142 : static FVAR(smbias, -1e6f, 0.01f, 1e6f);
1143 : static FVAR(smpolyfactor2, -1e3f, 1.5f, 1e3f);
1144 : static FVAR(smpolyoffset2, -1e3f, 0, 1e3f);
1145 : static FVAR(smbias2, -1e6f, 0.02f, 1e6f);
1146 : static FVAR(smprec, 1e-3f, 1, 1e3f);
1147 : static FVAR(smcubeprec, 1e-3f, 1, 1e3f);
1148 : static FVAR(smspotprec, 1e-3f, 1, 1e3f);
1149 :
1150 0 : VARFP(smsize, 10, 12, 14, shadowatlas.cleanup()); //size of shadow map: 2^size = x,y dimensions (1024x1024 at 10, 16384x16384 at 14)
1151 0 : VARFP(smdepthprec, 0, 0, 2, shadowatlas.cleanup()); //bit depth of sm depth map: 16bpp, 24bpp, or 32bpp respectively
1152 : VAR(smsidecull, 0, 1, 1); //`s`hadow `m`ap `side` `cull`: toggles culling lights outside the view frustum (outside the fov)
1153 : VAR(smviscull, 0, 1, 1); //`s`hadow `m`ap `vis`ibility `cull`ing: toggles visibility culling based of distance
1154 : VAR(smborder, 0, 3, 16); //`s`hadow `m`ap border
1155 : VAR(smborder2, 0, 4, 16); //smborder if smfilter > 2, for filter bleed reasons
1156 : VAR(smminsize, 1, 96, 1024); //min size for individual sm, not whole buffer
1157 : VAR(smmaxsize, 1, 384, 1024); //max size for individual sm, not whole buffer
1158 : //VAR(smmaxsize, 1, 4096, 4096);
1159 : VAR(smused, 1, 0, 0); //read only: shadow map area used
1160 : VAR(smquery, 0, 1, 1); // `s`hadow `m`ap `query1: whether to occlusion query lights
1161 0 : VARF(smcullside, 0, 1, 1, shadowatlas.cleanup());
1162 0 : VARF(smcache, 0, 1, 2, shadowatlas.cleanup());
1163 0 : VARFP(smfilter, 0, 2, 3, { cleardeferredlightshaders(); shadowatlas.cleanup(); cleanupvolumetric(); });
1164 0 : VARFP(smgather, 0, 0, 1, { cleardeferredlightshaders(); shadowatlas.cleanup(); cleanupvolumetric(); });
1165 : VAR(smnoshadow, 0, 0, 1);
1166 : VAR(smdynshadow, 0, 1, 1); //`s`hadow `m`ap `dyn`amic `shadow`
1167 :
1168 0 : void ShadowAtlas::setsmnoncomparemode() // use texture gather
1169 : {
1170 0 : glTexParameteri(target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
1171 0 : glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1172 0 : glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1173 0 : }
1174 :
1175 0 : void ShadowAtlas::setsmcomparemode() // use embedded shadow cmp
1176 : {
1177 0 : glTexParameteri(target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
1178 0 : glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1179 0 : glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1180 0 : }
1181 :
1182 0 : void ShadowAtlas::setcomparemode()
1183 : {
1184 0 : if(usesmcomparemode())
1185 : {
1186 0 : setsmcomparemode();
1187 : }
1188 : else
1189 : {
1190 0 : setsmnoncomparemode();
1191 : }
1192 0 : }
1193 :
1194 0 : static bool usegatherforsm()
1195 : {
1196 0 : return smfilter > 1 && smgather;
1197 : }
1198 :
1199 0 : bool ShadowAtlas::usesmcomparemode()
1200 : {
1201 0 : return !usegatherforsm() || (usetexgather > 1);
1202 : }
1203 :
1204 0 : void ShadowAtlas::view()
1205 : {
1206 0 : int w = std::min(hudw(), hudh())/2,
1207 0 : h = (w*hudh())/hudw(),
1208 0 : x = hudw()-w,
1209 0 : y = hudh()-h;
1210 0 : float tw = 1,
1211 0 : th = 1;
1212 0 : if(target == GL_TEXTURE_RECTANGLE)
1213 : {
1214 0 : vec2 sasize = shadowatlaspacker.dimensions();
1215 0 : tw = sasize.x;
1216 0 : th = sasize.y;
1217 0 : SETSHADER(hudrect);
1218 : }
1219 : else
1220 : {
1221 0 : hudshader->set();
1222 : }
1223 0 : gle::colorf(1, 1, 1);
1224 0 : glBindTexture(target, tex);
1225 0 : if(usesmcomparemode())
1226 : {
1227 0 : setsmnoncomparemode();
1228 : }
1229 0 : debugquad(x, y, w, h, 0, 0, tw, th);
1230 0 : if(usesmcomparemode())
1231 : {
1232 0 : setsmcomparemode();
1233 : }
1234 0 : }
1235 :
1236 : static VAR(debugshadowatlas, 0, 0, 1);
1237 :
1238 0 : void ShadowAtlas::setup()
1239 : {
1240 0 : int size = std::min((1<<smsize), hwtexsize);
1241 0 : shadowatlaspacker.resize(size, size);
1242 :
1243 0 : if(!tex)
1244 : {
1245 0 : glGenTextures(1, &tex);
1246 : }
1247 0 : vec2 sasize = shadowatlaspacker.dimensions();
1248 0 : target = usegatherforsm() ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE;
1249 0 : createtexture(tex, sasize.x, sasize.y, nullptr, 3, 1, smdepthprec > 1 ? GL_DEPTH_COMPONENT32 : (smdepthprec ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16), target);
1250 0 : glTexParameteri(target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
1251 0 : glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
1252 :
1253 0 : if(!fbo)
1254 : {
1255 0 : glGenFramebuffers(1, &fbo);
1256 : }
1257 0 : glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1258 0 : glDrawBuffer(GL_NONE);
1259 0 : glReadBuffer(GL_NONE);
1260 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, target, tex, 0);
1261 0 : if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
1262 : {
1263 0 : fatal("failed allocating shadow atlas!");
1264 : }
1265 0 : glBindFramebuffer(GL_FRAMEBUFFER, 0);
1266 0 : }
1267 :
1268 : const std::array<matrix4, 6> cubeshadowviewmatrix =
1269 : {{
1270 : // sign-preserving cubemap projections
1271 : matrix4(vec(0, 0, 1), vec(0, 1, 0), vec(-1, 0, 0)), // +X
1272 : matrix4(vec(0, 0, 1), vec(0, 1, 0), vec( 1, 0, 0)), // -X
1273 : matrix4(vec(1, 0, 0), vec(0, 0, 1), vec(0, -1, 0)), // +Y
1274 : matrix4(vec(1, 0, 0), vec(0, 0, 1), vec(0, 1, 0)), // -Y
1275 : matrix4(vec(1, 0, 0), vec(0, 1, 0), vec(0, 0, -1)), // +Z
1276 : matrix4(vec(1, 0, 0), vec(0, 1, 0), vec(0, 0, 1)) // -Z
1277 : }};
1278 :
1279 : static constexpr int LightTile_MaxBatch = 8; //also used in lightbatchkey below
1280 :
1281 0 : static VARF(lighttilebatch, 0, LightTile_MaxBatch, LightTile_MaxBatch, cleardeferredlightshaders());
1282 0 : VARF(batchsunlight, 0, 2, 2, cleardeferredlightshaders());
1283 :
1284 : int shadowmapping = 0;
1285 :
1286 : //not final: batchstack/batchrect derived
1287 : class LightRect
1288 : {
1289 : public:
1290 : uchar x1, y1, x2, y2;
1291 :
1292 : LightRect() {}
1293 0 : LightRect(const lightinfo &l)
1294 0 : {
1295 0 : calctilebounds(l.sx1, l.sy1, l.sx2, l.sy2, x1, y1, x2, y2);
1296 0 : }
1297 :
1298 0 : bool outside(const LightRect &o) const
1299 : {
1300 0 : return x1 >= o.x2 || x2 <= o.x1 || y1 >= o.y2 || y2 <= o.y1;
1301 : }
1302 :
1303 0 : bool inside(const LightRect &o) const
1304 : {
1305 0 : return x1 >= o.x1 && x2 <= o.x2 && y1 >= o.y1 && y2 <= o.y2;
1306 : }
1307 :
1308 0 : void intersect(const LightRect &o)
1309 : {
1310 0 : x1 = std::max(x1, o.x1);
1311 0 : y1 = std::max(y1, o.y1);
1312 0 : x2 = std::min(x2, o.x2);
1313 0 : y2 = std::min(y2, o.y2);
1314 0 : }
1315 :
1316 0 : bool overlaps(int tx1, int ty1, int tx2, int ty2, const uint *tilemask) const
1317 : {
1318 0 : if(static_cast<int>(x2) <= tx1 || static_cast<int>(x1) >= tx2 || static_cast<int>(y2) <= ty1 || static_cast<int>(y1) >= ty2)
1319 : {
1320 0 : return false;
1321 : }
1322 0 : if(!tilemask)
1323 : {
1324 0 : return true;
1325 : }
1326 0 : uint xmask = (1<<x2) - (1<<x1);
1327 0 : for(int y = std::max(static_cast<int>(y1), ty1), end = std::min(static_cast<int>(y2), ty2); y < end; y++)
1328 : {
1329 0 : if(tilemask[y] & xmask)
1330 : {
1331 0 : return true;
1332 : }
1333 : }
1334 0 : return false;
1335 : }
1336 : protected:
1337 : //only called by child batchstack object
1338 0 : LightRect(uchar x1, uchar y1, uchar x2, uchar y2) : x1(x1), y1(y1), x2(x2), y2(y2) {}
1339 : };
1340 :
1341 : //batchflag enum is local to this file
1342 : enum
1343 : {
1344 : BatchFlag_Spotlight = 1<<0,
1345 : BatchFlag_NoShadow = 1<<1,
1346 : BatchFlag_NoSun = 1<<2
1347 : };
1348 :
1349 : struct LightBatch
1350 : {
1351 : uchar flags, numlights;
1352 : ushort lights[LightTile_MaxBatch];
1353 :
1354 : std::vector<LightRect> rects;
1355 :
1356 : void reset()
1357 : {
1358 : rects.clear();
1359 : }
1360 :
1361 0 : bool overlaps(int tx1, int ty1, int tx2, int ty2, const uint *tilemask) const
1362 : {
1363 0 : if(!tx1 && !ty1 && tx2 >= lighttilew && ty2 >= lighttileh && !tilemask)
1364 : {
1365 0 : return true;
1366 : }
1367 0 : for(size_t i = 0; i < rects.size(); i++)
1368 : {
1369 0 : if(rects[i].overlaps(tx1, ty1, tx2, ty2, tilemask))
1370 : {
1371 0 : return true;
1372 : }
1373 : }
1374 0 : return false;
1375 : }
1376 : };
1377 :
1378 : static std::vector<lightinfo> lights;
1379 : static std::vector<int> lightorder;
1380 : static std::vector<LightBatch> lightbatches;
1381 : std::vector<ShadowMapInfo> shadowmaps;
1382 :
1383 1 : void clearshadowcache()
1384 : {
1385 1 : shadowmaps.clear();
1386 :
1387 1 : clearradiancehintscache();
1388 1 : clearshadowmeshes();
1389 1 : }
1390 :
1391 0 : void addshadowmap(ushort x, ushort y, int size, int &idx, int light, const shadowcacheval *cached)
1392 : {
1393 0 : idx = shadowmaps.size();
1394 : ShadowMapInfo sm;
1395 0 : sm.x = x;
1396 0 : sm.y = y;
1397 0 : sm.size = size;
1398 0 : sm.light = light;
1399 0 : sm.sidemask = 0;
1400 0 : sm.cached = cached;
1401 0 : shadowmaps.push_back(sm);
1402 0 : }
1403 :
1404 : //calculate bouunding box reflective shadow map splits
1405 0 : int calcbbrsmsplits(const ivec &bbmin, const ivec &bbmax)
1406 : {
1407 0 : if(!rsmcull)
1408 : {
1409 0 : return 1;
1410 : }
1411 0 : for(int k = 0; k < 4; ++k)
1412 : {
1413 0 : const plane &p = rsm.cull[k];
1414 0 : ivec omin, omax;
1415 0 : if(p.x > 0)
1416 : {
1417 0 : omin.x = bbmin.x;
1418 0 : omax.x = bbmax.x;
1419 : }
1420 : else
1421 : {
1422 0 : omin.x = bbmax.x;
1423 0 : omax.x = bbmin.x;
1424 : }
1425 0 : if(p.y > 0)
1426 : {
1427 0 : omin.y = bbmin.y;
1428 0 : omax.y = bbmax.y;
1429 : }
1430 : else
1431 : {
1432 0 : omin.y = bbmax.y;
1433 0 : omax.y = bbmin.y;
1434 : }
1435 0 : if(p.z > 0)
1436 : {
1437 0 : omin.z = bbmin.z;
1438 0 : omax.z = bbmax.z;
1439 : }
1440 : else
1441 : {
1442 0 : omin.z = bbmax.z;
1443 0 : omax.z = bbmin.z;
1444 : }
1445 0 : if(omax.dist(p) < 0)
1446 : {
1447 0 : return 0;
1448 : }
1449 0 : if(omin.dist(p) < 0)
1450 : {
1451 0 : while(++k < 4)
1452 : {
1453 0 : const plane &p = rsm.cull[k];
1454 0 : ivec omax(p.x > 0 ? bbmax.x : bbmin.x, p.y > 0 ? bbmax.y : bbmin.y, p.z > 0 ? bbmax.z : bbmin.z);
1455 0 : if(omax.dist(p) < 0)
1456 : {
1457 0 : return 0;
1458 : }
1459 : }
1460 : }
1461 : }
1462 0 : return 1;
1463 : }
1464 :
1465 0 : int calcspherersmsplits(const vec ¢er, float radius)
1466 : {
1467 0 : if(!rsmcull)
1468 : {
1469 0 : return 1;
1470 : }
1471 0 : for(int k = 0; k < 4; ++k)
1472 : {
1473 0 : const plane &p = rsm.cull[k];
1474 0 : float dist = p.dist(center);
1475 0 : if(dist < -radius)
1476 : {
1477 0 : return 0;
1478 : }
1479 0 : if(dist < radius)
1480 : {
1481 0 : while(++k < 4)
1482 : {
1483 0 : const plane &p = rsm.cull[k];
1484 0 : if(p.dist(center) < -radius)
1485 : {
1486 0 : return 0;
1487 : }
1488 : }
1489 : }
1490 : }
1491 0 : return 1;
1492 : }
1493 :
1494 0 : bool sphereinsidespot(const vec &dir, int spot, const vec ¢er, float radius)
1495 : {
1496 0 : const vec2 &sc = sincos360[spot];
1497 0 : float cdist = dir.dot(center),
1498 0 : cradius = radius + sc.y*cdist;
1499 0 : return sc.x*sc.x*(center.dot(center) - cdist*cdist) <= cradius*cradius;
1500 : }
1501 :
1502 0 : bool bbinsidespot(const vec &origin, const vec &dir, int spot, const ivec &bbmin, const ivec &bbmax)
1503 : {
1504 0 : vec radius = vec(ivec(bbmax).sub(bbmin)).mul(0.5f),
1505 0 : center = vec(bbmin).add(radius);
1506 0 : return sphereinsidespot(dir, spot, center.sub(origin), radius.magnitude());
1507 : }
1508 :
1509 : static FVAR(avatarshadowdist, 0, 12, 100);
1510 : static FVAR(avatarshadowbias, 0, 8, 100);
1511 0 : static VARF(avatarshadowstencil, 0, 1, 2, initwarning("g-buffer setup", Init_Load, Change_Shaders));
1512 :
1513 : int avatarmask = 0;
1514 :
1515 1 : static bool useavatarmask()
1516 : {
1517 1 : return avatarshadowstencil && ghasstencil && (!msaasamples || (msaalight && avatarshadowstencil > 1));
1518 : }
1519 :
1520 0 : void enableavatarmask()
1521 : {
1522 0 : if(useavatarmask())
1523 : {
1524 0 : avatarmask = 0x40;
1525 0 : glStencilFunc(GL_ALWAYS, avatarmask, ~0);
1526 0 : glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
1527 0 : glEnable(GL_STENCIL_TEST);
1528 : }
1529 0 : }
1530 :
1531 0 : void disableavatarmask()
1532 : {
1533 0 : if(avatarmask)
1534 : {
1535 0 : avatarmask = 0;
1536 0 : glDisable(GL_STENCIL_TEST);
1537 : }
1538 0 : }
1539 :
1540 : static VAR(forcespotlights, 1, 0, 0);
1541 :
1542 : static Shader *volumetricshader = nullptr;
1543 : std::array<Shader *, 2> volumetricbilateralshader = { nullptr, nullptr };
1544 :
1545 0 : void clearvolumetricshaders()
1546 : {
1547 0 : volumetricshader = nullptr;
1548 0 : volumetricbilateralshader.fill(nullptr);
1549 0 : }
1550 :
1551 0 : static VARFP(volumetric, 0, 1, 1, cleanupvolumetric()); //toggles displaying volumetric lights
1552 0 : static VARFP(volreduce, 0, 1, 2, cleanupvolumetric()); //size reduction factor for volumetric tex: 1 is 1/4, 2 is 1/16
1553 0 : static VARFP(volbilateral, 0, 1, 3, cleanupvolumetric()); //toggles bilateral filtering
1554 : static FVAR(volbilateraldepth, 0, 4, 1e3f); //bilateral filtering depth
1555 0 : static VARFP(volblur, 0, 1, 3, cleanupvolumetric());
1556 0 : static VARFP(volsteps, 1, 32, 128, cleanupvolumetric()); //iterations to run for volumetric algorithm
1557 : static FVAR(volminstep, 0, 0.0625f, 1e3f);
1558 : static FVAR(volprefilter, 0, 0.1, 1e3f);
1559 : static FVAR(voldistclamp, 0, 0.99f, 2);
1560 0 : static CVAR1R(volcolor, 0x808080);
1561 : static FVARR(volscale, 0, 1, 16);
1562 :
1563 0 : Shader *loadvolumetricshader()
1564 : {
1565 0 : std::string common, shadow;
1566 :
1567 0 : if(usegatherforsm())
1568 : {
1569 0 : common.push_back(smfilter > 2 ? 'G' : 'g');
1570 : }
1571 0 : else if(smfilter)
1572 : {
1573 0 : common.push_back(smfilter > 2 ? 'E' : (smfilter > 1 ? 'F' : 'f'));
1574 : }
1575 0 : if(spotlights || forcespotlights)
1576 : {
1577 0 : common.push_back('s');
1578 : }
1579 :
1580 0 : shadow.push_back('p');
1581 :
1582 0 : DEF_FORMAT_STRING(name, "volumetric%s%s%d", common.c_str(), shadow.c_str(), volsteps);
1583 0 : return generateshader(name, "volumetricshader \"%s\" \"%s\" %d", common.c_str(), shadow.c_str(), volsteps);
1584 0 : }
1585 :
1586 0 : static void loadvolumetricshaders()
1587 : {
1588 0 : volumetricshader = loadvolumetricshader();
1589 :
1590 0 : if(volbilateral)
1591 : {
1592 0 : for(int i = 0; i < 2; ++i)
1593 : {
1594 0 : DEF_FORMAT_STRING(name, "volumetricbilateral%c%d%d", 'x' + i, volbilateral, volreduce);
1595 0 : volumetricbilateralshader[i] = generateshader(name, "volumetricbilateralshader %d %d", volbilateral, volreduce);
1596 : }
1597 : }
1598 0 : }
1599 :
1600 : static int volw = -1,
1601 : volh = -1;
1602 : static std::array<GLuint, 2> volfbo = { 0, 0 },
1603 : voltex = { 0, 0 };
1604 :
1605 0 : static void setupvolumetric(int w, int h)
1606 : {
1607 0 : volw = w>>volreduce;
1608 0 : volh = h>>volreduce;
1609 :
1610 0 : for(int i = 0; i < (volbilateral || volblur ? 2 : 1); ++i)
1611 : {
1612 0 : if(!voltex[i])
1613 : {
1614 0 : glGenTextures(1, &voltex[i]);
1615 : }
1616 0 : if(!volfbo[i])
1617 : {
1618 0 : glGenFramebuffers(1, &volfbo[i]);
1619 : }
1620 :
1621 0 : glBindFramebuffer(GL_FRAMEBUFFER, volfbo[i]);
1622 :
1623 0 : createtexture(voltex[i], volw, volh, nullptr, 3, 1, hdrformat, GL_TEXTURE_RECTANGLE);
1624 :
1625 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, voltex[i], 0);
1626 :
1627 0 : if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
1628 : {
1629 0 : fatal("failed allocating volumetric buffer!");
1630 : }
1631 : }
1632 :
1633 0 : glBindFramebuffer(GL_FRAMEBUFFER, 0);
1634 :
1635 0 : loadvolumetricshaders();
1636 0 : }
1637 :
1638 0 : void cleanupvolumetric()
1639 : {
1640 0 : for(GLuint &i : volfbo)
1641 : {
1642 0 : if(i)
1643 : {
1644 0 : glDeleteFramebuffers(1, &i);
1645 0 : i = 0;
1646 : }
1647 : }
1648 0 : for(GLuint &i : voltex)
1649 : {
1650 0 : if(i)
1651 : {
1652 0 : glDeleteTextures(1, &i);
1653 0 : i = 0;
1654 : }
1655 : }
1656 0 : volw = volh = -1;
1657 :
1658 0 : clearvolumetricshaders();
1659 0 : }
1660 :
1661 : static Shader *deferredlightshader = nullptr,
1662 : *deferredminimapshader = nullptr,
1663 : *deferredmsaapixelshader = nullptr,
1664 : *deferredmsaasampleshader = nullptr;
1665 :
1666 0 : void cleardeferredlightshaders()
1667 : {
1668 0 : deferredlightshader = nullptr;
1669 0 : deferredminimapshader = nullptr;
1670 0 : deferredmsaapixelshader = nullptr;
1671 0 : deferredmsaasampleshader = nullptr;
1672 0 : }
1673 :
1674 0 : Shader *loaddeferredlightshader(const char *type = nullptr)
1675 : {
1676 : string common, shadow, sun;
1677 0 : int commonlen = 0,
1678 0 : shadowlen = 0,
1679 0 : sunlen = 0;
1680 :
1681 0 : bool minimap = false,
1682 0 : multisample = false,
1683 0 : avatar = true;
1684 0 : if(type)
1685 : {
1686 0 : if(std::strchr(type, 'm'))
1687 : {
1688 0 : minimap = true;
1689 : }
1690 0 : if(std::strchr(type, 'M'))
1691 : {
1692 0 : multisample = true;
1693 : }
1694 0 : if(std::strchr(type, 'D'))
1695 : {
1696 0 : avatar = false;
1697 : }
1698 0 : copystring(common, type);
1699 0 : commonlen = std::strlen(common);
1700 : }
1701 0 : if(!minimap)
1702 : {
1703 0 : if(!multisample || msaalight)
1704 : {
1705 0 : common[commonlen++] = 't';
1706 : }
1707 0 : if(avatar && useavatarmask())
1708 : {
1709 0 : common[commonlen++] = 'd';
1710 : }
1711 0 : if(lighttilebatch)
1712 : {
1713 0 : common[commonlen++] = 'n';
1714 0 : common[commonlen++] = '0' + lighttilebatch;
1715 : }
1716 : }
1717 0 : if(usegatherforsm())
1718 : {
1719 0 : common[commonlen++] = smfilter > 2 ? 'G' : 'g';
1720 : }
1721 0 : else if(smfilter)
1722 : {
1723 0 : common[commonlen++] = smfilter > 2 ? 'E' : (smfilter > 1 ? 'F' : 'f');
1724 : }
1725 0 : if(spotlights || forcespotlights)
1726 : {
1727 0 : common[commonlen++] = 's';
1728 : }
1729 0 : if(nospeclights)
1730 : {
1731 0 : common[commonlen++] = 'z';
1732 : }
1733 0 : common[commonlen] = '\0';
1734 :
1735 0 : shadow[shadowlen++] = 'p';
1736 0 : shadow[shadowlen] = '\0';
1737 :
1738 0 : int usecsm = 0,
1739 0 : userh = 0;
1740 0 : if(!sunlight.iszero() && csm.getcsmproperty(CascadedShadowMap::ShadowMap))
1741 : {
1742 0 : usecsm = csm.getcsmproperty(CascadedShadowMap::Splits);
1743 0 : sun[sunlen++] = 'c';
1744 0 : sun[sunlen++] = '0' + usecsm;
1745 0 : if(!minimap)
1746 : {
1747 0 : if(avatar && ao && aosun)
1748 : {
1749 0 : sun[sunlen++] = 'A';
1750 : }
1751 0 : if(gi && giscale && gidist)
1752 : {
1753 0 : userh = rhsplits;
1754 0 : sun[sunlen++] = 'r';
1755 0 : sun[sunlen++] = '0' + rhsplits;
1756 : }
1757 : }
1758 : }
1759 0 : if(!minimap)
1760 : {
1761 0 : if(avatar && ao)
1762 : {
1763 0 : sun[sunlen++] = 'a';
1764 : }
1765 0 : if(lighttilebatch && (!usecsm || batchsunlight > (userh ? 1 : 0)))
1766 : {
1767 0 : sun[sunlen++] = 'b';
1768 : }
1769 : }
1770 0 : sun[sunlen] = '\0';
1771 :
1772 0 : DEF_FORMAT_STRING(name, "deferredlight%s%s%s", common, shadow, sun);
1773 0 : return generateshader(name, "deferredlightshader \"%s\" \"%s\" \"%s\" %d %d %d", common, shadow, sun, usecsm, userh, !minimap ? lighttilebatch : 0);
1774 : }
1775 :
1776 0 : void loaddeferredlightshaders()
1777 : {
1778 0 : if(msaasamples)
1779 : {
1780 : string opts;
1781 0 : if(msaalight > 2)
1782 : {
1783 0 : copystring(opts, "MS");
1784 : }
1785 0 : else if(msaalight==2)
1786 : {
1787 0 : copystring(opts, ghasstencil || !msaaedgedetect ? "MO" : "MOT");
1788 : }
1789 : else
1790 : {
1791 0 : formatstring(opts, ghasstencil || !msaaedgedetect ? "MR%d" : "MRT%d", msaasamples);
1792 : }
1793 0 : deferredmsaasampleshader = loaddeferredlightshader(opts);
1794 0 : deferredmsaapixelshader = loaddeferredlightshader("M");
1795 0 : deferredlightshader = msaalight ? deferredmsaapixelshader : loaddeferredlightshader("D");
1796 : }
1797 : else
1798 : {
1799 0 : deferredlightshader = loaddeferredlightshader();
1800 : }
1801 0 : }
1802 :
1803 0 : static bool sortlights(int x, int y)
1804 : {
1805 0 : const lightinfo &xl = lights[x],
1806 0 : &yl = lights[y];
1807 0 : if(!xl.spot)
1808 : {
1809 0 : if(yl.spot)
1810 : {
1811 0 : return true;
1812 : }
1813 : }
1814 0 : else if(!yl.spot)
1815 : {
1816 0 : return false;
1817 : }
1818 0 : if(!xl.noshadow())
1819 : {
1820 0 : if(yl.noshadow())
1821 : {
1822 0 : return true;
1823 : }
1824 : }
1825 0 : else if(!yl.noshadow())
1826 : {
1827 0 : return false;
1828 : }
1829 0 : if(xl.sz1 < yl.sz1)
1830 : {
1831 0 : return true;
1832 : }
1833 0 : else if(xl.sz1 > yl.sz1)
1834 : {
1835 0 : return false;
1836 : }
1837 0 : return xl.dist - xl.radius < yl.dist - yl.radius;
1838 : }
1839 :
1840 : VAR(lighttilealignw, 1, 16, 256); // x tiling size for lights inside the shadow cache (pixel grid size to snap to)
1841 : VAR(lighttilealignh, 1, 16, 256); // y tiling size for lights
1842 : int lighttilemaxw = variable("lighttilew", 1, 10, lighttilemaxwidth, &lighttilemaxw, nullptr, 0);
1843 : int lighttilemaxh = variable("lighttileh", 1, 10, lighttilemaxheight, &lighttilemaxh, nullptr, 0);
1844 :
1845 : int lighttilew = 0,
1846 : lighttileh = 0,
1847 : lighttilevieww = 0,
1848 : lighttileviewh = 0;
1849 :
1850 0 : void calctilesize()
1851 : {
1852 0 : lighttilevieww = (vieww + lighttilealignw - 1)/lighttilealignw;
1853 0 : lighttileviewh = (viewh + lighttilealignh - 1)/lighttilealignh;
1854 0 : lighttilew = std::min(lighttilevieww, lighttilemaxw);
1855 0 : lighttileh = std::min(lighttileviewh, lighttilemaxh);
1856 0 : }
1857 :
1858 0 : void resetlights()
1859 : {
1860 : static constexpr int shadowcacheevict = 2;
1861 : static int evictshadowcache = 0;
1862 0 : shadowatlas.cache.clear();
1863 0 : if(smcache)
1864 : {
1865 0 : vec2 sasize = shadowatlaspacker.dimensions();
1866 0 : int evictx = ((evictshadowcache%shadowcacheevict)*sasize.x)/shadowcacheevict,
1867 0 : evicty = ((evictshadowcache/shadowcacheevict)*sasize.y)/shadowcacheevict,
1868 0 : evictx2 = (((evictshadowcache%shadowcacheevict)+1)*sasize.x)/shadowcacheevict,
1869 0 : evicty2 = (((evictshadowcache/shadowcacheevict)+1)*sasize.y)/shadowcacheevict;
1870 0 : for(const ShadowMapInfo &sm : shadowmaps)
1871 : {
1872 0 : if(sm.light < 0)
1873 : {
1874 0 : continue;
1875 : }
1876 0 : lightinfo &l = lights[sm.light];
1877 0 : if(sm.cached && shadowatlas.full)
1878 : {
1879 0 : int w = l.spot ? sm.size : sm.size*3,
1880 0 : h = l.spot ? sm.size : sm.size*2;
1881 0 : if(sm.x < evictx2 && sm.x + w > evictx && sm.y < evicty2 && sm.y + h > evicty)
1882 : {
1883 0 : continue;
1884 : }
1885 : }
1886 0 : shadowatlas.cache[l] = sm;
1887 : }
1888 0 : if(shadowatlas.full)
1889 : {
1890 0 : evictshadowcache = (evictshadowcache + 1)%(shadowcacheevict*shadowcacheevict);
1891 0 : shadowatlas.full = false;
1892 : }
1893 : }
1894 :
1895 0 : lights.clear();
1896 0 : lightorder.clear();
1897 :
1898 0 : shadowmaps.clear();
1899 0 : shadowatlaspacker.reset();
1900 :
1901 0 : calctilesize();
1902 0 : }
1903 :
1904 : static VAR(depthtestlights, 0, 2, 2);
1905 : static FVAR(depthtestlightsclamp, 0, 0.999995f, 1); //z margin for light depth testing at depthtestlights = 2
1906 : static VAR(depthfaillights, 0, 1, 1);
1907 :
1908 0 : static void lightquads(float z, const vec2 &s1, const vec2 &s2)
1909 : {
1910 0 : gle::attribf(s1.x, s1.y, z);
1911 0 : gle::attribf(s1.x, s2.y, z);
1912 0 : gle::attribf(s2.x, s2.y, z);
1913 0 : gle::attribf(s1.x, s1.y, z);
1914 0 : gle::attribf(s2.x, s2.y, z);
1915 0 : gle::attribf(s2.x, s1.y, z);
1916 :
1917 0 : }
1918 :
1919 0 : static void lightquads(float z, const vec2 &s1, const vec2 &s2, const ivec2 &t1, const ivec2 &t2)
1920 : {
1921 0 : int vx1 = std::max(static_cast<int>(std::floor((s1.x*0.5f+0.5f)*vieww)), ((t1.x()*lighttilevieww)/lighttilew)*lighttilealignw),
1922 0 : vy1 = std::max(static_cast<int>(std::floor((s1.y*0.5f+0.5f)*viewh)), ((t1.y()*lighttileviewh)/lighttileh)*lighttilealignh),
1923 0 : vx2 = std::min(static_cast<int>(std::ceil((s2.x*0.5f+0.5f)*vieww)), std::min(((t2.x()*lighttilevieww)/lighttilew)*lighttilealignw, vieww)),
1924 0 : vy2 = std::min(static_cast<int>(std::ceil((s2.y*0.5f+0.5f)*viewh)), std::min(((t2.y()*lighttileviewh)/lighttileh)*lighttilealignh, viewh));
1925 0 : lightquads(z, {(vx1*2.0f)/vieww-1.0f, (vy1*2.0f)/viewh-1.0f}, {(vx2*2.0f)/vieww-1.0f, (vy2*2.0f)/viewh-1.0f});
1926 0 : }
1927 :
1928 0 : static void lightquads(float z, vec2 s1, vec2 s2, int x1, int y1, int x2, int y2, const uint *tilemask)
1929 : {
1930 0 : if(!tilemask)
1931 : {
1932 0 : lightquads(z, s1, s2, {x1, y1}, {x2, y2});
1933 : }
1934 : else
1935 : {
1936 0 : for(int y = y1; y < y2;)
1937 : {
1938 0 : int starty = y;
1939 0 : uint xmask = (1<<x2) - (1<<x1),
1940 0 : startmask = tilemask[y] & xmask;
1941 : do
1942 : {
1943 0 : ++y;
1944 0 : } while(y < y2 && (tilemask[y]&xmask) == startmask);
1945 0 : for(int x = x1; x < x2;)
1946 : {
1947 0 : while(x < x2 && !(startmask&(1<<x)))
1948 : {
1949 0 : ++x;
1950 : }
1951 0 : if(x >= x2)
1952 : {
1953 0 : break;
1954 : }
1955 0 : int startx = x;
1956 : do
1957 : {
1958 0 : ++x;
1959 0 : } while(x < x2 && startmask&(1<<x));
1960 0 : lightquads(z, s1, s2, {startx, starty}, {x, y});
1961 : }
1962 : }
1963 : }
1964 0 : }
1965 :
1966 0 : static void lightquad(float sz1, float bsx1, float bsy1, float bsx2, float bsy2, const uint *tilemask)
1967 : {
1968 : int btx1, bty1, btx2, bty2;
1969 0 : calctilebounds(bsx1, bsy1, bsx2, bsy2, btx1, bty1, btx2, bty2);
1970 :
1971 0 : gle::begin(GL_TRIANGLES);
1972 0 : lightquads(sz1, {bsx1, bsy1}, {bsx2, bsy2}, btx1, bty1, btx2, bty2, tilemask);
1973 0 : gle::end();
1974 0 : }
1975 :
1976 : static VAR(fullbright, 0, 0, 1); //toggles rendering at fullbrightlevel light
1977 : static VAR(fullbrightlevel, 0, 160, 255); //grayscale shade for lighting when at fullbright
1978 :
1979 0 : void GBuffer::bindlighttexs(int msaapass, bool transparent) const
1980 : {
1981 0 : if(msaapass)
1982 : {
1983 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mscolortex);
1984 : }
1985 : else
1986 : {
1987 0 : glBindTexture(GL_TEXTURE_RECTANGLE, gcolortex);
1988 : }
1989 0 : glActiveTexture(GL_TEXTURE1);
1990 0 : if(msaapass)
1991 : {
1992 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msnormaltex);
1993 : }
1994 : else
1995 : {
1996 0 : glBindTexture(GL_TEXTURE_RECTANGLE, gnormaltex);
1997 : }
1998 0 : if(transparent)
1999 : {
2000 0 : glActiveTexture(GL_TEXTURE2);
2001 0 : if(msaapass)
2002 : {
2003 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msglowtex);
2004 : }
2005 : else
2006 : {
2007 0 : glBindTexture(GL_TEXTURE_RECTANGLE, gglowtex);
2008 : }
2009 : }
2010 0 : glActiveTexture(GL_TEXTURE3);
2011 0 : if(msaapass)
2012 : {
2013 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
2014 : }
2015 : else
2016 : {
2017 0 : glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
2018 : }
2019 0 : glActiveTexture(GL_TEXTURE4);
2020 0 : shadowatlas.bind();
2021 0 : shadowatlas.setcomparemode();
2022 0 : if(ao)
2023 : {
2024 0 : glActiveTexture(GL_TEXTURE5);
2025 0 : glBindTexture(GL_TEXTURE_RECTANGLE, aotex[2] ? aotex[2] : aotex[0]);
2026 : }
2027 0 : if(useradiancehints())
2028 : {
2029 0 : for(int i = 0; i < 4; ++i)
2030 : {
2031 0 : glActiveTexture(GL_TEXTURE6 + i);
2032 0 : glBindTexture(GL_TEXTURE_3D, rhtex[i]);
2033 : }
2034 : }
2035 0 : glActiveTexture(GL_TEXTURE0);
2036 0 : }
2037 :
2038 0 : static void setlightglobals(bool transparent = false)
2039 : {
2040 0 : vec2 sasize = shadowatlaspacker.dimensions();
2041 0 : GLOBALPARAMF(shadowatlasscale, 1.0f/sasize.x, 1.0f/sasize.y);
2042 0 : if(ao)
2043 : {
2044 0 : if(transparent || drawtex || (editmode && fullbright))
2045 : {
2046 0 : GLOBALPARAMF(aoscale, 0.0f, 0.0f);
2047 0 : GLOBALPARAMF(aoparams, 1.0f, 0.0f, 1.0f, 0.0f);
2048 0 : }
2049 : else
2050 : {
2051 0 : GLOBALPARAM(aoscale, aotex[2] ? vec2(1, 1) : vec2(static_cast<float>(aow)/vieww, static_cast<float>(aoh)/viewh));
2052 0 : GLOBALPARAMF(aoparams, aomin, 1.0f-aomin, aosunmin, 1.0f-aosunmin);
2053 : }
2054 : }
2055 0 : float lightscale = 2.0f*ldrscaleb();
2056 0 : if(!drawtex && editmode && fullbright)
2057 : {
2058 0 : GLOBALPARAMF(lightscale, fullbrightlevel*lightscale, fullbrightlevel*lightscale, fullbrightlevel*lightscale, 255*lightscale);
2059 0 : }
2060 : else
2061 : {
2062 0 : GLOBALPARAMF(lightscale, ambient.x*lightscale*ambientscale, ambient.y*lightscale*ambientscale, ambient.z*lightscale*ambientscale, 255*lightscale);
2063 : }
2064 0 : if(!sunlight.iszero() && csm.getcsmproperty(CascadedShadowMap::ShadowMap))
2065 : {
2066 0 : csm.bindparams();
2067 0 : rh.bindparams();
2068 0 : if(!drawtex && editmode && fullbright)
2069 : {
2070 0 : GLOBALPARAMF(sunlightdir, 0, 0, 0);
2071 0 : GLOBALPARAMF(sunlightcolor, 0, 0, 0);
2072 0 : GLOBALPARAMF(giscale, 0);
2073 0 : GLOBALPARAMF(skylightcolor, 0, 0, 0);
2074 0 : }
2075 : else
2076 : {
2077 0 : GLOBALPARAM(sunlightdir, sunlightdir);
2078 0 : GLOBALPARAMF(sunlightcolor, sunlight.x*lightscale*sunlightscale, sunlight.y*lightscale*sunlightscale, sunlight.z*lightscale*sunlightscale);
2079 0 : GLOBALPARAMF(giscale, 2*giscale);
2080 0 : GLOBALPARAMF(skylightcolor, 2*giaoscale*skylight.x*lightscale*skylightscale, 2*giaoscale*skylight.y*lightscale*skylightscale, 2*giaoscale*skylight.z*lightscale*skylightscale);
2081 : }
2082 : }
2083 :
2084 0 : matrix4 lightmatrix;
2085 0 : lightmatrix.identity();
2086 0 : GLOBALPARAM(lightmatrix, lightmatrix);
2087 0 : }
2088 :
2089 : //values only for interaction between setlightparams() and setlightshader()
2090 : struct LightParamInfo
2091 : {
2092 : std::array<vec4<float>, 8> lightposv, lightcolorv, spotparamsv, shadowparamsv;
2093 : std::array<vec2, 8> shadowoffsetv;
2094 : };
2095 :
2096 : //sets the ith element of lightposv, lightcolorv, spotparamsv, shadowparamsv, shadowoffsetv
2097 : //UB if i > 7
2098 : //
2099 0 : static void setlightparams(int i, const lightinfo &l, LightParamInfo &li)
2100 : {
2101 0 : li.lightposv[i] = vec4<float>(l.o, 1).div(l.radius);
2102 0 : li.lightcolorv[i] = vec4<float>(vec(l.color).mul(2*ldrscaleb()), l.nospec() ? 0 : 1);
2103 0 : if(l.spot > 0)
2104 : {
2105 0 : li.spotparamsv[i] = vec4<float>(vec(l.dir).neg(), 1/(1 - cos360(l.spot)));
2106 : }
2107 0 : if(l.shadowmap >= 0)
2108 : {
2109 0 : const ShadowMapInfo &sm = shadowmaps[l.shadowmap];
2110 0 : float smnearclip = SQRT3 / l.radius, smfarclip = SQRT3,
2111 0 : bias = (smfilter > 2 || shadowatlaspacker.dimensions().x > shadowatlassize ? smbias2 : smbias) * (smcullside ? 1 : -1) * smnearclip * (1024.0f / sm.size);
2112 0 : int border = smfilter > 2 ? smborder2 : smborder;
2113 0 : if(l.spot > 0)
2114 : {
2115 0 : li.shadowparamsv[i] = vec4<float>(
2116 0 : -0.5f * sm.size * cotan360(l.spot),
2117 0 : (-smnearclip * smfarclip / (smfarclip - smnearclip) - 0.5f*bias),
2118 0 : 1 / (1 + std::fabs(l.dir.z)),
2119 0 : 0.5f + 0.5f * (smfarclip + smnearclip) / (smfarclip - smnearclip));
2120 : }
2121 : else
2122 : {
2123 0 : li.shadowparamsv[i] = vec4<float>(
2124 0 : -0.5f * (sm.size - border),
2125 0 : -smnearclip * smfarclip / (smfarclip - smnearclip) - 0.5f*bias,
2126 0 : sm.size,
2127 0 : 0.5f + 0.5f * (smfarclip + smnearclip) / (smfarclip - smnearclip));
2128 : }
2129 0 : li.shadowoffsetv[i] = vec2(sm.x + 0.5f*sm.size, sm.y + 0.5f*sm.size);
2130 : }
2131 0 : }
2132 :
2133 0 : static void setlightshader(Shader *s, const LightParamInfo &li, int n, bool baselight, bool shadowmap, bool spotlight, bool transparent = false, bool avatar = false)
2134 : {
2135 0 : static const LocalShaderParam lightpos("lightpos"),
2136 0 : lightcolor("lightcolor"),
2137 0 : spotparams("spotparams"),
2138 0 : shadowparams("shadowparams"),
2139 0 : shadowoffset("shadowoffset");
2140 0 : s->setvariant(n-1, (shadowmap ? 1 : 0) + (baselight ? 0 : 2) + (spotlight ? 4 : 0) + (transparent ? 8 : 0) + (avatar ? 24 : 0));
2141 0 : lightpos.setv(li.lightposv.data(), n);
2142 0 : lightcolor.setv(li.lightcolorv.data(), n);
2143 0 : if(spotlight)
2144 : {
2145 0 : spotparams.setv(li.spotparamsv.data(), n);
2146 : }
2147 0 : if(shadowmap)
2148 : {
2149 0 : shadowparams.setv(li.shadowparamsv.data(), n);
2150 0 : shadowoffset.setv(li.shadowoffsetv.data(), n);
2151 : }
2152 0 : }
2153 :
2154 0 : static void setavatarstencil(int stencilref, bool on)
2155 : {
2156 0 : glStencilFunc(GL_EQUAL, (on ? 0x40 : 0) | stencilref, !(stencilref&0x08) && msaalight==2 ? 0x47 : 0x4F);
2157 0 : }
2158 :
2159 0 : void GBuffer::rendersunpass(Shader *s, int stencilref, bool transparent, float bsx1, float bsy1, float bsx2, float bsy2, const uint *tilemask)
2160 : {
2161 0 : if(hasDBT && depthtestlights > 1)
2162 : {
2163 0 : glDepthBounds_(0, depthtestlightsclamp);
2164 : }
2165 0 : int tx1 = std::max(static_cast<int>(std::floor((bsx1*0.5f+0.5f)*vieww)), 0),
2166 0 : ty1 = std::max(static_cast<int>(std::floor((bsy1*0.5f+0.5f)*viewh)), 0),
2167 0 : tx2 = std::min(static_cast<int>(std::ceil((bsx2*0.5f+0.5f)*vieww)), vieww),
2168 0 : ty2 = std::min(static_cast<int>(std::ceil((bsy2*0.5f+0.5f)*viewh)), viewh);
2169 0 : s->setvariant(transparent ? 0 : -1, 16);
2170 0 : lightquad(-1, (tx1*2.0f)/vieww-1.0f, (ty1*2.0f)/viewh-1.0f, (tx2*2.0f)/vieww-1.0f, (ty2*2.0f)/viewh-1.0f, tilemask);
2171 0 : lightpassesused++;
2172 :
2173 0 : if(stencilref >= 0)
2174 : {
2175 0 : setavatarstencil(stencilref, true);
2176 :
2177 0 : s->setvariant(0, 17);
2178 0 : lightquad(-1, (tx1*2.0f)/vieww-1.0f, (ty1*2.0f)/viewh-1.0f, (tx2*2.0f)/vieww-1.0f, (ty2*2.0f)/viewh-1.0f, tilemask);
2179 0 : lightpassesused++;
2180 :
2181 0 : setavatarstencil(stencilref, false);
2182 : }
2183 0 : }
2184 :
2185 0 : void GBuffer::renderlightsnobatch(Shader *s, int stencilref, bool transparent, float bsx1, float bsy1, float bsx2, float bsy2)
2186 : {
2187 0 : lightsphere::enable();
2188 :
2189 0 : glEnable(GL_SCISSOR_TEST);
2190 :
2191 0 : bool outside = true;
2192 0 : static LightParamInfo li;
2193 0 : for(int avatarpass = 0; avatarpass < (stencilref >= 0 ? 2 : 1); ++avatarpass)
2194 : {
2195 0 : if(avatarpass)
2196 : {
2197 0 : setavatarstencil(stencilref, true);
2198 : }
2199 :
2200 0 : for(size_t i = 0; i < lightorder.size(); i++)
2201 : {
2202 0 : const lightinfo &l = lights[lightorder[i]];
2203 0 : float sx1 = std::max(bsx1, l.sx1),
2204 0 : sy1 = std::max(bsy1, l.sy1),
2205 0 : sx2 = std::min(bsx2, l.sx2),
2206 0 : sy2 = std::min(bsy2, l.sy2);
2207 0 : if(sx1 >= sx2 || sy1 >= sy2 || l.sz1 >= l.sz2 || (avatarpass && l.dist - l.radius > avatarshadowdist))
2208 : {
2209 0 : continue;
2210 : }
2211 0 : matrix4 lightmatrix = camprojmatrix;
2212 0 : lightmatrix.translate(l.o);
2213 0 : lightmatrix.scale(l.radius);
2214 0 : GLOBALPARAM(lightmatrix, lightmatrix);
2215 :
2216 0 : setlightparams(0, l, li);
2217 0 : setlightshader(s, li, 1, false, l.shadowmap >= 0, l.spot > 0, transparent, avatarpass > 0);
2218 :
2219 0 : int tx1 = static_cast<int>(std::floor((sx1*0.5f+0.5f)*vieww)),
2220 0 : ty1 = static_cast<int>(std::floor((sy1*0.5f+0.5f)*viewh)),
2221 0 : tx2 = static_cast<int>(std::ceil((sx2*0.5f+0.5f)*vieww)),
2222 0 : ty2 = static_cast<int>(std::ceil((sy2*0.5f+0.5f)*viewh));
2223 0 : glScissor(tx1, ty1, tx2-tx1, ty2-ty1);
2224 :
2225 0 : if(hasDBT && depthtestlights > 1)
2226 : {
2227 0 : glDepthBounds_(l.sz1*0.5f + 0.5f, std::min(l.sz2*0.5f + 0.5f, depthtestlightsclamp));
2228 : }
2229 :
2230 0 : if(camera1->o.dist(l.o) <= l.radius + nearplane + 1 && depthfaillights)
2231 : {
2232 0 : if(outside)
2233 : {
2234 0 : outside = false;
2235 0 : glDepthFunc(GL_GEQUAL);
2236 0 : glCullFace(GL_FRONT);
2237 : }
2238 : }
2239 0 : else if(!outside)
2240 : {
2241 0 : outside = true;
2242 0 : glDepthFunc(GL_LESS);
2243 0 : glCullFace(GL_BACK);
2244 : }
2245 :
2246 0 : lightsphere::draw();
2247 :
2248 0 : lightpassesused++;
2249 : }
2250 :
2251 0 : if(avatarpass)
2252 : {
2253 0 : setavatarstencil(stencilref, false);
2254 : }
2255 : }
2256 :
2257 0 : if(!outside)
2258 : {
2259 0 : outside = true;
2260 0 : glDepthFunc(GL_LESS);
2261 0 : glCullFace(GL_BACK);
2262 : }
2263 :
2264 0 : glDisable(GL_SCISSOR_TEST);
2265 :
2266 0 : lightsphere::disable();
2267 0 : }
2268 :
2269 0 : void GBuffer::renderlightbatches(Shader &s, int stencilref, bool transparent, float bsx1, float bsy1, float bsx2, float bsy2, const uint *tilemask)
2270 : {
2271 0 : bool sunpass = !sunlight.iszero() && csm.getcsmproperty(CascadedShadowMap::ShadowMap) && batchsunlight <= (gi && giscale && gidist ? 1 : 0);
2272 : int btx1, bty1, btx2, bty2;
2273 0 : calctilebounds(bsx1, bsy1, bsx2, bsy2, btx1, bty1, btx2, bty2);
2274 0 : static LightParamInfo li;
2275 0 : for(size_t i = 0; i < lightbatches.size(); i++)
2276 : {
2277 0 : const LightBatch &batch = lightbatches[i];
2278 0 : if(!batch.overlaps(btx1, bty1, btx2, bty2, tilemask))
2279 : {
2280 0 : continue;
2281 : }
2282 :
2283 0 : int n = batch.numlights;
2284 0 : float sx1 = 1,
2285 0 : sy1 = 1,
2286 0 : sx2 = -1,
2287 0 : sy2 = -1,
2288 0 : sz1 = 1,
2289 0 : sz2 = -1;
2290 0 : for(int j = 0; j < n; ++j)
2291 : {
2292 0 : const lightinfo &l = lights[batch.lights[j]];
2293 0 : setlightparams(j, l, li); //set 0...batch.numlights
2294 0 : l.addscissor(sx1, sy1, sx2, sy2, sz1, sz2);
2295 : }
2296 :
2297 0 : bool baselight = !(batch.flags & BatchFlag_NoSun) && !sunpass;
2298 0 : if(baselight)
2299 : {
2300 0 : sx1 = bsx1;
2301 0 : sy1 = bsy1;
2302 0 : sx2 = bsx2;
2303 0 : sy2 = bsy2;
2304 0 : sz1 = -1;
2305 0 : sz2 = 1;
2306 : }
2307 : else
2308 : {
2309 0 : sx1 = std::max(sx1, bsx1);
2310 0 : sy1 = std::max(sy1, bsy1);
2311 0 : sx2 = std::min(sx2, bsx2);
2312 0 : sy2 = std::min(sy2, bsy2);
2313 0 : if(sx1 >= sx2 || sy1 >= sy2 || sz1 >= sz2)
2314 : {
2315 0 : continue;
2316 : }
2317 : }
2318 :
2319 0 : if(n)
2320 : {
2321 0 : bool shadowmap = !(batch.flags & BatchFlag_NoShadow),
2322 0 : spotlight = (batch.flags & BatchFlag_Spotlight) != 0;
2323 0 : setlightshader(&s, li, n, baselight, shadowmap, spotlight, transparent);
2324 : }
2325 : else
2326 : {
2327 0 : s.setvariant(transparent ? 0 : -1, 16);
2328 : }
2329 :
2330 0 : lightpassesused++;
2331 :
2332 0 : if(hasDBT && depthtestlights > 1)
2333 : {
2334 0 : glDepthBounds_(sz1*0.5f + 0.5f, std::min(sz2*0.5f + 0.5f, depthtestlightsclamp));
2335 : }
2336 0 : gle::begin(GL_TRIANGLES);
2337 0 : for(size_t j = 0; j < batch.rects.size(); j++)
2338 : {
2339 0 : const LightRect &r = batch.rects[j];
2340 0 : int x1 = std::max(static_cast<int>(r.x1), btx1),
2341 0 : y1 = std::max(static_cast<int>(r.y1), bty1),
2342 0 : x2 = std::min(static_cast<int>(r.x2), btx2),
2343 0 : y2 = std::min(static_cast<int>(r.y2), bty2);
2344 0 : if(x1 < x2 && y1 < y2)
2345 : {
2346 0 : lightquads(sz1, {sx1, sy1}, {sx2, sy2}, x1, y1, x2, y2, tilemask);
2347 : }
2348 : }
2349 0 : gle::end();
2350 : }
2351 :
2352 0 : if(stencilref >= 0)
2353 : {
2354 0 : setavatarstencil(stencilref, true);
2355 :
2356 0 : bool baselight = !sunpass;
2357 0 : for(int offset = 0; baselight || offset < static_cast<int>(lightorder.size()); baselight = false)
2358 : {
2359 0 : int n = 0;
2360 0 : bool shadowmap = false,
2361 0 : spotlight = false;
2362 0 : float sx1 = 1,
2363 0 : sy1 = 1,
2364 0 : sx2 = -1,
2365 0 : sy2 = -1,
2366 0 : sz1 = 1,
2367 0 : sz2 = -1;
2368 0 : for(; offset < static_cast<int>(lightorder.size()); offset++)
2369 : {
2370 0 : const lightinfo &l = lights[lightorder[offset]];
2371 0 : if(l.dist - l.radius > avatarshadowdist)
2372 : {
2373 0 : continue;
2374 : }
2375 0 : if(!n)
2376 : {
2377 0 : shadowmap = l.shadowmap >= 0;
2378 0 : spotlight = l.spot > 0;
2379 : }
2380 0 : else if(n >= lighttilebatch || (l.shadowmap >= 0) != shadowmap || (l.spot > 0) != spotlight)
2381 : {
2382 : break;
2383 : }
2384 0 : setlightparams(n++, l, li);
2385 0 : l.addscissor(sx1, sy1, sx2, sy2, sz1, sz2);
2386 : }
2387 0 : if(baselight)
2388 : {
2389 0 : sx1 = bsx1;
2390 0 : sy1 = bsy1;
2391 0 : sx2 = bsx2;
2392 0 : sy2 = bsy2;
2393 0 : sz1 = -1;
2394 0 : sz2 = 1;
2395 : }
2396 : else
2397 : {
2398 0 : if(!n)
2399 : {
2400 0 : break;
2401 : }
2402 0 : sx1 = std::max(sx1, bsx1);
2403 0 : sy1 = std::max(sy1, bsy1);
2404 0 : sx2 = std::min(sx2, bsx2);
2405 0 : sy2 = std::min(sy2, bsy2);
2406 0 : if(sx1 >= sx2 || sy1 >= sy2 || sz1 >= sz2)
2407 : {
2408 0 : continue;
2409 : }
2410 : }
2411 :
2412 0 : if(n)
2413 : {
2414 0 : setlightshader(&s, li, n, baselight, shadowmap, spotlight, false, true);
2415 : }
2416 : else
2417 : {
2418 0 : s.setvariant(0, 17);
2419 : }
2420 0 : if(hasDBT && depthtestlights > 1)
2421 : {
2422 0 : glDepthBounds_(sz1*0.5f + 0.5f, std::min(sz2*0.5f + 0.5f, depthtestlightsclamp));
2423 : }
2424 0 : lightquad(sz1, sx1, sy1, sx2, sy2, tilemask);
2425 0 : lightpassesused++;
2426 : }
2427 :
2428 0 : setavatarstencil(stencilref, false);
2429 : }
2430 0 : }
2431 :
2432 0 : void GBuffer::renderlights(float bsx1, float bsy1, float bsx2, float bsy2, const uint *tilemask, int stencilmask, int msaapass, bool transparent)
2433 : {
2434 0 : Shader *s = drawtex == Draw_TexMinimap ? deferredminimapshader : (msaapass <= 0 ? deferredlightshader : (msaapass > 1 ? deferredmsaasampleshader : deferredmsaapixelshader));
2435 0 : if(!s || s == nullshader)
2436 : {
2437 0 : return;
2438 : }
2439 :
2440 0 : bool depth = true;
2441 0 : if(!depthtestlights)
2442 : {
2443 0 : glDisable(GL_DEPTH_TEST);
2444 0 : depth = false;
2445 : }
2446 : else
2447 : {
2448 0 : glDepthMask(GL_FALSE);
2449 : }
2450 :
2451 0 : bindlighttexs(msaapass, transparent);
2452 0 : setlightglobals(transparent);
2453 :
2454 0 : gle::defvertex(3);
2455 :
2456 0 : bool avatar = useavatarmask() && !transparent && !drawtex;
2457 0 : int stencilref = -1;
2458 0 : if(msaapass == 1 && ghasstencil)
2459 : {
2460 0 : int tx1 = std::max(static_cast<int>(std::floor((bsx1*0.5f+0.5f)*vieww)), 0),
2461 0 : ty1 = std::max(static_cast<int>(std::floor((bsy1*0.5f+0.5f)*viewh)), 0),
2462 0 : tx2 = std::min(static_cast<int>(std::ceil((bsx2*0.5f+0.5f)*vieww)), vieww),
2463 0 : ty2 = std::min(static_cast<int>(std::ceil((bsy2*0.5f+0.5f)*viewh)), viewh);
2464 0 : glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
2465 0 : if(stencilmask)
2466 : {
2467 0 : glStencilFunc(GL_EQUAL, stencilmask|0x08, 0x07);
2468 : }
2469 : else
2470 : {
2471 0 : glStencilFunc(GL_ALWAYS, 0x08, ~0);
2472 0 : glEnable(GL_STENCIL_TEST);
2473 : }
2474 0 : if(avatar)
2475 : {
2476 0 : glStencilMask(~0x40);
2477 : }
2478 0 : if(depthtestlights && depth)
2479 : {
2480 0 : glDisable(GL_DEPTH_TEST);
2481 0 : depth = false;
2482 : }
2483 0 : glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2484 0 : SETSHADER(msaaedgedetect);
2485 0 : lightquad(-1, (tx1*2.0f)/vieww-1.0f, (ty1*2.0f)/viewh-1.0f, (tx2*2.0f)/vieww-1.0f, (ty2*2.0f)/viewh-1.0f, tilemask);
2486 0 : glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2487 0 : glStencilFunc(GL_EQUAL, stencilref = stencilmask, (avatar ? 0x40 : 0) | (msaalight==2 ? 0x07 : 0x0F));
2488 0 : glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
2489 0 : if(avatar)
2490 : {
2491 0 : glStencilMask(~0);
2492 : }
2493 0 : else if(msaalight==2 && !stencilmask)
2494 : {
2495 0 : glDisable(GL_STENCIL_TEST);
2496 : }
2497 0 : }
2498 0 : else if(msaapass == 2)
2499 : {
2500 0 : if(ghasstencil)
2501 : {
2502 0 : glStencilFunc(GL_EQUAL, stencilref = stencilmask|0x08, avatar ? 0x4F : 0x0F);
2503 : }
2504 0 : if(msaalight==2)
2505 : {
2506 0 : glSampleMaski(0, 2); glEnable(GL_SAMPLE_MASK);
2507 : }
2508 : }
2509 0 : else if(ghasstencil && (stencilmask || avatar))
2510 : {
2511 0 : if(!stencilmask)
2512 : {
2513 0 : glEnable(GL_STENCIL_TEST);
2514 : }
2515 0 : glStencilFunc(GL_EQUAL, stencilref = stencilmask, avatar ? 0x4F : 0x0F);
2516 0 : glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
2517 : }
2518 :
2519 0 : if(!avatar)
2520 : {
2521 0 : stencilref = -1;
2522 : }
2523 :
2524 0 : glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
2525 0 : glEnable(GL_BLEND);
2526 :
2527 0 : if(hasDBT && depthtestlights > 1)
2528 : {
2529 0 : glEnable(GL_DEPTH_BOUNDS_TEST_EXT);
2530 : }
2531 :
2532 0 : bool sunpass = !lighttilebatch || drawtex == Draw_TexMinimap || (!sunlight.iszero() && csm.getcsmproperty(CascadedShadowMap::ShadowMap) && batchsunlight <= (gi && giscale && gidist ? 1 : 0));
2533 0 : if(sunpass)
2534 : {
2535 0 : if(depthtestlights && depth)
2536 : {
2537 0 : glDisable(GL_DEPTH_TEST);
2538 0 : depth = false;
2539 : }
2540 0 : rendersunpass(s, stencilref, transparent, bsx1, bsy1, bsx2, bsy2, tilemask);
2541 : }
2542 :
2543 0 : if(depthtestlights && !depth)
2544 : {
2545 0 : glEnable(GL_DEPTH_TEST);
2546 0 : depth = true;
2547 : }
2548 :
2549 0 : if(!lighttilebatch || drawtex == Draw_TexMinimap)
2550 : {
2551 0 : renderlightsnobatch(s, stencilref, transparent, bsx1, bsy1, bsx2, bsy2);
2552 : }
2553 : else
2554 : {
2555 0 : renderlightbatches(*s, stencilref, transparent, bsx1, bsy1, bsx2, bsy2, tilemask);
2556 : }
2557 :
2558 0 : if(msaapass == 1 && ghasstencil)
2559 : {
2560 0 : if(msaalight==2 && !stencilmask && !avatar)
2561 : {
2562 0 : glEnable(GL_STENCIL_TEST);
2563 : }
2564 : }
2565 0 : else if(msaapass == 2)
2566 : {
2567 0 : if(ghasstencil && !stencilmask)
2568 : {
2569 0 : glDisable(GL_STENCIL_TEST);
2570 : }
2571 0 : if(msaalight==2)
2572 : {
2573 0 : glDisable(GL_SAMPLE_MASK);
2574 : }
2575 : }
2576 0 : else if(avatar && !stencilmask)
2577 : {
2578 0 : glDisable(GL_STENCIL_TEST);
2579 : }
2580 :
2581 0 : glDisable(GL_BLEND);
2582 :
2583 0 : if(!depthtestlights)
2584 : {
2585 0 : glEnable(GL_DEPTH_TEST);
2586 : }
2587 : else
2588 : {
2589 0 : glDepthMask(GL_TRUE);
2590 0 : if(hasDBT && depthtestlights > 1)
2591 : {
2592 0 : glDisable(GL_DEPTH_BOUNDS_TEST_EXT);
2593 : }
2594 : }
2595 : }
2596 :
2597 0 : void GBuffer::rendervolumetric()
2598 : {
2599 0 : if(!volumetric || !volumetriclights || !volscale)
2600 : {
2601 0 : return;
2602 : }
2603 0 : float bsx1 = 1,
2604 0 : bsy1 = 1,
2605 0 : bsx2 = -1,
2606 0 : bsy2 = -1;
2607 0 : for(size_t i = 0; i < lightorder.size(); i++)
2608 : {
2609 0 : const lightinfo &l = lights[lightorder[i]];
2610 0 : if(!l.volumetric() || l.checkquery())
2611 : {
2612 0 : continue;
2613 : }
2614 :
2615 0 : l.addscissor(bsx1, bsy1, bsx2, bsy2);
2616 : }
2617 0 : if(bsx1 >= bsx2 || bsy1 >= bsy2)
2618 : {
2619 0 : return;
2620 : }
2621 :
2622 0 : timer *voltimer = begintimer("volumetric lights");
2623 :
2624 0 : glBindFramebuffer(GL_FRAMEBUFFER, volfbo[0]);
2625 0 : glViewport(0, 0, volw, volh);
2626 :
2627 0 : glClearColor(0, 0, 0, 0);
2628 0 : glClear(GL_COLOR_BUFFER_BIT);
2629 :
2630 0 : glActiveTexture(GL_TEXTURE3);
2631 0 : if(msaalight)
2632 : {
2633 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
2634 : }
2635 : else
2636 : {
2637 0 : glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
2638 : }
2639 0 : glActiveTexture(GL_TEXTURE4);
2640 0 : shadowatlas.bind();
2641 0 : shadowatlas.setcomparemode();
2642 0 : glActiveTexture(GL_TEXTURE0);
2643 0 : vec2 sasize = shadowatlaspacker.dimensions();
2644 0 : GLOBALPARAMF(shadowatlasscale, 1.0f/sasize.x, 1.0f/sasize.y);
2645 0 : GLOBALPARAMF(volscale, static_cast<float>(vieww)/volw, static_cast<float>(viewh)/volh, static_cast<float>(volw)/vieww, static_cast<float>(volh)/viewh);
2646 0 : GLOBALPARAMF(volminstep, volminstep);
2647 0 : GLOBALPARAMF(volprefilter, volprefilter);
2648 0 : GLOBALPARAMF(voldistclamp, farplane*voldistclamp);
2649 :
2650 0 : glBlendFunc(GL_ONE, GL_ONE);
2651 0 : glEnable(GL_BLEND);
2652 :
2653 0 : if(!depthtestlights)
2654 : {
2655 0 : glDisable(GL_DEPTH_TEST);
2656 : }
2657 : else
2658 : {
2659 0 : glDepthMask(GL_FALSE);
2660 : }
2661 :
2662 0 : lightsphere::enable();
2663 :
2664 0 : glEnable(GL_SCISSOR_TEST);
2665 :
2666 0 : bool outside = true;
2667 0 : for(size_t i = 0; i < lightorder.size(); i++)
2668 : {
2669 0 : const lightinfo &l = lights[lightorder[i]];
2670 0 : if(!l.volumetric() || l.checkquery())
2671 : {
2672 0 : continue;
2673 : }
2674 :
2675 0 : matrix4 lightmatrix = camprojmatrix;
2676 0 : lightmatrix.translate(l.o);
2677 0 : lightmatrix.scale(l.radius);
2678 0 : GLOBALPARAM(lightmatrix, lightmatrix);
2679 :
2680 0 : if(l.spot > 0)
2681 : {
2682 0 : volumetricshader->setvariant(0, l.shadowmap >= 0 ? 2 : 1);
2683 0 : LOCALPARAM(spotparams, vec4<float>(l.dir, 1/(1 - cos360(l.spot))));
2684 : }
2685 0 : else if(l.shadowmap >= 0)
2686 : {
2687 0 : volumetricshader->setvariant(0, 0);
2688 : }
2689 : else
2690 : {
2691 0 : volumetricshader->set();
2692 : }
2693 :
2694 0 : LOCALPARAM(lightpos, vec4<float>(l.o, 1).div(l.radius));
2695 0 : vec color = vec(l.color).mul(ldrscaleb()).mul(volcolor.tocolor().mul(volscale));
2696 0 : LOCALPARAM(lightcolor, color);
2697 :
2698 0 : if(l.shadowmap >= 0)
2699 : {
2700 0 : ShadowMapInfo &sm = shadowmaps[l.shadowmap];
2701 0 : float smnearclip = SQRT3 / l.radius,
2702 0 : smfarclip = SQRT3,
2703 0 : bias = (smfilter > 2 ? smbias2 : smbias) * (smcullside ? 1 : -1) * smnearclip * (1024.0f / sm.size);
2704 0 : int border = smfilter > 2 ? smborder2 : smborder;
2705 0 : if(l.spot > 0)
2706 : {
2707 0 : LOCALPARAMF(shadowparams,
2708 : 0.5f * sm.size * cotan360(l.spot),
2709 : (-smnearclip * smfarclip / (smfarclip - smnearclip) - 0.5f*bias),
2710 : 1 / (1 + std::fabs(l.dir.z)),
2711 : 0.5f + 0.5f * (smfarclip + smnearclip) / (smfarclip - smnearclip));
2712 : }
2713 : else
2714 : {
2715 0 : LOCALPARAMF(shadowparams,
2716 : 0.5f * (sm.size - border),
2717 : -smnearclip * smfarclip / (smfarclip - smnearclip) - 0.5f*bias,
2718 : sm.size,
2719 : 0.5f + 0.5f * (smfarclip + smnearclip) / (smfarclip - smnearclip));
2720 : }
2721 0 : LOCALPARAMF(shadowoffset, sm.x + 0.5f*sm.size, sm.y + 0.5f*sm.size);
2722 : }
2723 :
2724 0 : int tx1 = static_cast<int>(std::floor((l.sx1*0.5f+0.5f)*volw)),
2725 0 : ty1 = static_cast<int>(std::floor((l.sy1*0.5f+0.5f)*volh)),
2726 0 : tx2 = static_cast<int>(std::ceil((l.sx2*0.5f+0.5f)*volw)),
2727 0 : ty2 = static_cast<int>(std::ceil((l.sy2*0.5f+0.5f)*volh));
2728 0 : glScissor(tx1, ty1, tx2-tx1, ty2-ty1);
2729 :
2730 0 : if(camera1->o.dist(l.o) <= l.radius + nearplane + 1 && depthfaillights)
2731 : {
2732 0 : if(outside)
2733 : {
2734 0 : outside = false;
2735 0 : if(depthtestlights)
2736 : {
2737 0 : glDisable(GL_DEPTH_TEST);
2738 : }
2739 0 : glCullFace(GL_FRONT);
2740 : }
2741 : }
2742 0 : else if(!outside)
2743 : {
2744 0 : outside = true;
2745 0 : if(depthtestlights)
2746 : {
2747 0 : glEnable(GL_DEPTH_TEST);
2748 : }
2749 0 : glCullFace(GL_BACK);
2750 : }
2751 :
2752 0 : lightsphere::draw();
2753 : }
2754 :
2755 0 : if(!outside)
2756 : {
2757 0 : outside = true;
2758 0 : glCullFace(GL_BACK);
2759 : }
2760 :
2761 0 : lightsphere::disable();
2762 :
2763 0 : if(depthtestlights)
2764 : {
2765 0 : glDepthMask(GL_TRUE);
2766 :
2767 0 : glDisable(GL_DEPTH_TEST);
2768 : }
2769 :
2770 0 : int cx1 = static_cast<int>(std::floor((bsx1*0.5f+0.5f)*volw))&~1,
2771 0 : cy1 = static_cast<int>(std::floor((bsy1*0.5f+0.5f)*volh))&~1,
2772 0 : cx2 = (static_cast<int>(std::ceil((bsx2*0.5f+0.5f)*volw))&~1) + 2,
2773 0 : cy2 = (static_cast<int>(std::ceil((bsy2*0.5f+0.5f)*volh))&~1) + 2;
2774 0 : if(volbilateral || volblur)
2775 : {
2776 0 : int radius = (volbilateral ? volbilateral : volblur)*2;
2777 0 : cx1 = std::max(cx1 - radius, 0);
2778 0 : cy1 = std::max(cy1 - radius, 0);
2779 0 : cx2 = std::min(cx2 + radius, volw);
2780 0 : cy2 = std::min(cy2 + radius, volh);
2781 0 : glScissor(cx1, cy1, cx2-cx1, cy2-cy1);
2782 :
2783 0 : glDisable(GL_BLEND);
2784 :
2785 0 : if(volbilateral)
2786 : {
2787 0 : for(int i = 0; i < 2; ++i)
2788 : {
2789 0 : glBindFramebuffer(GL_FRAMEBUFFER, volfbo[(i+1)%2]);
2790 0 : glViewport(0, 0, volw, volh);
2791 0 : volumetricbilateralshader[i]->set();
2792 0 : setbilateralparams(volbilateral, volbilateraldepth);
2793 0 : glBindTexture(GL_TEXTURE_RECTANGLE, voltex[i%2]);
2794 0 : screenquadoffset(0.25f, 0.25f, vieww, viewh);
2795 : }
2796 : }
2797 : else
2798 : {
2799 : std::array<float, maxblurradius+1> blurweights,
2800 : bluroffsets;
2801 0 : setupblurkernel(volblur, blurweights, bluroffsets);
2802 0 : for(int i = 0; i < 2; ++i)
2803 : {
2804 0 : glBindFramebuffer(GL_FRAMEBUFFER, volfbo[(i+1)%2]);
2805 0 : glViewport(0, 0, volw, volh);
2806 0 : setblurshader(i%2, 1, volblur, blurweights, bluroffsets, GL_TEXTURE_RECTANGLE);
2807 0 : glBindTexture(GL_TEXTURE_RECTANGLE, voltex[i%2]);
2808 0 : screenquad(volw, volh);
2809 : }
2810 : }
2811 :
2812 0 : glEnable(GL_BLEND);
2813 : }
2814 :
2815 0 : glBindFramebuffer(GL_FRAMEBUFFER, msaalight ? mshdrfbo : hdrfbo);
2816 0 : glViewport(0, 0, vieww, viewh);
2817 :
2818 0 : int margin = (1<<volreduce) - 1;
2819 0 : cx1 = std::max((cx1 * vieww) / volw - margin, 0);
2820 0 : cy1 = std::max((cy1 * viewh) / volh - margin, 0);
2821 0 : cx2 = std::min((cx2 * vieww + margin + volw - 1) / volw, vieww);
2822 0 : cy2 = std::min((cy2 * viewh + margin + volh - 1) / volh, viewh);
2823 0 : glScissor(cx1, cy1, cx2-cx1, cy2-cy1);
2824 :
2825 0 : bool avatar = useavatarmask();
2826 0 : if(avatar)
2827 : {
2828 0 : glStencilFunc(GL_EQUAL, 0, 0x40);
2829 0 : glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
2830 0 : glEnable(GL_STENCIL_TEST);
2831 : }
2832 :
2833 0 : SETSHADER(scalelinear);
2834 0 : glBindTexture(GL_TEXTURE_RECTANGLE, voltex[0]);
2835 0 : screenquad(volw, volh);
2836 :
2837 0 : if(volbilateral || volblur)
2838 : {
2839 0 : std::swap(volfbo[0], volfbo[1]);
2840 0 : std::swap(voltex[0], voltex[1]);
2841 : }
2842 :
2843 0 : if(avatar)
2844 : {
2845 0 : glDisable(GL_STENCIL_TEST);
2846 : }
2847 :
2848 0 : glDisable(GL_SCISSOR_TEST);
2849 :
2850 0 : glEnable(GL_DEPTH_TEST);
2851 :
2852 0 : glDisable(GL_BLEND);
2853 :
2854 0 : endtimer(voltimer);
2855 : }
2856 :
2857 : static VAR(oqvol, 0, 1, 1); //`o`cclusion `q`uery `vol`umetrics: toggles occlusion queries of volumetric lights
2858 : static VAR(oqlights, 0, 1, 1); //`o`cclusion `q`uery `lights: toggles occlusion queries of lights behind geometry
2859 : static VAR(debuglightscissor, 0, 0, 1); //displays the light scissor map in the corner of the screen
2860 :
2861 0 : static void viewlightscissor()
2862 : {
2863 0 : std::vector<extentity *> &ents = entities::getents();
2864 0 : gle::defvertex(2);
2865 0 : for(size_t i = 0; i < entgroup.size(); i++)
2866 : {
2867 0 : size_t idx = entgroup[i];
2868 0 : if((ents.size() > idx) && ents[idx]->type == EngineEnt_Light)
2869 : {
2870 0 : const extentity &e = *ents[idx];
2871 0 : for(size_t j = 0; j < lights.size(); j++)
2872 : {
2873 0 : if(lights[j].o == e.o)
2874 : {
2875 0 : const lightinfo &l = lights[j];
2876 0 : if(!l.validscissor())
2877 : {
2878 0 : break;
2879 : }
2880 0 : gle::colorf(l.color.x/255, l.color.y/255, l.color.z/255);
2881 0 : float x1 = (l.sx1+1)/2*hudw(),
2882 0 : x2 = (l.sx2+1)/2*hudw(),
2883 0 : y1 = (1-l.sy1)/2*hudh(),
2884 0 : y2 = (1-l.sy2)/2*hudh();
2885 0 : gle::begin(GL_TRIANGLE_STRIP);
2886 0 : gle::attribf(x1, y1);
2887 0 : gle::attribf(x2, y1);
2888 0 : gle::attribf(x1, y2);
2889 0 : gle::attribf(x2, y2);
2890 0 : gle::end();
2891 : }
2892 : }
2893 : }
2894 : }
2895 0 : }
2896 :
2897 0 : void collectlights()
2898 : {
2899 0 : if(lights.size())
2900 : {
2901 0 : return;
2902 : }
2903 :
2904 : // point lights processed here
2905 0 : const std::vector<extentity *> &ents = entities::getents();
2906 0 : if(!editmode || !fullbright)
2907 : {
2908 0 : for(size_t i = 0; i < ents.size(); i++)
2909 : {
2910 0 : const extentity *e = ents[i];
2911 0 : if(e->type != EngineEnt_Light || e->attr1 <= 0)
2912 : {
2913 0 : continue;
2914 : }
2915 0 : if(smviscull && view.isfoggedsphere(e->attr1, e->o))
2916 : {
2917 0 : continue;
2918 : }
2919 0 : lightinfo l = lightinfo(i, *e);
2920 0 : lights.push_back(l);
2921 0 : if(l.validscissor())
2922 : {
2923 0 : lightorder.emplace_back(lights.size()-1);
2924 : }
2925 : }
2926 : }
2927 :
2928 0 : size_t numdynlights = 0;
2929 0 : if(!drawtex)
2930 : {
2931 0 : updatedynlights();
2932 0 : numdynlights = finddynlights();
2933 : }
2934 0 : for(size_t i = 0; i < numdynlights; ++i)
2935 : {
2936 0 : vec o, color, dir;
2937 : float radius;
2938 : int spot, flags;
2939 0 : if(!getdynlight(i, o, radius, color, dir, spot, flags))
2940 : {
2941 0 : continue;
2942 : }
2943 0 : const lightinfo &l = lights.emplace_back(lightinfo(o, vec(color).mul(255).max(0), radius, flags, dir, spot));
2944 0 : if(l.validscissor())
2945 : {
2946 0 : lightorder.emplace_back(lights.size()-1);
2947 : }
2948 : }
2949 :
2950 0 : std::sort(lightorder.begin(), lightorder.end(), sortlights);
2951 :
2952 0 : bool queried = false;
2953 0 : if(!drawtex && smquery && oqfrags && oqlights)
2954 : {
2955 0 : for(size_t i = 0; i < lightorder.size(); i++)
2956 : {
2957 0 : int idx = lightorder[i];
2958 0 : lightinfo &l = lights[idx];
2959 0 : if((l.noshadow() && (!oqvol || !l.volumetric())) || l.radius >= rootworld.mapsize())
2960 : {
2961 0 : continue;
2962 : }
2963 0 : vec bbmin, bbmax;
2964 0 : l.calcbb(bbmin, bbmax);
2965 0 : if(!camera1->o.insidebb(bbmin, bbmax, 2))
2966 : {
2967 0 : l.query = occlusionengine.newquery(&l);
2968 0 : if(l.query)
2969 : {
2970 0 : if(!queried)
2971 : {
2972 0 : startbb(false);
2973 0 : queried = true;
2974 : }
2975 0 : l.query->startquery();
2976 0 : ivec bo(bbmin),
2977 0 : br = ivec(bbmax).sub(bo).add(1);
2978 0 : drawbb(bo, br);
2979 0 : occlusionengine.endquery();
2980 : }
2981 : }
2982 : }
2983 : }
2984 0 : if(queried)
2985 : {
2986 0 : endbb(false);
2987 0 : glFlush();
2988 : }
2989 :
2990 0 : smused = 0;
2991 :
2992 0 : if(smcache && !smnoshadow && shadowatlas.cache.size())
2993 : {
2994 0 : for(int mismatched = 0; mismatched < 2; ++mismatched)
2995 : {
2996 0 : for(size_t i = 0; i < lightorder.size(); i++)
2997 : {
2998 0 : int idx = lightorder[i];
2999 0 : lightinfo &l = lights[idx];
3000 0 : if(l.noshadow())
3001 : {
3002 0 : continue;
3003 : }
3004 0 : auto itr = shadowatlas.cache.find(l);
3005 0 : if(itr == shadowatlas.cache.end())
3006 : {
3007 0 : continue;
3008 : }
3009 0 : float prec = smprec,
3010 : lod;
3011 : int w, h;
3012 0 : if(l.spot)
3013 : {
3014 0 : w = 1;
3015 0 : h = 1;
3016 0 : prec *= tan360(l.spot);
3017 0 : lod = smspotprec;
3018 : }
3019 : else
3020 : {
3021 0 : w = 3;
3022 0 : h = 2;
3023 0 : lod = smcubeprec;
3024 : }
3025 0 : lod *= std::clamp(l.radius * prec / sqrtf(std::max(1.0f, l.dist/l.radius)), static_cast<float>(smminsize), static_cast<float>(smmaxsize));
3026 0 : const float sasizex = shadowatlaspacker.dimensions().x;
3027 0 : int size = std::clamp(static_cast<int>(std::ceil((lod * sasizex) / shadowatlassize)), 1, static_cast<int>(sasizex) / w);
3028 0 : w *= size;
3029 0 : h *= size;
3030 0 : const shadowcacheval &cached = (*itr).second;
3031 0 : if(mismatched)
3032 : {
3033 0 : if(cached.size == size)
3034 : {
3035 0 : continue;
3036 : }
3037 0 : ushort x = USHRT_MAX,
3038 0 : y = USHRT_MAX;
3039 0 : if(!shadowatlaspacker.insert(x, y, w, h))
3040 : {
3041 0 : continue;
3042 : }
3043 0 : addshadowmap(x, y, size, l.shadowmap, idx);
3044 : }
3045 : else
3046 : {
3047 0 : if(cached.size != size)
3048 : {
3049 0 : continue;
3050 : }
3051 0 : ushort x = cached.x,
3052 0 : y = cached.y;
3053 0 : shadowatlaspacker.reserve(x, y, w, h);
3054 0 : addshadowmap(x, y, size, l.shadowmap, idx, &cached);
3055 : }
3056 0 : smused += w*h;
3057 : }
3058 : }
3059 : }
3060 : }
3061 :
3062 : static VAR(csminoq, 0, 1, 1); //cascaded shadow maps in occlusion queries
3063 : static VAR(sminoq, 0, 1, 1); //shadow maps in occlusion queries
3064 : VAR(rhinoq, 0, 1, 1); //radiance hints in occlusion queries
3065 :
3066 0 : bool shouldworkinoq()
3067 : {
3068 0 : return !drawtex && oqfrags && (!wireframe || !editmode);
3069 : }
3070 :
3071 : struct BatchRect final : LightRect
3072 : {
3073 : uchar group;
3074 : ushort idx;
3075 :
3076 : BatchRect() {}
3077 0 : BatchRect(const lightinfo &l, ushort idx)
3078 0 : : LightRect(l),
3079 0 : group((l.shadowmap < 0 ? BatchFlag_NoShadow : 0) | (l.spot > 0 ? BatchFlag_Spotlight : 0)),
3080 0 : idx(idx)
3081 0 : {}
3082 : };
3083 :
3084 : struct BatchStack final : LightRect
3085 : {
3086 : ushort offset, numrects;
3087 : uchar flags;
3088 :
3089 : BatchStack() {}
3090 0 : BatchStack(uchar x1, uchar y1, uchar x2, uchar y2, ushort offset, ushort numrects, uchar flags = 0) : LightRect(x1, y1, x2, y2), offset(offset), numrects(numrects), flags(flags) {}
3091 : };
3092 :
3093 0 : static void batchlights(const BatchStack &initstack, std::vector<BatchRect> &batchrects, int &lightbatchstacksused, int &lightbatchrectsused)
3094 : {
3095 0 : constexpr size_t stacksize = 32;
3096 0 : std::stack<BatchStack> stack;
3097 0 : stack.push(initstack);
3098 :
3099 0 : while(stack.size() > 0)
3100 : {
3101 0 : const BatchStack s = stack.top();
3102 0 : stack.pop();
3103 0 : if(stack.size() + 5 > stacksize)
3104 : {
3105 0 : batchlights(s, batchrects, lightbatchstacksused, lightbatchrectsused);
3106 0 : continue;
3107 : }
3108 0 : ++lightbatchstacksused;
3109 0 : int groups[BatchFlag_NoSun] = { 0 };
3110 0 : LightRect split(s);
3111 0 : ushort splitidx = USHRT_MAX;
3112 0 : int outside = s.offset,
3113 0 : inside = s.offset + s.numrects;
3114 0 : for(int i = outside; i < inside; ++i)
3115 : {
3116 0 : const BatchRect &r = batchrects[i];
3117 0 : if(r.outside(s))
3118 : {
3119 0 : if(i != outside)
3120 : {
3121 0 : std::swap(batchrects[i], batchrects[outside]);
3122 : }
3123 0 : ++outside;
3124 : }
3125 0 : else if(s.inside(r))
3126 : {
3127 0 : ++groups[r.group];
3128 0 : std::swap(batchrects[i--], batchrects[--inside]);
3129 : }
3130 0 : else if(r.idx < splitidx)
3131 : {
3132 0 : split = r;
3133 0 : splitidx = r.idx;
3134 : }
3135 : }
3136 :
3137 0 : uchar flags = s.flags;
3138 0 : int batched = s.offset + s.numrects;
3139 0 : for(int g = 0; g < BatchFlag_NoShadow; ++g)
3140 : {
3141 0 : while(groups[g] >= lighttilebatch || (inside == outside && (groups[g] || !(flags & BatchFlag_NoSun))))
3142 : {
3143 0 : LightBatch key;
3144 0 : key.flags = flags | g;
3145 0 : flags |= BatchFlag_NoSun;
3146 :
3147 0 : int n = std::min(groups[g], lighttilebatch);
3148 0 : groups[g] -= n;
3149 0 : key.numlights = n;
3150 0 : for(int i = 0; i < n; ++i)
3151 : {
3152 0 : int best = -1;
3153 0 : ushort bestidx = USHRT_MAX;
3154 0 : for(int j = inside; j < batched; ++j)
3155 : {
3156 0 : const BatchRect &r = batchrects[j];
3157 : {
3158 0 : if(r.group == g && r.idx < bestidx)
3159 : {
3160 0 : best = j;
3161 0 : bestidx = r.idx;
3162 : }
3163 : }
3164 : }
3165 0 : key.lights[i] = lightorder[bestidx];
3166 0 : std::swap(batchrects[best], batchrects[--batched]);
3167 : }
3168 :
3169 0 : key.rects.push_back(s);
3170 0 : lightbatches.push_back(std::move(key));
3171 0 : ++lightbatchrectsused;
3172 0 : }
3173 : }
3174 0 : if(splitidx != USHRT_MAX)
3175 : {
3176 0 : int numoverlap = batched - outside;
3177 0 : split.intersect(s);
3178 :
3179 0 : if(split.y1 > s.y1)
3180 : {
3181 0 : stack.push(BatchStack(s.x1, s.y1, s.x2, split.y1, outside, numoverlap, flags));
3182 : }
3183 0 : if(split.x1 > s.x1)
3184 : {
3185 0 : stack.push(BatchStack(s.x1, split.y1, split.x1, split.y2, outside, numoverlap, flags));
3186 : }
3187 0 : stack.push(BatchStack(split.x1, split.y1, split.x2, split.y2, outside, numoverlap, flags));
3188 0 : if(split.x2 < s.x2)
3189 : {
3190 0 : stack.push(BatchStack(split.x2, split.y1, s.x2, split.y2, outside, numoverlap, flags));
3191 : }
3192 0 : if(split.y2 < s.y2)
3193 : {
3194 0 : stack.push(BatchStack(s.x1, split.y2, s.x2, s.y2, outside, numoverlap, flags));
3195 : }
3196 : }
3197 : }
3198 0 : }
3199 :
3200 0 : static bool sortlightbatches(const LightBatch &x, const LightBatch &y)
3201 : {
3202 0 : if(x.flags < y.flags)
3203 : {
3204 0 : return true;
3205 : }
3206 0 : if(x.flags > y.flags)
3207 : {
3208 0 : return false;
3209 : }
3210 0 : return x.numlights > y.numlights;
3211 : }
3212 :
3213 0 : static void batchlights(std::vector<BatchRect> &batchrects, int &lightbatchstacksused, int &lightbatchrectsused, int &lightbatchesused)
3214 : {
3215 0 : lightbatches.clear();
3216 0 : lightbatchstacksused = 0;
3217 0 : lightbatchrectsused = 0;
3218 :
3219 0 : if(lighttilebatch && drawtex != Draw_TexMinimap)
3220 : {
3221 0 : batchlights(BatchStack(0, 0, lighttilew, lighttileh, 0, batchrects.size()), batchrects, lightbatchstacksused, lightbatchrectsused);
3222 0 : std::sort(lightbatches.begin(), lightbatches.end(), sortlightbatches);
3223 : }
3224 :
3225 0 : lightbatchesused = lightbatches.size();
3226 0 : }
3227 :
3228 0 : void GBuffer::packlights()
3229 : {
3230 0 : lightsvisible = lightsoccluded = 0;
3231 0 : lightpassesused = 0;
3232 0 : std::vector<BatchRect> batchrects;
3233 :
3234 0 : for(size_t i = 0; i < lightorder.size(); i++)
3235 : {
3236 0 : int idx = lightorder[i];
3237 0 : lightinfo &l = lights[idx];
3238 0 : if(l.checkquery())
3239 : {
3240 0 : if(l.shadowmap >= 0)
3241 : {
3242 0 : shadowmaps[l.shadowmap].light = -1;
3243 0 : l.shadowmap = -1;
3244 : }
3245 0 : lightsoccluded++;
3246 0 : continue;
3247 : }
3248 :
3249 0 : if(!l.noshadow() && !smnoshadow && l.shadowmap < 0)
3250 : {
3251 0 : float prec = smprec,
3252 : lod;
3253 : int w, h;
3254 0 : if(l.spot)
3255 : {
3256 0 : w = 1;
3257 0 : h = 1;
3258 0 : prec *= tan360(l.spot);
3259 0 : lod = smspotprec;
3260 : }
3261 : else
3262 : {
3263 0 : w = 3;
3264 0 : h = 2;
3265 0 : lod = smcubeprec;
3266 : }
3267 0 : lod *= std::clamp(l.radius * prec / sqrtf(std::max(1.0f, l.dist/l.radius)), static_cast<float>(smminsize), static_cast<float>(smmaxsize));
3268 0 : const float sasizex = shadowatlaspacker.dimensions().x;
3269 0 : int size = std::clamp(static_cast<int>(std::ceil((lod * sasizex) / shadowatlassize)), 1, static_cast<int>(sasizex) / w);
3270 0 : w *= size;
3271 0 : h *= size;
3272 0 : ushort x = USHRT_MAX,
3273 0 : y = USHRT_MAX;
3274 0 : if(shadowatlaspacker.insert(x, y, w, h))
3275 : {
3276 0 : addshadowmap(x, y, size, l.shadowmap, idx);
3277 0 : smused += w*h;
3278 : }
3279 0 : else if(smcache)
3280 : {
3281 0 : shadowatlas.full = true;
3282 : }
3283 : }
3284 0 : batchrects.emplace_back(l, i);
3285 : }
3286 :
3287 0 : lightsvisible = lightorder.size() - lightsoccluded;
3288 :
3289 0 : batchlights(batchrects, lightbatchstacksused, lightbatchrectsused, lightbatchesused);
3290 0 : }
3291 :
3292 0 : void GBuffer::rendercsmshadowmaps() const
3293 : {
3294 0 : if(sunlight.iszero() || !csm.getcsmproperty(CascadedShadowMap::ShadowMap))
3295 : {
3296 0 : return;
3297 : }
3298 0 : if(inoq)
3299 : {
3300 0 : glBindFramebuffer(GL_FRAMEBUFFER, shadowatlas.fbo);
3301 0 : glDepthMask(GL_TRUE);
3302 : }
3303 0 : csm.setup();
3304 0 : shadowmapping = ShadowMap_Cascade;
3305 0 : shadoworigin = vec(0, 0, 0);
3306 0 : shadowdir = csm.getlightview();
3307 0 : shadowbias = csm.getlightview().project_bb(worldmin, worldmax);
3308 0 : shadowradius = std::fabs(csm.getlightview().project_bb(worldmax, worldmin));
3309 :
3310 0 : float polyfactor = csm.getcsmproperty(CascadedShadowMap::PolyFactor),
3311 0 : polyoffset = csm.getcsmproperty(CascadedShadowMap::PolyOffset);
3312 0 : if(smfilter > 2)
3313 : {
3314 0 : csm.setcsmproperty(CascadedShadowMap::PolyFactor, csm.getcsmproperty(CascadedShadowMap::PolyFactor2));
3315 0 : csm.setcsmproperty(CascadedShadowMap::PolyOffset, csm.getcsmproperty(CascadedShadowMap::PolyOffset2));
3316 : }
3317 0 : if(polyfactor || polyoffset)
3318 : {
3319 0 : glPolygonOffset(polyfactor, polyoffset);
3320 0 : glEnable(GL_POLYGON_OFFSET_FILL);
3321 : }
3322 0 : glEnable(GL_SCISSOR_TEST);
3323 :
3324 0 : findshadowvas();
3325 0 : findshadowmms();
3326 :
3327 0 : batching::shadowmaskbatchedmodels(smdynshadow!=0);
3328 0 : batchshadowmapmodels();
3329 :
3330 0 : for(int i = 0; i < csm.getcsmproperty(CascadedShadowMap::Splits); ++i)
3331 : {
3332 0 : if(csm.splits[i].idx >= 0)
3333 : {
3334 0 : const ShadowMapInfo &sm = shadowmaps[csm.splits[i].idx];
3335 :
3336 0 : shadowmatrix.mul(csm.splits[i].proj, csm.getmodel());
3337 0 : GLOBALPARAM(shadowmatrix, shadowmatrix);
3338 :
3339 0 : glViewport(sm.x, sm.y, sm.size, sm.size);
3340 0 : glScissor(sm.x, sm.y, sm.size, sm.size);
3341 0 : glClear(GL_DEPTH_BUFFER_BIT);
3342 :
3343 0 : shadowside = i;
3344 :
3345 0 : rendershadowmapworld();
3346 0 : batching::rendershadowmodelbatches();
3347 : }
3348 : }
3349 :
3350 0 : batching::clearbatchedmapmodels();
3351 :
3352 0 : glDisable(GL_SCISSOR_TEST);
3353 :
3354 0 : if(polyfactor || polyoffset)
3355 : {
3356 0 : glDisable(GL_POLYGON_OFFSET_FILL);
3357 : }
3358 0 : shadowmapping = 0;
3359 :
3360 0 : if(inoq)
3361 : {
3362 0 : glBindFramebuffer(GL_FRAMEBUFFER, msaasamples ? msfbo : gfbo);
3363 0 : glViewport(0, 0, vieww, viewh);
3364 :
3365 0 : glFlush();
3366 : }
3367 : }
3368 :
3369 0 : int calcshadowinfo(const extentity &e, vec &origin, float &radius, vec &spotloc, int &spotangle, float &bias)
3370 : {
3371 0 : if(e.attr5&LightEnt_NoShadow || e.attr1 <= smminradius)
3372 : {
3373 0 : return ShadowMap_None;
3374 : }
3375 0 : origin = e.o;
3376 0 : radius = e.attr1;
3377 : int type, w, border;
3378 : float lod;
3379 0 : if(e.attached && e.attached->type == EngineEnt_Spotlight)
3380 : {
3381 0 : type = ShadowMap_Spot;
3382 0 : w = 1;
3383 0 : border = 0;
3384 0 : lod = smspotprec;
3385 0 : spotloc = e.attached->o;
3386 0 : spotangle = std::clamp(static_cast<int>(e.attached->attr1), 1, 89);
3387 : }
3388 : else
3389 : {
3390 0 : type = ShadowMap_CubeMap;
3391 0 : w = 3;
3392 0 : lod = smcubeprec;
3393 0 : border = smfilter > 2 ? smborder2 : smborder;
3394 0 : spotloc = e.o;
3395 0 : spotangle = 0;
3396 : }
3397 :
3398 0 : lod *= smminsize;
3399 0 : const float sasizex = shadowatlaspacker.dimensions().x;
3400 0 : int size = std::clamp(static_cast<int>(std::ceil((lod * sasizex) / shadowatlassize)), 1, static_cast<int>(sasizex) / w);
3401 0 : bias = border / static_cast<float>(size - border);
3402 :
3403 0 : return type;
3404 : }
3405 :
3406 : matrix4 shadowmatrix;
3407 :
3408 0 : void GBuffer::rendershadowmaps(int offset) const
3409 : {
3410 0 : if(!(sminoq && !debugshadowatlas && !inoq && shouldworkinoq()))
3411 : {
3412 0 : offset = 0;
3413 : }
3414 :
3415 0 : for(; offset < static_cast<int>(shadowmaps.size()); offset++)
3416 : {
3417 0 : if(shadowmaps[offset].light >= 0)
3418 : {
3419 0 : break;
3420 : }
3421 : }
3422 :
3423 0 : if(offset >= static_cast<int>(shadowmaps.size()))
3424 : {
3425 0 : return;
3426 : }
3427 :
3428 0 : if(inoq)
3429 : {
3430 0 : glBindFramebuffer(GL_FRAMEBUFFER, shadowatlas.fbo);
3431 0 : glDepthMask(GL_TRUE);
3432 : }
3433 :
3434 0 : float polyfactor = smpolyfactor,
3435 0 : polyoffset = smpolyoffset;
3436 0 : if(smfilter > 2)
3437 : {
3438 0 : polyfactor = smpolyfactor2;
3439 0 : polyoffset = smpolyoffset2;
3440 : }
3441 0 : if(polyfactor || polyoffset)
3442 : {
3443 0 : glPolygonOffset(polyfactor, polyoffset);
3444 0 : glEnable(GL_POLYGON_OFFSET_FILL);
3445 : }
3446 :
3447 0 : glEnable(GL_SCISSOR_TEST);
3448 :
3449 0 : const std::vector<extentity *> &ents = entities::getents();
3450 0 : for(size_t i = offset; i < shadowmaps.size(); i++)
3451 : {
3452 0 : ShadowMapInfo &sm = shadowmaps[i];
3453 0 : if(sm.light < 0)
3454 : {
3455 0 : continue;
3456 : }
3457 0 : const lightinfo &l = lights[sm.light];
3458 0 : const extentity *e = l.ent >= 0 ? ents[l.ent] : nullptr;
3459 : int border, sidemask;
3460 0 : if(l.spot)
3461 : {
3462 0 : shadowmapping = ShadowMap_Spot;
3463 0 : border = 0;
3464 0 : sidemask = 1;
3465 : }
3466 : else
3467 : {
3468 0 : shadowmapping = ShadowMap_CubeMap;
3469 0 : border = smfilter > 2 ? smborder2 : smborder;
3470 0 : sidemask = drawtex == Draw_TexMinimap ? 0x2F : (smsidecull ? view.cullfrustumsides(l.o, l.radius, sm.size, border) : 0x3F);
3471 : }
3472 :
3473 0 : sm.sidemask = sidemask;
3474 :
3475 0 : shadoworigin = l.o;
3476 0 : shadowradius = l.radius;
3477 0 : shadowbias = border / static_cast<float>(sm.size - border);
3478 0 : shadowdir = l.dir;
3479 0 : shadowspot = l.spot;
3480 :
3481 0 : const shadowmesh *mesh = e ? findshadowmesh(l.ent, *e) : nullptr;
3482 :
3483 0 : findshadowvas();
3484 0 : findshadowmms();
3485 :
3486 0 : batching::shadowmaskbatchedmodels(!(l.flags&LightEnt_Static) && smdynshadow);
3487 0 : batchshadowmapmodels(mesh != nullptr);
3488 :
3489 0 : const shadowcacheval *cached = nullptr;
3490 0 : int cachemask = 0;
3491 0 : if(smcache)
3492 : {
3493 0 : int dynmask = smcache <= 1 ? batching::batcheddynamicmodels() : 0;
3494 0 : cached = sm.cached;
3495 0 : if(cached)
3496 : {
3497 0 : if(!debugshadowatlas)
3498 : {
3499 0 : cachemask = cached->sidemask & ~dynmask;
3500 : }
3501 0 : sm.sidemask |= cachemask;
3502 : }
3503 0 : sm.sidemask &= ~dynmask;
3504 :
3505 0 : sidemask &= ~cachemask;
3506 0 : if(!sidemask)
3507 : {
3508 0 : batching::clearbatchedmapmodels();
3509 0 : continue;
3510 : }
3511 : }
3512 :
3513 0 : float smnearclip = SQRT3 / l.radius,
3514 0 : smfarclip = SQRT3;
3515 0 : matrix4 smprojmatrix(vec4<float>(static_cast<float>(sm.size - border) / sm.size, 0, 0, 0),
3516 0 : vec4<float>(0, static_cast<float>(sm.size - border) / sm.size, 0, 0),
3517 0 : vec4<float>(0, 0, -(smfarclip + smnearclip) / (smfarclip - smnearclip), -1),
3518 0 : vec4<float>(0, 0, -2*smnearclip*smfarclip / (smfarclip - smnearclip), 0));
3519 :
3520 0 : if(shadowmapping == ShadowMap_Spot)
3521 : {
3522 0 : glViewport(sm.x, sm.y, sm.size, sm.size);
3523 0 : glScissor(sm.x, sm.y, sm.size, sm.size);
3524 0 : glClear(GL_DEPTH_BUFFER_BIT);
3525 :
3526 0 : float invradius = 1.0f / l.radius,
3527 0 : spotscale = invradius * cotan360(l.spot);
3528 0 : matrix4 spotmatrix(vec(l.spotx).mul(spotscale), vec(l.spoty).mul(spotscale), vec(l.dir).mul(-invradius));
3529 0 : spotmatrix.translate(vec(l.o).neg());
3530 0 : shadowmatrix.mul(smprojmatrix, spotmatrix);
3531 0 : GLOBALPARAM(shadowmatrix, shadowmatrix);
3532 :
3533 0 : glCullFace((l.dir.z >= 0) == (smcullside != 0) ? GL_BACK : GL_FRONT);
3534 :
3535 0 : shadowside = 0;
3536 :
3537 0 : if(mesh)
3538 : {
3539 0 : rendershadowmesh(mesh);
3540 : }
3541 : else
3542 : {
3543 0 : rendershadowmapworld();
3544 : }
3545 0 : batching::rendershadowmodelbatches();
3546 : }
3547 : else
3548 : {
3549 0 : if(!cachemask)
3550 : {
3551 0 : int cx1 = sidemask & 0x03 ? 0 : (sidemask & 0xC ? sm.size : 2 * sm.size),
3552 0 : cx2 = sidemask & 0x30 ? 3 * sm.size : (sidemask & 0xC ? 2 * sm.size : sm.size),
3553 0 : cy1 = sidemask & 0x15 ? 0 : sm.size,
3554 0 : cy2 = sidemask & 0x2A ? 2 * sm.size : sm.size;
3555 0 : glScissor(sm.x + cx1, sm.y + cy1, cx2 - cx1, cy2 - cy1);
3556 0 : glClear(GL_DEPTH_BUFFER_BIT);
3557 : }
3558 0 : for(int side = 0; side < 6; ++side)
3559 : {
3560 0 : if(sidemask&(1<<side))
3561 : {
3562 0 : int sidex = (side>>1)*sm.size,
3563 0 : sidey = (side&1)*sm.size;
3564 0 : glViewport(sm.x + sidex, sm.y + sidey, sm.size, sm.size);
3565 0 : glScissor(sm.x + sidex, sm.y + sidey, sm.size, sm.size);
3566 0 : if(cachemask)
3567 : {
3568 0 : glClear(GL_DEPTH_BUFFER_BIT);
3569 : }
3570 0 : matrix4 cubematrix(cubeshadowviewmatrix[side]);
3571 0 : cubematrix.scale(1.0f/l.radius);
3572 0 : cubematrix.translate(vec(l.o).neg());
3573 0 : shadowmatrix.mul(smprojmatrix, cubematrix);
3574 0 : GLOBALPARAM(shadowmatrix, shadowmatrix);
3575 :
3576 0 : glCullFace((side & 1) ^ (side >> 2) ^ smcullside ? GL_FRONT : GL_BACK);
3577 :
3578 0 : shadowside = side;
3579 :
3580 0 : if(mesh)
3581 : {
3582 0 : rendershadowmesh(mesh);
3583 : }
3584 : else
3585 : {
3586 0 : rendershadowmapworld();
3587 : }
3588 0 : batching::rendershadowmodelbatches();
3589 : }
3590 : }
3591 : }
3592 :
3593 0 : batching::clearbatchedmapmodels();
3594 : }
3595 :
3596 0 : glCullFace(GL_BACK);
3597 0 : glDisable(GL_SCISSOR_TEST);
3598 :
3599 0 : if(polyfactor || polyoffset)
3600 : {
3601 0 : glDisable(GL_POLYGON_OFFSET_FILL);
3602 : }
3603 0 : shadowmapping = 0;
3604 0 : if(inoq)
3605 : {
3606 0 : glBindFramebuffer(GL_FRAMEBUFFER, msaasamples ? msfbo : gfbo);
3607 0 : glViewport(0, 0, vieww, viewh);
3608 :
3609 0 : glFlush();
3610 : }
3611 : }
3612 :
3613 0 : void GBuffer::rendershadowatlas()
3614 : {
3615 0 : timer *smcputimer = begintimer("shadow map", false),
3616 0 : *smtimer = begintimer("shadow map");
3617 :
3618 0 : glBindFramebuffer(GL_FRAMEBUFFER, shadowatlas.fbo);
3619 0 : glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
3620 :
3621 0 : if(debugshadowatlas)
3622 : {
3623 0 : glClearDepth(0);
3624 0 : glClear(GL_DEPTH_BUFFER_BIT);
3625 0 : glClearDepth(1);
3626 : }
3627 :
3628 : // sun light
3629 0 : if(!csminoq || debugshadowatlas || inoq || !shouldworkinoq())
3630 : {
3631 0 : rendercsmshadowmaps();
3632 : }
3633 :
3634 0 : const int smoffset = shadowmaps.size();
3635 :
3636 0 : packlights();
3637 :
3638 : // point lights
3639 0 : rendershadowmaps(smoffset);
3640 :
3641 0 : glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
3642 :
3643 0 : endtimer(smtimer);
3644 0 : endtimer(smcputimer);
3645 0 : }
3646 :
3647 0 : void GBuffer::workinoq()
3648 : {
3649 0 : collectlights();
3650 :
3651 0 : if(drawtex)
3652 : {
3653 0 : return;
3654 : }
3655 :
3656 0 : if(shouldworkinoq())
3657 : {
3658 0 : inoq = true;
3659 :
3660 0 : if(csminoq && !debugshadowatlas)
3661 : {
3662 0 : rendercsmshadowmaps();
3663 : }
3664 0 : if(sminoq && !debugshadowatlas)
3665 : {
3666 0 : rendershadowmaps();
3667 : }
3668 0 : if(rhinoq)
3669 : {
3670 0 : renderradiancehints();
3671 : }
3672 :
3673 0 : inoq = false;
3674 : }
3675 : }
3676 :
3677 : static VAR(gdepthclear, 0, 1, 1); //toggles whether to clear the g depth buffer to 0000/1111 (black or white depending on gdepthformat) upon creation
3678 : static VAR(gcolorclear, 0, 1, 1); //toggles whether to clear the g buffer to 0,0,0,0 (black) upon creation
3679 :
3680 0 : void GBuffer::preparegbuffer(bool depthclear)
3681 : {
3682 0 : glBindFramebuffer(GL_FRAMEBUFFER, msaasamples && (msaalight || !drawtex) ? msfbo : gfbo);
3683 0 : glViewport(0, 0, vieww, viewh);
3684 :
3685 0 : if(drawtex && gdepthinit)
3686 : {
3687 0 : glEnable(GL_SCISSOR_TEST);
3688 0 : glScissor(0, 0, vieww, viewh);
3689 : }
3690 0 : if(gdepthformat && gdepthclear)
3691 : {
3692 0 : maskgbuffer("d");
3693 0 : if(gdepthformat == 1)
3694 : {
3695 0 : glClearColor(1, 1, 1, 1);
3696 : }
3697 : else
3698 : {
3699 0 : glClearColor(-farplane, 0, 0, 0);
3700 : }
3701 0 : glClear(GL_COLOR_BUFFER_BIT);
3702 0 : maskgbuffer("cn");
3703 : }
3704 : else
3705 : {
3706 0 : maskgbuffer("cnd");
3707 : }
3708 0 : if(gcolorclear)
3709 : {
3710 0 : glClearColor(0, 0, 0, 0);
3711 : }
3712 0 : glClear((depthclear ? GL_DEPTH_BUFFER_BIT : 0)|(gcolorclear ? GL_COLOR_BUFFER_BIT : 0)|(depthclear && ghasstencil && (!msaasamples || msaalight || ghasstencil > 1) ? GL_STENCIL_BUFFER_BIT : 0));
3713 0 : if(gdepthformat && gdepthclear)
3714 : {
3715 0 : maskgbuffer("cnd");
3716 : }
3717 0 : if(drawtex && gdepthinit)
3718 : {
3719 0 : glDisable(GL_SCISSOR_TEST);
3720 : }
3721 0 : gdepthinit = true;
3722 :
3723 0 : matrix4 invscreenmatrix,
3724 0 : invcammatrix,
3725 0 : invcamprojmatrix;
3726 0 : invcammatrix.invert(cammatrix);
3727 0 : invcamprojmatrix.invert(camprojmatrix);
3728 0 : invscreenmatrix.identity();
3729 0 : invscreenmatrix.settranslation(-1.0f, -1.0f, -1.0f);
3730 0 : invscreenmatrix.setscale(2.0f/vieww, 2.0f/viewh, 2.0f);
3731 :
3732 0 : eyematrix.muld(projmatrix.inverse(), invscreenmatrix);
3733 0 : if(drawtex == Draw_TexMinimap)
3734 : {
3735 0 : linearworldmatrix.muld(invcamprojmatrix, invscreenmatrix);
3736 0 : if(!gdepthformat)
3737 : {
3738 0 : worldmatrix = linearworldmatrix;
3739 : }
3740 0 : linearworldmatrix.a.z = invcammatrix.a.z;
3741 0 : linearworldmatrix.b.z = invcammatrix.b.z;
3742 0 : linearworldmatrix.c.z = invcammatrix.c.z;
3743 0 : linearworldmatrix.d.z = invcammatrix.d.z;
3744 0 : if(gdepthformat)
3745 : {
3746 0 : worldmatrix = linearworldmatrix;
3747 : }
3748 0 : GLOBALPARAMF(radialfogscale, 0, 0, 0, 0);
3749 : }
3750 : else
3751 : {
3752 0 : float xscale = eyematrix.a.x,
3753 0 : yscale = eyematrix.b.y,
3754 0 : xoffset = eyematrix.d.x,
3755 0 : yoffset = eyematrix.d.y,
3756 0 : zscale = eyematrix.d.z;
3757 0 : matrix4 depthmatrix(vec(xscale/zscale, 0, xoffset/zscale), vec(0, yscale/zscale, yoffset/zscale));
3758 0 : linearworldmatrix.muld(invcammatrix, depthmatrix);
3759 0 : if(gdepthformat)
3760 : {
3761 0 : worldmatrix = linearworldmatrix;
3762 : }
3763 : else
3764 : {
3765 0 : worldmatrix.muld(invcamprojmatrix, invscreenmatrix);
3766 : }
3767 :
3768 0 : GLOBALPARAMF(radialfogscale, xscale/zscale, yscale/zscale, xoffset/zscale, yoffset/zscale);
3769 : }
3770 :
3771 0 : screenmatrix.identity();
3772 0 : screenmatrix.settranslation(0.5f*vieww, 0.5f*viewh, 0.5f);
3773 0 : screenmatrix.setscale(0.5f*vieww, 0.5f*viewh, 0.5f);
3774 0 : screenmatrix.muld(camprojmatrix);
3775 :
3776 0 : GLOBALPARAMF(viewsize, vieww, viewh, 1.0f/vieww, 1.0f/viewh);
3777 0 : GLOBALPARAMF(gdepthscale, eyematrix.d.z, eyematrix.c.w, eyematrix.d.w);
3778 0 : GLOBALPARAMF(gdepthinvscale, eyematrix.d.z / eyematrix.c.w, eyematrix.d.w / eyematrix.c.w);
3779 0 : GLOBALPARAMF(gdepthpackparams, -1.0f/farplane, -255.0f/farplane, -(255.0f*255.0f)/farplane);
3780 0 : GLOBALPARAMF(gdepthunpackparams, -farplane, -farplane/255.0f, -farplane/(255.0f*255.0f));
3781 0 : GLOBALPARAM(worldmatrix, worldmatrix);
3782 :
3783 0 : GLOBALPARAMF(ldrscale, ldrscale);
3784 0 : GLOBALPARAMF(hdrgamma, hdrgamma, 1.0f/hdrgamma);
3785 0 : GLOBALPARAM(camera, camera1->o);
3786 0 : GLOBALPARAMF(millis, lastmillis/1000.0f);
3787 :
3788 0 : glerror();
3789 :
3790 0 : if(depthclear)
3791 : {
3792 0 : resetlights();
3793 : }
3794 0 : batching::resetmodelbatches();
3795 0 : }
3796 :
3797 :
3798 : //allows passing nothing to internal uses of rendergbuffer
3799 : //(the parameter is for taking a game function to be rendered onscreen)
3800 0 : void GBuffer::dummyfxn()
3801 : {
3802 0 : return;
3803 : }
3804 :
3805 0 : void GBuffer::rendergbuffer(bool depthclear, void (*gamefxn)())
3806 : {
3807 0 : timer *gcputimer = drawtex ? nullptr : begintimer("g-buffer", false),
3808 0 : *gtimer = drawtex ? nullptr : begintimer("g-buffer");
3809 :
3810 0 : preparegbuffer(depthclear);
3811 :
3812 0 : if(limitsky())
3813 : {
3814 0 : renderexplicitsky();
3815 0 : glerror();
3816 : }
3817 0 : rendergeom();
3818 0 : glerror();
3819 0 : renderdecals();
3820 0 : glerror();
3821 0 : rendermapmodels();
3822 0 : glerror();
3823 0 : gamefxn();
3824 0 : if(drawtex == Draw_TexMinimap)
3825 : {
3826 0 : if(depthclear)
3827 : {
3828 0 : findmaterials();
3829 : }
3830 0 : renderminimapmaterials();
3831 0 : glerror();
3832 : }
3833 0 : else if(!drawtex)
3834 : {
3835 0 : rendermodelbatches();
3836 0 : glerror();
3837 0 : renderstains(StainBuffer_Opaque, true);
3838 0 : renderstains(StainBuffer_Mapmodel, true);
3839 0 : glerror();
3840 : }
3841 :
3842 0 : endtimer(gtimer);
3843 0 : endtimer(gcputimer);
3844 0 : }
3845 :
3846 0 : void GBuffer::shademinimap(const vec &color)
3847 : {
3848 0 : glerror();
3849 :
3850 0 : glBindFramebuffer(GL_FRAMEBUFFER, msaalight ? mshdrfbo : hdrfbo);
3851 0 : glViewport(0, 0, vieww, viewh);
3852 :
3853 0 : if(color.x >= 0)
3854 : {
3855 0 : glClearColor(color.x, color.y, color.z, 0);
3856 0 : glClear(GL_COLOR_BUFFER_BIT);
3857 : }
3858 :
3859 0 : renderlights(-1, -1, 1, 1, nullptr, 0, msaalight ? -1 : 0);
3860 0 : glerror();
3861 0 : }
3862 :
3863 0 : void GBuffer::shademodelpreview(int x, int y, int w, int h, bool background, bool scissor)
3864 : {
3865 0 : glerror();
3866 :
3867 0 : glBindFramebuffer(GL_FRAMEBUFFER, 0);
3868 0 : glViewport(0, 0, hudw(), hudh());
3869 :
3870 0 : if(msaalight)
3871 : {
3872 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mscolortex);
3873 : }
3874 : else
3875 : {
3876 0 : glBindTexture(GL_TEXTURE_RECTANGLE, gcolortex);
3877 : }
3878 0 : glActiveTexture(GL_TEXTURE1);
3879 0 : if(msaalight)
3880 : {
3881 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msnormaltex);
3882 : }
3883 : else
3884 : {
3885 0 : glBindTexture(GL_TEXTURE_RECTANGLE, gnormaltex);
3886 : }
3887 0 : glActiveTexture(GL_TEXTURE3);
3888 0 : if(msaalight)
3889 : {
3890 0 : glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
3891 : }
3892 : else
3893 : {
3894 0 : glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
3895 : }
3896 0 : glActiveTexture(GL_TEXTURE0);
3897 :
3898 0 : float lightscale = 2.0f*ldrscale;
3899 0 : GLOBALPARAMF(lightscale, 0.1f*lightscale, 0.1f*lightscale, 0.1f*lightscale, lightscale);
3900 0 : GLOBALPARAM(sunlightdir, vec(0, -1, 2).normalize());
3901 0 : GLOBALPARAMF(sunlightcolor, 0.6f*lightscale, 0.6f*lightscale, 0.6f*lightscale);
3902 :
3903 0 : SETSHADER(modelpreview);
3904 :
3905 0 : LOCALPARAMF(cutout, background ? -1 : 0);
3906 :
3907 0 : if(scissor)
3908 : {
3909 0 : glEnable(GL_SCISSOR_TEST);
3910 : }
3911 :
3912 0 : int sx = std::clamp(x, 0, hudw()),
3913 0 : sy = std::clamp(y, 0, hudh()),
3914 0 : sw = std::clamp(x + w, 0, hudw()) - sx,
3915 0 : sh = std::clamp(y + h, 0, hudh()) - sy;
3916 0 : float sxk = 2.0f/hudw(),
3917 0 : syk = 2.0f/hudh(),
3918 0 : txk = vieww/static_cast<float>(w),
3919 0 : tyk = viewh/static_cast<float>(h);
3920 0 : hudquad(sx*sxk - 1, sy*syk - 1, sw*sxk, sh*syk, (sx-x)*txk, (sy-y)*tyk, sw*txk, sh*tyk);
3921 :
3922 0 : if(scissor)
3923 : {
3924 0 : glDisable(GL_SCISSOR_TEST);
3925 : }
3926 :
3927 0 : glerror();
3928 0 : }
3929 :
3930 0 : void GBuffer::shadesky() const
3931 : {
3932 0 : glBindFramebuffer(GL_FRAMEBUFFER, msaalight ? mshdrfbo : hdrfbo);
3933 0 : glViewport(0, 0, vieww, viewh);
3934 :
3935 0 : drawskybox((hdrclear > 0 ? hdrclear-- : msaalight) > 0);
3936 0 : }
3937 :
3938 0 : bool GBuffer::istransparentlayer() const
3939 : {
3940 0 : return transparentlayer;
3941 : }
3942 :
3943 0 : void shadegbuffer()
3944 : {
3945 0 : if(msaasamples && !msaalight && !drawtex)
3946 : {
3947 0 : gbuf.resolvemsaadepth(vieww, viewh);
3948 : }
3949 0 : glerror();
3950 :
3951 0 : timer *shcputimer = begintimer("deferred shading", false),
3952 0 : *shtimer = begintimer("deferred shading");
3953 :
3954 0 : gbuf.shadesky();
3955 :
3956 0 : if(msaasamples && (msaalight || !drawtex))
3957 : {
3958 0 : if((ghasstencil && msaaedgedetect) || msaalight==2)
3959 : {
3960 0 : for(int i = 0; i < 2; ++i)
3961 : {
3962 0 : gbuf.renderlights(-1, -1, 1, 1, nullptr, 0, i+1);
3963 : }
3964 0 : }
3965 : else
3966 : {
3967 0 : gbuf.renderlights(-1, -1, 1, 1, nullptr, 0, drawtex ? -1 : 3);
3968 : }
3969 0 : }
3970 : else
3971 : {
3972 0 : gbuf.renderlights();
3973 : }
3974 0 : glerror();
3975 :
3976 0 : if(!drawtex)
3977 : {
3978 0 : renderstains(StainBuffer_Opaque, false);
3979 0 : renderstains(StainBuffer_Mapmodel, false);
3980 : }
3981 :
3982 0 : endtimer(shtimer);
3983 0 : endtimer(shcputimer);
3984 0 : }
3985 :
3986 0 : void setuplights(GBuffer &buf)
3987 : {
3988 0 : glerror();
3989 0 : buf.setupgbuffer();
3990 0 : if(bloomw < 0 || bloomh < 0)
3991 : {
3992 0 : setupbloom(gw, gh);
3993 : }
3994 0 : if(ao && (aow < 0 || aoh < 0))
3995 : {
3996 0 : setupao(gw, gh);
3997 : }
3998 0 : if(volumetriclights && volumetric && (volw < 0 || volh < 0))
3999 : {
4000 0 : setupvolumetric(gw, gh);
4001 : }
4002 0 : if(!shadowatlas.fbo)
4003 : {
4004 0 : shadowatlas.setup();
4005 : }
4006 0 : if(useradiancehints() && !rhfbo)
4007 : {
4008 0 : setupradiancehints();
4009 : }
4010 0 : if(!deferredlightshader)
4011 : {
4012 0 : loaddeferredlightshaders();
4013 : }
4014 0 : if(drawtex == Draw_TexMinimap && !deferredminimapshader)
4015 : {
4016 0 : deferredminimapshader = loaddeferredlightshader(msaalight ? "mM" : "m");
4017 : }
4018 0 : setupaa(buf, gw, gh);
4019 0 : glerror();
4020 0 : }
4021 :
4022 0 : bool debuglights()
4023 : {
4024 0 : viewao(); //this fxn checks for the appropriate debug var
4025 0 : if(debugshadowatlas)
4026 : {
4027 0 : shadowatlas.view();
4028 : }
4029 0 : else if(debugdepth)
4030 : {
4031 0 : gbuf.viewdepth();
4032 : }
4033 0 : else if(debugstencil)
4034 : {
4035 0 : viewstencil();
4036 : }
4037 0 : else if(debugrefract)
4038 : {
4039 0 : gbuf.viewrefract();
4040 : }
4041 0 : else if(debuglightscissor)
4042 : {
4043 0 : viewlightscissor();
4044 : }
4045 0 : else if(debugrsm)
4046 : {
4047 0 : viewrsm();
4048 : }
4049 0 : else if(debugrh)
4050 : {
4051 0 : viewrh();
4052 : }
4053 0 : else if(!debugaa())
4054 : {
4055 0 : return false;
4056 : }
4057 0 : return true;
4058 : }
4059 :
4060 0 : void cleanuplights()
4061 : {
4062 0 : gbuf.cleanupgbuffer();
4063 0 : cleanupbloom();
4064 0 : cleanupao();
4065 0 : cleanupvolumetric();
4066 0 : shadowatlas.cleanup();
4067 0 : cleanupradiancehints();
4068 0 : lightsphere::cleanup();
4069 0 : cleanupaa();
4070 0 : }
4071 :
4072 1 : int GBuffer::getlightdebuginfo(uint type) const
4073 : {
4074 1 : switch(type)
4075 : {
4076 1 : case 0:
4077 : {
4078 1 : return lightpassesused;
4079 : }
4080 0 : case 1:
4081 : {
4082 0 : return lightsvisible;
4083 : }
4084 0 : case 2:
4085 : {
4086 0 : return lightsoccluded;
4087 : }
4088 0 : case 3:
4089 : {
4090 0 : return lightbatchesused;
4091 : }
4092 0 : case 4:
4093 : {
4094 0 : return lightbatchrectsused;
4095 : }
4096 0 : case 5:
4097 : {
4098 0 : return lightbatchstacksused;
4099 : }
4100 0 : default:
4101 : {
4102 0 : return -1;
4103 : }
4104 : }
4105 : }
4106 :
4107 1 : void initrenderlightscmds()
4108 : {
4109 2 : addcommand("usepacknorm", reinterpret_cast<identfun>(+[](){intret(usepacknorm() ? 1 : 0);}), "", Id_Command);
4110 2 : addcommand("lightdebuginfo", reinterpret_cast<identfun>(+[] (int * index) {intret(gbuf.getlightdebuginfo(static_cast<uint>(*index)));} ), "i", Id_Command);
4111 2 : addcommand("getcsmproperty", reinterpret_cast<identfun>(+[] (int * index) {floatret(csm.getcsmproperty(*index));} ), "i", Id_Command);
4112 2 : addcommand("setcsmproperty", reinterpret_cast<identfun>(+[] (int * index, float * value) {intret(csm.setcsmproperty(*index, *value));} ), "if", Id_Command);
4113 1 : }
|