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