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