1 #include "engine.h"
2 
3 int gw = -1, gh = -1, bloomw = -1, bloomh = -1, lasthdraccum = 0;
4 GLuint gfbo = 0, gdepthtex = 0, gcolortex = 0, gnormaltex = 0, gglowtex = 0, gdepthrb = 0, gstencilrb = 0;
5 bool gdepthinit = false;
6 int scalew = -1, scaleh = -1;
7 GLuint scalefbo[2] = { 0, 0 }, scaletex[2] = { 0, 0 };
8 GLuint hdrfbo = 0, hdrtex = 0, bloompbo = 0, bloomfbo[6] = { 0, 0, 0, 0, 0, 0 }, bloomtex[6] = { 0, 0, 0, 0, 0, 0 };
9 int hdrclear = 0;
10 GLuint refractfbo = 0, refracttex = 0;
11 GLenum bloomformat = 0, hdrformat = 0, stencilformat = 0;
12 bool hdrfloat = false;
13 GLuint msfbo = 0, msdepthtex = 0, mscolortex = 0, msnormaltex = 0, msglowtex = 0, msdepthrb = 0, msstencilrb = 0, mshdrfbo = 0, mshdrtex = 0, msrefractfbo = 0, msrefracttex = 0;
14 vector<vec2> msaapositions;
15 int aow = -1, aoh = -1;
16 GLuint aofbo[4] = { 0, 0, 0, 0 }, aotex[4] = { 0, 0, 0, 0 }, aonoisetex = 0;
17 matrix4 eyematrix, worldmatrix, linearworldmatrix, screenmatrix;
18 
19 extern int amd_pf_bug;
20 
gethdrformat(int prec,int fallback=GL_RGB)21 int gethdrformat(int prec, int fallback = GL_RGB)
22 {
23     if(prec >= 3 && hasTF) return GL_RGB16F;
24     if(prec >= 2 && hasPF && !amd_pf_bug) return GL_R11F_G11F_B10F;
25     if(prec >= 1) return GL_RGB10;
26     return fallback;
27 }
28 
29 extern int bloomsize, bloomprec;
30 
setupbloom(int w,int h)31 void setupbloom(int w, int h)
32 {
33     int maxsize = ((1<<bloomsize)*5)/4;
34     while(w >= maxsize || h >= maxsize)
35     {
36         w /= 2;
37         h /= 2;
38     }
39     if(w == bloomw && h == bloomh) return;
40     bloomw = w;
41     bloomh = h;
42 
43     loopi(5) if(!bloomtex[i]) glGenTextures(1, &bloomtex[i]);
44 
45     loopi(5) if(!bloomfbo[i]) glGenFramebuffers_(1, &bloomfbo[i]);
46 
47     bloomformat = gethdrformat(bloomprec);
48     createtexture(bloomtex[0], max(gw/2, bloomw), max(gh/2, bloomh), NULL, 3, 1, bloomformat, GL_TEXTURE_RECTANGLE);
49     createtexture(bloomtex[1], max(gw/4, bloomw), max(gh/4, bloomh), NULL, 3, 1, bloomformat, GL_TEXTURE_RECTANGLE);
50     createtexture(bloomtex[2], bloomw, bloomh, NULL, 3, 1, GL_RGB, GL_TEXTURE_RECTANGLE);
51     createtexture(bloomtex[3], bloomw, bloomh, NULL, 3, 1, GL_RGB, GL_TEXTURE_RECTANGLE);
52     if(bloomformat != GL_RGB)
53     {
54         if(!bloomtex[5]) glGenTextures(1, &bloomtex[5]);
55         if(!bloomfbo[5]) glGenFramebuffers_(1, &bloomfbo[5]);
56         createtexture(bloomtex[5], bloomw, bloomh, NULL, 3, 1, bloomformat, GL_TEXTURE_RECTANGLE);
57     }
58 
59     if(hwvtexunits < 4)
60     {
61         glGenBuffers_(1, &bloompbo);
62         glBindBuffer_(GL_PIXEL_PACK_BUFFER, bloompbo);
63         glBufferData_(GL_PIXEL_PACK_BUFFER, 4*(hasTF ? sizeof(GLfloat) : sizeof(GLushort))*(hasTRG ? 1 : 3), NULL, GL_DYNAMIC_COPY);
64         glBindBuffer_(GL_PIXEL_PACK_BUFFER, 0);
65     }
66 
67     static const uchar gray[12] = { 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32 };
68     static const float grayf[12] = { 0.125f, 0.125f, 0.125f, 0.125f, 0.125f, 0.125f, 0.125f, 0.125f, 0.125f, 0.125f, 0.125f, 0.125f };
69     createtexture(bloomtex[4], bloompbo ? 4 : 1, 1, hasTF ? (const void *)grayf : (const void *)gray, 3, 1, hasTF ? (hasTRG ? GL_R16F : GL_RGB16F) : (hasTRG ? GL_R16 : GL_RGB16));
70 
71     loopi(5 + (bloomformat != GL_RGB ? 1 : 0))
72     {
73         glBindFramebuffer_(GL_FRAMEBUFFER, bloomfbo[i]);
74         glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, i==4 ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE, bloomtex[i], 0);
75 
76         if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
77             fatal("failed allocating bloom buffer!");
78     }
79 
80     glBindFramebuffer_(GL_FRAMEBUFFER, 0);
81 }
82 
cleanupbloom()83 void cleanupbloom()
84 {
85     if(bloompbo) { glDeleteBuffers_(1, &bloompbo); bloompbo = 0; }
86     loopi(6) if(bloomfbo[i]) { glDeleteFramebuffers_(1, &bloomfbo[i]); bloomfbo[i] = 0; }
87     loopi(6) if(bloomtex[i]) { glDeleteTextures(1, &bloomtex[i]); bloomtex[i] = 0; }
88     bloomw = bloomh = -1;
89     lasthdraccum = 0;
90 }
91 
92 extern int ao, aotaps, aoreduce, aoreducedepth, aonoise, aobilateral, aobilateralupscale, aopackdepth, aodepthformat, aoprec, aoderivnormal;
93 
94 static Shader *bilateralshader[2] = { NULL, NULL };
95 
loadbilateralshader(int pass)96 Shader *loadbilateralshader(int pass)
97 {
98     if(!aobilateral) return nullshader;
99 
100     string opts;
101     int optslen = 0;
102 
103     bool linear = aoreducedepth && (aoreduce || aoreducedepth > 1),
104          upscale = aoreduce && aobilateralupscale,
105          reduce = aoreduce && (upscale || (!linear && !aopackdepth));
106     if(reduce)
107     {
108         opts[optslen++] = 'r';
109         opts[optslen++] = '0' + aoreduce;
110     }
111     if(upscale) opts[optslen++] = 'u';
112     else if(linear) opts[optslen++] = 'l';
113     if(aopackdepth) opts[optslen++] = 'p';
114     opts[optslen] = '\0';
115 
116     defformatstring(name, "bilateral%c%s%d", 'x' + pass, opts, aobilateral);
117     return generateshader(name, "bilateralshader \"%s\" %d %d", opts, aobilateral, reduce ? aoreduce : 0);
118 }
119 
loadbilateralshaders()120 void loadbilateralshaders()
121 {
122     loopk(2) bilateralshader[k] = loadbilateralshader(k);
123 }
124 
clearbilateralshaders()125 void clearbilateralshaders()
126 {
127     loopk(2) bilateralshader[k] = NULL;
128 }
129 
setbilateralshader(int radius,int pass,float depth)130 void setbilateralshader(int radius, int pass, float depth)
131 {
132     bilateralshader[pass]->set();
133     float sigma = blursigma*2*radius;
134     LOCALPARAMF(bilateralparams, 1.0f/(M_LN2*2*sigma*sigma), 1.0f/(M_LN2*depth*depth));
135 }
136 
137 static Shader *ambientobscuranceshader = NULL;
138 
loadambientobscuranceshader()139 Shader *loadambientobscuranceshader()
140 {
141     string opts;
142     int optslen = 0;
143 
144     bool linear = aoreducedepth && (aoreduce || aoreducedepth > 1);
145     if(linear) opts[optslen++] = 'l';
146     if(aoderivnormal) opts[optslen++] = 'd';
147     if(aobilateral && aopackdepth) opts[optslen++] = 'p';
148     opts[optslen] = '\0';
149 
150     defformatstring(name, "ambientobscurance%s%d", opts, aotaps);
151     return generateshader(name, "ambientobscuranceshader \"%s\" %d", opts, aotaps);
152 }
153 
loadaoshaders()154 void loadaoshaders()
155 {
156     ambientobscuranceshader = loadambientobscuranceshader();
157 }
158 
clearaoshaders()159 void clearaoshaders()
160 {
161     ambientobscuranceshader = NULL;
162 }
163 
setupao(int w,int h)164 void setupao(int w, int h)
165 {
166     int sw = w>>aoreduce, sh = h>>aoreduce;
167 
168     if(sw == aow && sh == aoh) return;
169 
170     aow = sw;
171     aoh = sh;
172 
173     if(!aonoisetex) glGenTextures(1, &aonoisetex);
174     bvec *noise = new bvec[(1<<aonoise)*(1<<aonoise)];
175     loopk((1<<aonoise)*(1<<aonoise)) noise[k] = bvec(vec(rndscale(2)-1, rndscale(2)-1, 0).normalize());
176     createtexture(aonoisetex, 1<<aonoise, 1<<aonoise, noise, 0, 0, GL_RGB, GL_TEXTURE_2D);
177     delete[] noise;
178 
179     bool upscale = aoreduce && aobilateral && aobilateralupscale;
180     GLenum format = aoprec && hasTRG ? GL_R8 : GL_RGBA8,
181            packformat = aobilateral && aopackdepth ? (aodepthformat ? GL_RG16F : GL_RGBA8) : format;
182     int packfilter = upscale && aopackdepth && !aodepthformat ? 0 : 1;
183     loopi(upscale ? 3 : 2)
184     {
185         if(!aotex[i]) glGenTextures(1, &aotex[i]);
186         if(!aofbo[i]) glGenFramebuffers_(1, &aofbo[i]);
187         createtexture(aotex[i], upscale && i ? w : aow, upscale && i >= 2 ? h : aoh, NULL, 3, i < 2 ? packfilter : 1, i < 2 ? packformat : format, GL_TEXTURE_RECTANGLE);
188         glBindFramebuffer_(GL_FRAMEBUFFER, aofbo[i]);
189         glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, aotex[i], 0);
190         if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
191             fatal("failed allocating AO buffer!");
192         if(!upscale && packformat == GL_RG16F)
193         {
194             glClearColor(0, 0, 0, 0);
195             glClear(GL_COLOR_BUFFER_BIT);
196         }
197     }
198 
199     if(aoreducedepth && (aoreduce || aoreducedepth > 1))
200     {
201         if(!aotex[3]) glGenTextures(1, &aotex[3]);
202         if(!aofbo[3]) glGenFramebuffers_(1, &aofbo[3]);
203         createtexture(aotex[3], aow, aoh, NULL, 3, 0, aodepthformat > 1 ? GL_R32F : (aodepthformat ? GL_R16F : GL_RGBA8), GL_TEXTURE_RECTANGLE);
204         glBindFramebuffer_(GL_FRAMEBUFFER, aofbo[3]);
205         glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, aotex[3], 0);
206         if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
207             fatal("failed allocating AO buffer!");
208     }
209 
210     glBindFramebuffer_(GL_FRAMEBUFFER, 0);
211 
212     loadaoshaders();
213     loadbilateralshaders();
214 }
215 
cleanupao()216 void cleanupao()
217 {
218     loopi(4) if(aofbo[i]) { glDeleteFramebuffers_(1, &aofbo[i]); aofbo[i] = 0; }
219     loopi(4) if(aotex[i]) { glDeleteTextures(1, &aotex[i]); aotex[i] = 0; }
220     if(aonoisetex) { glDeleteTextures(1, &aonoisetex); aonoisetex = 0; }
221     aow = bloomh = -1;
222 
223     clearaoshaders();
224     clearbilateralshaders();
225 }
226 
227 VARFP(ao, 0, 1, 1, { cleanupao(); cleardeferredlightshaders(); });
228 FVARR(aoradius, 0, 5, 256);
229 FVAR(aocutoff, 0, 2.0f, 1e3f);
230 FVARR(aodark, 1e-3f, 11.0f, 1e3f);
231 FVARR(aosharp, 1e-3f, 1, 1e3f);
232 FVAR(aoprefilterdepth, 0, 1, 1e3);
233 FVARR(aomin, 0, 0.25f, 1);
234 VARFR(aosun, 0, 1, 1, cleardeferredlightshaders());
235 FVARR(aosunmin, 0, 0.5f, 1);
236 VARP(aoblur, 0, 4, 7);
237 VARP(aoiter, 0, 0, 4);
238 VARFP(aoreduce, 0, 1, 2, cleanupao());
239 VARF(aoreducedepth, 0, 1, 2, cleanupao());
240 VARFP(aofloatdepth, 0, 1, 2, initwarning("AO setup", INIT_LOAD, CHANGE_SHADERS));
241 VARFP(aoprec, 0, 1, 1, cleanupao());
242 VAR(aodepthformat, 1, 0, 0);
243 VARF(aonoise, 0, 5, 8, cleanupao());
244 VARFP(aobilateral, 0, 3, 10, cleanupao());
245 FVARP(aobilateraldepth, 0, 4, 1e3f);
246 VARFP(aobilateralupscale, 0, 0, 1, cleanupao());
247 VARF(aopackdepth, 0, 1, 1, cleanupao());
248 VARFP(aotaps, 1, 5, 12, cleanupao());
249 VARF(aoderivnormal, 0, 0, 1, cleanupao());
250 VAR(debugao, 0, 0, 1);
251 
initao()252 void initao()
253 {
254     aodepthformat = aofloatdepth && hasTRG && hasTF ? aofloatdepth : 0;
255 }
256 
viewao()257 void viewao()
258 {
259     if(!ao) return;
260     int w = min(hudw, hudh)/2, h = (w*hudh)/hudw;
261     SETSHADER(hudrect);
262     gle::colorf(1, 1, 1);
263     glBindTexture(GL_TEXTURE_RECTANGLE, aotex[2] ? aotex[2] : aotex[0]);
264     int tw = aotex[2] ? gw : aow, th = aotex[2] ? gh : aoh;
265     debugquad(0, 0, w, h, 0, 0, tw, th);
266 }
267 
renderao()268 void renderao()
269 {
270     if(!ao) return;
271 
272     timer *aotimer = begintimer("ambient obscurance");
273 
274     if(msaasamples) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
275     else glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
276 
277     bool linear = aoreducedepth && (aoreduce || aoreducedepth > 1);
278     float xscale = eyematrix.a.x, yscale = eyematrix.b.y;
279     if(linear)
280     {
281         glBindFramebuffer_(GL_FRAMEBUFFER, aofbo[3]);
282         glViewport(0, 0, aow, aoh);
283         SETSHADER(linearizedepth);
284         screenquad(vieww, viewh);
285 
286         xscale *= float(vieww)/aow;
287         yscale *= float(viewh)/aoh;
288 
289         glBindTexture(GL_TEXTURE_RECTANGLE, aotex[3]);
290     }
291 
292     ambientobscuranceshader->set();
293 
294     glBindFramebuffer_(GL_FRAMEBUFFER, aofbo[0]);
295     glViewport(0, 0, aow, aoh);
296     glActiveTexture_(GL_TEXTURE1);
297     if(aoderivnormal)
298     {
299         if(msaasamples) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
300         else glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
301     }
302     else
303     {
304         if(msaasamples) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msnormaltex);
305         else glBindTexture(GL_TEXTURE_RECTANGLE, gnormaltex);
306         LOCALPARAM(normalmatrix, matrix3(cammatrix));
307     }
308     glActiveTexture_(GL_TEXTURE2);
309     glBindTexture(GL_TEXTURE_2D, aonoisetex);
310     glActiveTexture_(GL_TEXTURE0);
311 
312     LOCALPARAMF(tapparams, aoradius*eyematrix.d.z/xscale, aoradius*eyematrix.d.z/yscale, aoradius*aoradius*aocutoff*aocutoff);
313     LOCALPARAMF(contrastparams, (2.0f*aodark)/aotaps, aosharp);
314     LOCALPARAMF(offsetscale, xscale/eyematrix.d.z, yscale/eyematrix.d.z, eyematrix.d.x/eyematrix.d.z, eyematrix.d.y/eyematrix.d.z);
315     LOCALPARAMF(prefilterdepth, aoprefilterdepth);
316     screenquad(vieww, viewh, aow/float(1<<aonoise), aoh/float(1<<aonoise));
317 
318     if(aobilateral)
319     {
320         if(aoreduce && aobilateralupscale) loopi(2)
321         {
322             setbilateralshader(aobilateral, i, aobilateraldepth);
323             glBindFramebuffer_(GL_FRAMEBUFFER, aofbo[i+1]);
324             glViewport(0, 0, vieww, i ? viewh : aoh);
325             glBindTexture(GL_TEXTURE_RECTANGLE, aotex[i]);
326             glActiveTexture_(GL_TEXTURE1);
327             if(msaasamples) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
328             else glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
329             glActiveTexture_(GL_TEXTURE0);
330             screenquad(vieww, viewh, i ? vieww : aow, aoh);
331         }
332         else loopi(2 + 2*aoiter)
333         {
334             setbilateralshader(aobilateral, i%2, aobilateraldepth);
335             glBindFramebuffer_(GL_FRAMEBUFFER, aofbo[(i+1)%2]);
336             glViewport(0, 0, aow, aoh);
337             glBindTexture(GL_TEXTURE_RECTANGLE, aotex[i%2]);
338             glActiveTexture_(GL_TEXTURE1);
339             if(linear) glBindTexture(GL_TEXTURE_RECTANGLE, aotex[3]);
340             else if(msaasamples) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
341             else glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
342             glActiveTexture_(GL_TEXTURE0);
343             screenquad(vieww, viewh);
344         }
345     }
346     else if(aoblur)
347     {
348         float blurweights[MAXBLURRADIUS+1], bluroffsets[MAXBLURRADIUS+1];
349         setupblurkernel(aoblur, blurweights, bluroffsets);
350         loopi(2 + 2*aoiter)
351         {
352             glBindFramebuffer_(GL_FRAMEBUFFER, aofbo[(i+1)%2]);
353             glViewport(0, 0, aow, aoh);
354             setblurshader(i%2, 1, aoblur, blurweights, bluroffsets, GL_TEXTURE_RECTANGLE);
355             glBindTexture(GL_TEXTURE_RECTANGLE, aotex[i%2]);
356             screenquad(aow, aoh);
357         }
358     }
359 
360     glBindFramebuffer_(GL_FRAMEBUFFER, msaasamples ? msfbo : gfbo);
361     glViewport(0, 0, vieww, viewh);
362 
363     endtimer(aotimer);
364 }
365 
cleanupscale()366 void cleanupscale()
367 {
368     loopi(2) if(scalefbo[i]) { glDeleteFramebuffers_(1, &scalefbo[i]); scalefbo[i] = 0; }
369     loopi(2) if(scaletex[i]) { glDeleteTextures(1, &scaletex[i]); scaletex[i] = 0; }
370     scalew = scaleh = -1;
371 }
372 
373 extern int gscalecubic, gscalenearest;
374 
setupscale(int sw,int sh,int w,int h)375 void setupscale(int sw, int sh, int w, int h)
376 {
377     scalew = w;
378     scaleh = h;
379 
380     loopi(gscalecubic ? 2 : 1)
381     {
382         if(!scaletex[i]) glGenTextures(1, &scaletex[i]);
383         if(!scalefbo[i]) glGenFramebuffers_(1, &scalefbo[i]);
384 
385         glBindFramebuffer_(GL_FRAMEBUFFER, scalefbo[i]);
386 
387         createtexture(scaletex[i], sw, i ? h : sh, NULL, 3, gscalecubic || !gscalenearest ? 1 : 0, GL_RGB, GL_TEXTURE_RECTANGLE);
388 
389         glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, scaletex[i], 0);
390         if(!i) bindgdepth();
391 
392         if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
393             fatal("failed allocating scale buffer!");
394     }
395 
396     glBindFramebuffer_(GL_FRAMEBUFFER, 0);
397 
398     if(gscalecubic)
399     {
400         useshaderbyname("scalecubicx");
401         useshaderbyname("scalecubicy");
402     }
403 }
404 
shouldscale()405 GLuint shouldscale()
406 {
407     return scalefbo[0];
408 }
409 
doscale(GLuint outfbo)410 void doscale(GLuint outfbo)
411 {
412     if(!scaletex[0]) return;
413 
414     timer *scaletimer = begintimer("scaling");
415 
416     if(gscalecubic)
417     {
418         glBindFramebuffer_(GL_FRAMEBUFFER, scalefbo[1]);
419         glViewport(0, 0, gw, hudh);
420         glBindTexture(GL_TEXTURE_RECTANGLE, scaletex[0]);
421         SETSHADER(scalecubicy);
422         screenquad(gw, gh);
423         glBindFramebuffer_(GL_FRAMEBUFFER, outfbo);
424         glViewport(0, 0, hudw, hudh);
425         glBindTexture(GL_TEXTURE_RECTANGLE, scaletex[1]);
426         SETSHADER(scalecubicx);
427         screenquad(gw, hudh);
428     }
429     else
430     {
431         glBindFramebuffer_(GL_FRAMEBUFFER, outfbo);
432         glViewport(0, 0, hudw, hudh);
433         glBindTexture(GL_TEXTURE_RECTANGLE, scaletex[0]);
434         SETSHADER(scalelinear);
435         screenquad(gw, gh);
436     }
437 
438     endtimer(scaletimer);
439 }
440 
441 VARFP(glineardepth, 0, 0, 3, initwarning("g-buffer setup", INIT_LOAD, CHANGE_SHADERS));
442 VAR(gdepthformat, 1, 0, 0);
443 VARF(gstencil, 0, 0, 1, initwarning("g-buffer setup", INIT_LOAD, CHANGE_SHADERS));
444 VARF(gdepthstencil, 0, 2, 2, initwarning("g-buffer setup", INIT_LOAD, CHANGE_SHADERS));
445 VAR(ghasstencil, 1, 0, 0);
446 VARFP(msaa, 0, 0, 16, initwarning("MSAA setup", INIT_LOAD, CHANGE_SHADERS));
447 VARFP(csaa, 0, 0, 16, initwarning("MSAA setup", INIT_LOAD, CHANGE_SHADERS));
448 VARF(msaadepthstencil, 0, 2, 2, initwarning("MSAA setup", INIT_LOAD, CHANGE_SHADERS));
449 VARF(msaastencil, 0, 0, 1, initwarning("MSAA setup", INIT_LOAD, CHANGE_SHADERS));
450 VARF(msaaedgedetect, 0, 1, 1, cleanupgbuffer());
451 VARFP(msaalineardepth, -1, -1, 3, initwarning("MSAA setup", INIT_LOAD, CHANGE_SHADERS));
452 VARFP(msaatonemap, 0, 0, 1, cleanupgbuffer());
453 VARF(msaatonemapblit, 0, 0, 1, cleanupgbuffer());
454 VAR(msaamaxsamples, 1, 0, 0);
455 VAR(msaamaxdepthtexsamples, 1, 0, 0);
456 VAR(msaamaxcolortexsamples, 1, 0, 0);
457 VAR(msaaminsamples, 1, 0, 0);
458 VAR(msaasamples, 1, 0, 0);
459 VAR(msaamincolorsamples, 1, 0, 0);
460 VAR(msaacolorsamples, 1, 0, 0);
461 
checkmsaasamples()462 void checkmsaasamples()
463 {
464     GLuint tex;
465     glGenTextures(1, &tex);
466     glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, tex);
467 
468     GLint samples, colorsamples;
469     if(msaamincolorsamples < msaaminsamples)
470     {
471         glTexImage2DMultisampleCoverageNV_(GL_TEXTURE_2D_MULTISAMPLE, msaaminsamples, msaamincolorsamples, GL_RGBA8, 1, 1, GL_TRUE);
472         glGetTexLevelParameteriv(GL_TEXTURE_2D_MULTISAMPLE, 0, GL_TEXTURE_COVERAGE_SAMPLES_NV, &samples);
473         glGetTexLevelParameteriv(GL_TEXTURE_2D_MULTISAMPLE, 0, GL_TEXTURE_COLOR_SAMPLES_NV, &colorsamples);
474         msaasamples = samples;
475         msaacolorsamples = colorsamples;
476     }
477     else
478     {
479         glTexImage2DMultisample_(GL_TEXTURE_2D_MULTISAMPLE, msaaminsamples, GL_RGBA8, 1, 1, GL_TRUE);
480         glGetTexLevelParameteriv(GL_TEXTURE_2D_MULTISAMPLE, 0, GL_TEXTURE_SAMPLES, &samples);
481         msaacolorsamples = msaasamples = samples;
482     }
483 
484     glDeleteTextures(1, &tex);
485 }
486 
initgbuffer()487 void initgbuffer()
488 {
489     msaamaxsamples = msaamaxdepthtexsamples = msaamaxcolortexsamples = msaaminsamples = msaasamples = msaamincolorsamples = msaacolorsamples = 0;
490     msaapositions.setsize(0);
491 
492     if(hasFBMS && hasFBB && hasTMS)
493     {
494         GLint val;
495         glGetIntegerv(GL_MAX_SAMPLES, &val);
496         msaamaxsamples = val;
497         glGetIntegerv(GL_MAX_DEPTH_TEXTURE_SAMPLES, &val);
498         msaamaxdepthtexsamples = val;
499         glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &val);
500         msaamaxcolortexsamples = val;
501     }
502 
503     int maxsamples = min(msaamaxsamples, msaamaxcolortexsamples), reqsamples = min(max(msaa, csaa), maxsamples);
504     if(reqsamples >= 2)
505     {
506         msaaminsamples = 2;
507         while(msaaminsamples*2 <= reqsamples) msaaminsamples *= 2;
508         if(hasNVFBMSC && hasNVTMS)
509         {
510             if(msaa)
511             {
512                 int colorsamples = min(msaa, maxsamples);
513                 msaamincolorsamples = 2;
514                 while(msaamincolorsamples*2 <= colorsamples) msaamincolorsamples *= 2;
515             }
516         }
517         else msaamincolorsamples = msaaminsamples;
518     }
519 
520     int lineardepth = glineardepth;
521     if(msaaminsamples)
522     {
523         if(msaamaxdepthtexsamples < msaaminsamples)
524         {
525             if(msaalineardepth > 0) lineardepth = msaalineardepth;
526             else if(!lineardepth) lineardepth = 1;
527         }
528         else if(msaalineardepth >= 0) lineardepth = msaalineardepth;
529     }
530 
531     if(lineardepth > 1 && (!hasAFBO || !hasTF || !hasTRG)) gdepthformat = 1;
532     else gdepthformat = lineardepth;
533 
534     if(msaaminsamples)
535     {
536         ghasstencil = (msaadepthstencil > 1 || (msaadepthstencil && gdepthformat)) && hasDS ? 2 : (msaastencil ? 1 : 0);
537 
538         checkmsaasamples();
539     }
540     else ghasstencil = (gdepthstencil > 1 || (gdepthstencil && gdepthformat)) && hasDS ? 2 : (gstencil ? 1 : 0);
541 
542     initao();
543 }
544 
545 VARF(forcepacknorm, 0, 0, 1, initwarning("g-buffer setup", INIT_LOAD, CHANGE_SHADERS));
546 
usepacknorm()547 bool usepacknorm() { return forcepacknorm || msaasamples || (!useavatarmask() && gdepthformat!=1); }
548 ICOMMAND(usepacknorm, "", (), intret(usepacknorm() ? 1 : 0));
549 
maskgbuffer(const char * mask)550 void maskgbuffer(const char *mask)
551 {
552     GLenum drawbufs[4];
553     int numbufs = 0;
554     while(*mask) switch(*mask++)
555     {
556         case 'c': drawbufs[numbufs++] = GL_COLOR_ATTACHMENT0; break;
557         case 'n': drawbufs[numbufs++] = GL_COLOR_ATTACHMENT1; break;
558         case 'd': if(gdepthformat) drawbufs[numbufs++] = GL_COLOR_ATTACHMENT3; break;
559         case 'g': drawbufs[numbufs++] = GL_COLOR_ATTACHMENT2; break;
560     }
561     glDrawBuffers_(numbufs, drawbufs);
562 }
563 
564 extern int hdrprec, gscale;
565 
cleanupmsbuffer()566 void cleanupmsbuffer()
567 {
568     if(msfbo) { glDeleteFramebuffers_(1, &msfbo); msfbo = 0; }
569     if(msdepthtex) { glDeleteTextures(1, &msdepthtex); msdepthtex = 0; }
570     if(mscolortex) { glDeleteTextures(1, &mscolortex); mscolortex = 0; }
571     if(msnormaltex) { glDeleteTextures(1, &msnormaltex); msnormaltex = 0; }
572     if(msglowtex) { glDeleteTextures(1, &msglowtex); msglowtex = 0; }
573     if(msstencilrb) { glDeleteRenderbuffers_(1, &msstencilrb); msstencilrb = 0; }
574     if(msdepthrb) { glDeleteRenderbuffers_(1, &msdepthrb); msdepthrb = 0; }
575     if(mshdrfbo) { glDeleteFramebuffers_(1, &mshdrfbo); mshdrfbo = 0; }
576     if(mshdrtex) { glDeleteTextures(1, &mshdrtex); mshdrtex = 0; }
577     if(msrefractfbo) { glDeleteFramebuffers_(1, &msrefractfbo); msrefractfbo = 0; }
578     if(msrefracttex) { glDeleteTextures(1, &msrefracttex); msrefracttex = 0; }
579     msaacolorsamples = 0;
580 }
581 
bindmsdepth()582 void bindmsdepth()
583 {
584     if(gdepthformat)
585     {
586         glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, msdepthrb);
587         if(ghasstencil > 1) glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, msdepthrb);
588         else if(ghasstencil) glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, msstencilrb);
589     }
590     else
591     {
592         glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, msdepthtex, 0);
593         if(ghasstencil > 1) glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, msdepthtex, 0);
594         else if(ghasstencil) glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, msstencilrb);
595     }
596 }
597 
texms(GLenum format,int w,int h)598 static void texms(GLenum format, int w, int h)
599 {
600     if(msaacolorsamples < msaasamples)
601         glTexImage2DMultisampleCoverageNV_(GL_TEXTURE_2D_MULTISAMPLE, msaasamples, msaacolorsamples, format, w, h, GL_TRUE);
602     else
603         glTexImage2DMultisample_(GL_TEXTURE_2D_MULTISAMPLE, msaasamples, format, w, h, GL_TRUE);
604 }
605 
rbms(GLenum format,int w,int h)606 static void rbms(GLenum format, int w, int h)
607 {
608     if(msaacolorsamples < msaasamples)
609         glRenderbufferStorageMultisampleCoverageNV_(GL_RENDERBUFFER, msaasamples, msaacolorsamples, format, w, h);
610     else
611         glRenderbufferStorageMultisample_(GL_RENDERBUFFER, msaasamples, format, w, h);
612 }
613 
setupmsbuffer(int w,int h)614 void setupmsbuffer(int w, int h)
615 {
616     if(!msdepthtex) glGenTextures(1, &msdepthtex);
617     if(!mshdrtex) glGenTextures(1, &mshdrtex);
618     if(!mshdrfbo) glGenFramebuffers_(1, &mshdrfbo);
619 
620     glBindFramebuffer_(GL_FRAMEBUFFER, mshdrfbo);
621 
622     stencilformat = ghasstencil > 1 ? GL_DEPTH24_STENCIL8 : (ghasstencil ? GL_STENCIL_INDEX8 : 0);
623 
624     if(gdepthformat)
625     {
626         if(!msdepthrb) glGenRenderbuffers_(1, &msdepthrb);
627         glBindRenderbuffer_(GL_RENDERBUFFER, msdepthrb);
628         rbms(ghasstencil > 1 ? stencilformat : GL_DEPTH_COMPONENT, w, h);
629         glBindRenderbuffer_(GL_RENDERBUFFER, 0);
630     }
631     if(ghasstencil == 1)
632     {
633         if(!msstencilrb) glGenRenderbuffers_(1, &msstencilrb);
634         glBindRenderbuffer_(GL_RENDERBUFFER, msstencilrb);
635         rbms(GL_STENCIL_INDEX8, w, h);
636         glBindRenderbuffer_(GL_RENDERBUFFER, 0);
637     }
638 
639     static const GLenum depthformats[] = { GL_RGBA8, GL_R16F, GL_R32F };
640     GLenum depthformat = gdepthformat ? depthformats[gdepthformat-1] : (ghasstencil > 1 ? stencilformat : GL_DEPTH_COMPONENT);
641     glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
642     texms(depthformat, w, h);
643 
644     bindmsdepth();
645 
646     hdrformat = 0;
647     for(int prec = hdrprec; prec >= 0; prec--)
648     {
649         GLenum format = gethdrformat(prec);
650         glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mshdrtex);
651         glGetError();
652         texms(format, w, h);
653         if(glGetError() == GL_NO_ERROR)
654         {
655             glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, mshdrtex, 0);
656             if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
657             {
658                 hdrformat = format;
659                 break;
660             }
661         }
662     }
663 
664     if(!hdrformat || glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
665         fatal("failed allocating MSAA HDR buffer!");
666 
667     if(!mscolortex) glGenTextures(1, &mscolortex);
668     if(!msnormaltex) glGenTextures(1, &msnormaltex);
669     if(!msglowtex) glGenTextures(1, &msglowtex);
670     if(!msfbo) glGenFramebuffers_(1, &msfbo);
671 
672     glBindFramebuffer_(GL_FRAMEBUFFER, msfbo);
673 
674     maskgbuffer("cndg");
675 
676     glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mscolortex);
677     texms(GL_RGBA8, w, h);
678     glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msnormaltex);
679     texms(GL_RGBA8, w, h);
680     glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msglowtex);
681     texms(hasAFBO ? hdrformat : GL_RGBA8, w, h);
682 
683     bindmsdepth();
684     glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, mscolortex, 0);
685     glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D_MULTISAMPLE, msnormaltex, 0);
686     glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D_MULTISAMPLE, msglowtex, 0);
687     if(gdepthformat) glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D_MULTISAMPLE, msdepthtex, 0);
688 
689     if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
690     {
691         if(hasAFBO)
692         {
693             glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msglowtex);
694             texms(hasAFBO ? hdrformat : GL_RGBA8, w, h);
695             glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D_MULTISAMPLE, msglowtex, 0);
696             if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
697                 fatal("failed allocating MSAA g-buffer!");
698         }
699         else fatal("failed allocating MSAA g-buffer!");
700     }
701 
702     glClearColor(0, 0, 0, 0);
703     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | (ghasstencil ? GL_STENCIL_BUFFER_BIT : 0));
704 
705     msaapositions.setsize(0);
706     loopi(msaasamples)
707     {
708         GLfloat vals[2];
709         glGetMultisamplefv_(GL_SAMPLE_POSITION, i, vals);
710         msaapositions.add(vec2(vals[0], vals[1]));
711     }
712 
713     if(!msrefracttex) glGenTextures(1, &msrefracttex);
714     if(!msrefractfbo) glGenFramebuffers_(1, &msrefractfbo);
715 
716     glBindFramebuffer_(GL_FRAMEBUFFER, msrefractfbo);
717 
718     glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msrefracttex);
719     texms(GL_RGB, w, h);
720 
721     glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, msrefracttex, 0);
722     bindmsdepth();
723 
724     if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
725         fatal("failed allocating MSAA refraction buffer!");
726 
727     glBindFramebuffer_(GL_FRAMEBUFFER, 0);
728 
729     useshaderbyname("msaaedgedetect");
730     useshaderbyname("msaaresolve");
731     useshaderbyname("msaareducew");
732     useshaderbyname("msaareduce");
733     if((hasMSS || msaasamples==2) && msaatonemap)
734     {
735         useshaderbyname("msaatonemap");
736         if(hasMSS) useshaderbyname("msaatonemapsample");
737     }
738 }
739 
bindgdepth()740 void bindgdepth()
741 {
742     if(gdepthformat || msaasamples)
743     {
744         glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, gdepthrb);
745         if(ghasstencil > 1) glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gdepthrb);
746         else if(!msaasamples || ghasstencil) glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gstencilrb);
747     }
748     else
749     {
750         glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_RECTANGLE, gdepthtex, 0);
751         if(ghasstencil > 1) glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_RECTANGLE, gdepthtex, 0);
752         else if(ghasstencil) glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gstencilrb);
753     }
754 }
755 
setupgbuffer()756 void setupgbuffer()
757 {
758     int sw = renderw, sh = renderh;
759     if(gscale != 100)
760     {
761         sw = max((renderw*gscale + 99)/100, 1);
762         sh = max((renderh*gscale + 99)/100, 1);
763     }
764 
765     if(gw == sw && gh == sh && ((sw >= hudw && sh >= hudh && !scalefbo[0]) || (scalew == hudw && scaleh == hudh))) return;
766 
767     cleanupscale();
768     cleanupbloom();
769     cleanupao();
770     cleanupaa();
771     cleanuppostfx();
772 
773     gw = sw;
774     gh = sh;
775 
776     hdrformat = gethdrformat(hdrprec);
777     stencilformat = ghasstencil > 1 ? GL_DEPTH24_STENCIL8 : (ghasstencil ? GL_STENCIL_INDEX8 : 0);
778 
779     if(msaasamples) setupmsbuffer(gw, gh);
780 
781     hdrfloat = floatformat(hdrformat);
782     hdrclear = 3;
783     gdepthinit = false;
784 
785     if(gdepthformat || msaasamples)
786     {
787         if(!gdepthrb) glGenRenderbuffers_(1, &gdepthrb);
788         glBindRenderbuffer_(GL_RENDERBUFFER, gdepthrb);
789         glRenderbufferStorage_(GL_RENDERBUFFER, ghasstencil > 1 ? stencilformat : GL_DEPTH_COMPONENT, gw, gh);
790         glBindRenderbuffer_(GL_RENDERBUFFER, 0);
791     }
792     if(!msaasamples && ghasstencil == 1)
793     {
794         if(!gstencilrb) glGenRenderbuffers_(1, &gstencilrb);
795         glBindRenderbuffer_(GL_RENDERBUFFER, gstencilrb);
796         glRenderbufferStorage_(GL_RENDERBUFFER, GL_STENCIL_INDEX8, gw, gh);
797         glBindRenderbuffer_(GL_RENDERBUFFER, 0);
798     }
799 
800     if(!msaasamples)
801     {
802         if(!gdepthtex) glGenTextures(1, &gdepthtex);
803         if(!gcolortex) glGenTextures(1, &gcolortex);
804         if(!gnormaltex) glGenTextures(1, &gnormaltex);
805         if(!gglowtex) glGenTextures(1, &gglowtex);
806         if(!gfbo) glGenFramebuffers_(1, &gfbo);
807 
808         glBindFramebuffer_(GL_FRAMEBUFFER, gfbo);
809 
810         maskgbuffer("cndg");
811 
812         static const GLenum depthformats[] = { GL_RGBA8, GL_R16F, GL_R32F };
813         GLenum depthformat = gdepthformat ? depthformats[gdepthformat-1] : (ghasstencil > 1 ? stencilformat : GL_DEPTH_COMPONENT);
814         createtexture(gdepthtex, gw, gh, NULL, 3, 0, depthformat, GL_TEXTURE_RECTANGLE);
815 
816         createtexture(gcolortex, gw, gh, NULL, 3, 0, GL_RGBA8, GL_TEXTURE_RECTANGLE);
817         createtexture(gnormaltex, gw, gh, NULL, 3, 0, GL_RGBA8, GL_TEXTURE_RECTANGLE);
818         createtexture(gglowtex, gw, gh, NULL, 3, 0, hasAFBO ? hdrformat : GL_RGBA8, GL_TEXTURE_RECTANGLE);
819 
820         bindgdepth();
821         glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, gcolortex, 0);
822         glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_RECTANGLE, gnormaltex, 0);
823         glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_RECTANGLE, gglowtex, 0);
824         if(gdepthformat) glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_RECTANGLE, gdepthtex, 0);
825 
826         if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
827         {
828             if(hasAFBO)
829             {
830                 createtexture(gglowtex, gw, gh, NULL, 3, 0, GL_RGBA8, GL_TEXTURE_RECTANGLE);
831                 glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_RECTANGLE, gglowtex, 0);
832                 if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
833                     fatal("failed allocating g-buffer!");
834             }
835             else fatal("failed allocating g-buffer!");
836         }
837 
838         glClearColor(0, 0, 0, 0);
839         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | (ghasstencil ? GL_STENCIL_BUFFER_BIT : 0));
840     }
841 
842     if(!hdrtex) glGenTextures(1, &hdrtex);
843     if(!hdrfbo) glGenFramebuffers_(1, &hdrfbo);
844 
845     glBindFramebuffer_(GL_FRAMEBUFFER, hdrfbo);
846 
847     createtexture(hdrtex, gw, gh, NULL, 3, 1, hdrformat, GL_TEXTURE_RECTANGLE);
848 
849     glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, hdrtex, 0);
850     bindgdepth();
851 
852     if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
853         fatal("failed allocating HDR buffer!");
854 
855     if(!msaasamples || (hasMSS && msaatonemap && msaatonemapblit))
856     {
857         if(!refracttex) glGenTextures(1, &refracttex);
858         if(!refractfbo) glGenFramebuffers_(1, &refractfbo);
859 
860         glBindFramebuffer_(GL_FRAMEBUFFER, refractfbo);
861 
862         createtexture(refracttex, gw, gh, NULL, 3, 0, GL_RGB, GL_TEXTURE_RECTANGLE);
863 
864         glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, refracttex, 0);
865         bindgdepth();
866 
867         if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
868             fatal("failed allocating refraction buffer!");
869     }
870 
871     glBindFramebuffer_(GL_FRAMEBUFFER, 0);
872 
873     if(gw < hudw || gh < hudh) setupscale(gw, gh, hudw, hudh);
874 }
875 
cleanupgbuffer()876 void cleanupgbuffer()
877 {
878     if(gfbo) { glDeleteFramebuffers_(1, &gfbo); gfbo = 0; }
879     if(gdepthtex) { glDeleteTextures(1, &gdepthtex); gdepthtex = 0; }
880     if(gcolortex) { glDeleteTextures(1, &gcolortex); gcolortex = 0; }
881     if(gnormaltex) { glDeleteTextures(1, &gnormaltex); gnormaltex = 0; }
882     if(gglowtex) { glDeleteTextures(1, &gglowtex); gglowtex = 0; }
883     if(gstencilrb) { glDeleteRenderbuffers_(1, &gstencilrb); gstencilrb = 0; }
884     if(gdepthrb) { glDeleteRenderbuffers_(1, &gdepthrb); gdepthrb = 0; }
885     if(hdrfbo) { glDeleteFramebuffers_(1, &hdrfbo); hdrfbo = 0; }
886     if(hdrtex) { glDeleteTextures(1, &hdrtex); hdrtex = 0; }
887     if(refractfbo) { glDeleteFramebuffers_(1, &refractfbo); refractfbo = 0; }
888     if(refracttex) { glDeleteTextures(1, &refracttex); refracttex = 0; }
889     gw = gh = -1;
890     cleanupscale();
891     cleanupmsbuffer();
892     cleardeferredlightshaders();
893 }
894 
resolvemsaacolor(int w=vieww,int h=viewh)895 void resolvemsaacolor(int w = vieww, int h = viewh)
896 {
897     if(!msaasamples) return;
898 
899     timer *resolvetimer = drawtex ? NULL : begintimer("msaa resolve");
900 
901     glBindFramebuffer_(GL_READ_FRAMEBUFFER, mshdrfbo);
902     glBindFramebuffer_(GL_DRAW_FRAMEBUFFER, hdrfbo);
903     glBlitFramebuffer_(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
904 
905     glBindFramebuffer_(GL_FRAMEBUFFER, hdrfbo);
906 
907     endtimer(resolvetimer);
908 }
909 
910 FVAR(bloomthreshold, 1e-3f, 0.8f, 1e3f);
911 FVARP(bloomscale, 0, 1.0f, 1e3f);
912 VARP(bloomblur, 0, 7, 7);
913 VARP(bloomiter, 0, 0, 4);
914 VARFP(bloomsize, 6, 9, 11, cleanupbloom());
915 VARFP(bloomprec, 0, 2, 3, cleanupbloom());
916 FVAR(hdraccumscale, 0, 0.98f, 1);
917 VAR(hdraccummillis, 1, 33, 1000);
918 VAR(hdrreduce, 0, 2, 2);
919 VARFP(hdrprec, 0, 2, 3, cleanupgbuffer());
920 FVARFP(hdrgamma, 1e-3f, 2, 1e3f, initwarning("HDR setup", INIT_LOAD, CHANGE_SHADERS));
921 FVARR(hdrbright, 1e-4f, 1.0f, 1e4f);
922 FVAR(hdrsaturate, 1e-3f, 0.8f, 1e3f);
923 VARFP(gscale, 25, 100, 100, cleanupgbuffer());
924 VARFP(gscalecubic, 0, 0, 1, cleanupgbuffer());
925 VARFP(gscalenearest, 0, 0, 1, cleanupgbuffer());
926 FVARFP(gscalecubicsoft, 0, 0, 1, initwarning("scaling setup", INIT_LOAD, CHANGE_SHADERS));
927 
928 float ldrscale = 1.0f, ldrscaleb = 1.0f/255;
929 
copyhdr(int sw,int sh,GLuint fbo,int dw,int dh,bool flipx,bool flipy,bool swapxy)930 void copyhdr(int sw, int sh, GLuint fbo, int dw, int dh, bool flipx, bool flipy, bool swapxy)
931 {
932     if(!dw) dw = sw;
933     if(!dh) dh = sh;
934 
935     if(msaasamples) resolvemsaacolor(sw, sh);
936     GLERROR;
937 
938     glBindFramebuffer_(GL_FRAMEBUFFER, fbo);
939     glViewport(0, 0, dw, dh);
940     SETSHADER(hdrnop);
941     glBindTexture(GL_TEXTURE_RECTANGLE, hdrtex);
942     screenquadreorient(sw, sh, flipx, flipy, swapxy);
943     GLERROR;
944 
945     hdrclear = 3;
946 }
947 
loadhdrshaders(int aa)948 void loadhdrshaders(int aa)
949 {
950     switch(aa)
951     {
952         case AA_LUMA:
953             useshaderbyname("hdrtonemapluma");
954             useshaderbyname("hdrnopluma");
955             if(msaasamples && (hasMSS || msaasamples==2) && msaatonemap) useshaderbyname("msaatonemapluma");
956             break;
957         case AA_VELOCITY:
958             useshaderbyname("hdrtonemapvelocity");
959             useshaderbyname("hdrnopvelocity");
960             if(msaasamples && (hasMSS || msaasamples==2) && msaatonemap) useshaderbyname("msaatonemapvelocity");
961             break;
962         case AA_VELOCITY_MASKED:
963             if(!msaasamples && ghasstencil) useshaderbyname("hdrtonemapvelocity");
964             else
965             {
966                 useshaderbyname("hdrtonemapvelocitymasked");
967                 useshaderbyname("hdrnopvelocitymasked");
968                 if(msaasamples && (hasMSS || msaasamples==2) && msaatonemap) useshaderbyname("msaatonemapvelocitymasked");
969             }
970             break;
971         case AA_SPLIT:
972             useshaderbyname("msaatonemapsplit");
973             break;
974         case AA_SPLIT_LUMA:
975             useshaderbyname("msaatonemapsplitluma");
976             break;
977         case AA_SPLIT_VELOCITY:
978             useshaderbyname("msaatonemapsplitvelocity");
979             break;
980         case AA_SPLIT_VELOCITY_MASKED:
981             useshaderbyname("msaatonemapsplitvelocitymasked");
982             break;
983         default:
984             break;
985     }
986 }
987 
processhdr(GLuint outfbo,int aa)988 void processhdr(GLuint outfbo, int aa)
989 {
990     timer *hdrtimer = begintimer("hdr processing");
991 
992     GLOBALPARAMF(hdrparams, hdrbright, hdrsaturate, bloomthreshold, bloomscale);
993 
994     GLuint b0fbo = bloomfbo[1], b0tex = bloomtex[1], b1fbo =  bloomfbo[0], b1tex = bloomtex[0], ptex = hdrtex;
995     int b0w = max(vieww/4, bloomw), b0h = max(viewh/4, bloomh), b1w = max(vieww/2, bloomw), b1h = max(viewh/2, bloomh),
996         pw = vieww, ph = viewh;
997     if(msaasamples)
998     {
999         if(aa < AA_SPLIT && (!(hasMSS || msaasamples==2) || !msaatonemap))
1000         {
1001             glBindFramebuffer_(GL_READ_FRAMEBUFFER, mshdrfbo);
1002             glBindFramebuffer_(GL_DRAW_FRAMEBUFFER, hdrfbo);
1003             glBlitFramebuffer_(0, 0, vieww, viewh, 0, 0, vieww, viewh, GL_COLOR_BUFFER_BIT, GL_NEAREST);
1004         }
1005         else if(hasFBMSBS && (vieww > bloomw || viewh > bloomh))
1006         {
1007             int cw = max(vieww/2, bloomw), ch = max(viewh/2, bloomh);
1008             glBindFramebuffer_(GL_READ_FRAMEBUFFER, mshdrfbo);
1009             glBindFramebuffer_(GL_DRAW_FRAMEBUFFER, hdrfbo);
1010             glBlitFramebuffer_(0, 0, vieww, viewh, 0, 0, cw, ch, GL_COLOR_BUFFER_BIT, GL_SCALED_RESOLVE_FASTEST_EXT);
1011             pw = cw;
1012             ph = ch;
1013         }
1014         else
1015         {
1016             glBindFramebuffer_(GL_FRAMEBUFFER, hdrfbo);
1017             if(vieww/2 >= bloomw)
1018             {
1019                 pw = vieww/2;
1020                 if(viewh/2 >= bloomh)
1021                 {
1022                     ph = viewh/2;
1023                     glViewport(0, 0, pw, ph);
1024                     SETSHADER(msaareduce);
1025                 }
1026                 else
1027                 {
1028                     glViewport(0, 0, pw, viewh);
1029                     SETSHADER(msaareducew);
1030                 }
1031             }
1032             else
1033             {
1034                 glViewport(0, 0, vieww, viewh);
1035                 SETSHADER(msaaresolve);
1036             }
1037             glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mshdrtex);
1038             screenquad(vieww, viewh);
1039         }
1040     }
1041     if(hdrreduce) while(pw > bloomw || ph > bloomh)
1042     {
1043         GLuint cfbo = b1fbo, ctex = b1tex;
1044         int cw = max(pw/2, bloomw), ch = max(ph/2, bloomh);
1045 
1046         if(hdrreduce > 1 && cw/2 >= bloomw)
1047         {
1048             cw /= 2;
1049             if(ch/2 >= bloomh)
1050             {
1051                 ch /= 2;
1052                 SETSHADER(hdrreduce2);
1053             }
1054             else SETSHADER(hdrreduce2w);
1055         }
1056         else SETSHADER(hdrreduce);
1057         if(cw == bloomw && ch == bloomh) { if(bloomfbo[5]) { cfbo = bloomfbo[5]; ctex = bloomtex[5]; } else { cfbo = bloomfbo[2]; ctex = bloomtex[2]; } }
1058         glBindFramebuffer_(GL_FRAMEBUFFER, cfbo);
1059         glViewport(0, 0, cw, ch);
1060         glBindTexture(GL_TEXTURE_RECTANGLE, ptex);
1061         screenquad(pw, ph);
1062 
1063         ptex = ctex;
1064         pw = cw;
1065         ph = ch;
1066         swap(b0fbo, b1fbo);
1067         swap(b0tex, b1tex);
1068         swap(b0w, b1w);
1069         swap(b0h, b1h);
1070     }
1071 
1072     if(!lasthdraccum || lastmillis - lasthdraccum >= hdraccummillis)
1073     {
1074         GLuint ltex = ptex;
1075         int lw = pw, lh = ph;
1076         for(int i = 0; lw > 2 || lh > 2; i++)
1077         {
1078             int cw = max(lw/2, 2), ch = max(lh/2, 2);
1079 
1080             if(hdrreduce > 1 && cw/2 >= 2)
1081             {
1082                 cw /= 2;
1083                 if(ch/2 >= 2)
1084                 {
1085                     ch /= 2;
1086                     if(i) SETSHADER(hdrreduce2); else SETSHADER(hdrluminance2);
1087                 }
1088                 else if(i) SETSHADER(hdrreduce2w); else SETSHADER(hdrluminance2w);
1089             }
1090             else if(i) SETSHADER(hdrreduce); else SETSHADER(hdrluminance);
1091             glBindFramebuffer_(GL_FRAMEBUFFER, b1fbo);
1092             glViewport(0, 0, cw, ch);
1093             glBindTexture(GL_TEXTURE_RECTANGLE, ltex);
1094             screenquad(lw, lh);
1095 
1096             ltex = b1tex;
1097             lw = cw;
1098             lh = ch;
1099             swap(b0fbo, b1fbo);
1100             swap(b0tex, b1tex);
1101             swap(b0w, b1w);
1102             swap(b0h, b1h);
1103         }
1104 
1105         glBindFramebuffer_(GL_FRAMEBUFFER, bloomfbo[4]);
1106         glViewport(0, 0, bloompbo ? 4 : 1, 1);
1107         glEnable(GL_BLEND);
1108         glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
1109         SETSHADER(hdraccum);
1110         glBindTexture(GL_TEXTURE_RECTANGLE, b0tex);
1111         LOCALPARAMF(accumscale, lasthdraccum ? pow(hdraccumscale, float(lastmillis - lasthdraccum)/hdraccummillis) : 0);
1112         screenquad(2, 2);
1113         glDisable(GL_BLEND);
1114 
1115         if(bloompbo)
1116         {
1117             glBindBuffer_(GL_PIXEL_PACK_BUFFER, bloompbo);
1118             glPixelStorei(GL_PACK_ALIGNMENT, 1);
1119             glReadPixels(0, 0, 4, 1, hasTRG ? GL_RED : GL_RGB, hasTF ? GL_FLOAT : GL_UNSIGNED_SHORT, NULL);
1120             glBindBuffer_(GL_PIXEL_PACK_BUFFER, 0);
1121         }
1122 
1123         lasthdraccum = lastmillis;
1124     }
1125 
1126     if(bloompbo)
1127     {
1128         glBindBuffer_(GL_ARRAY_BUFFER, bloompbo);
1129         gle::enablecolor();
1130         gle::colorpointer(hasTF ? sizeof(GLfloat) : sizeof(GLushort), (void *)0, hasTF ? GL_FLOAT : GL_UNSIGNED_SHORT, 1);
1131         glBindBuffer_(GL_ARRAY_BUFFER, 0);
1132     }
1133 
1134     b0fbo = bloomfbo[3];
1135     b0tex = bloomtex[3];
1136     b1fbo = bloomfbo[2];
1137     b1tex = bloomtex[2];
1138     b0w = b1w = bloomw;
1139     b0h = b1h = bloomh;
1140 
1141     glActiveTexture_(GL_TEXTURE2);
1142     glBindTexture(GL_TEXTURE_2D, bloomtex[4]);
1143     glActiveTexture_(GL_TEXTURE0);
1144 
1145     glBindFramebuffer_(GL_FRAMEBUFFER, b0fbo);
1146     glViewport(0, 0, b0w, b0h);
1147     SETSHADER(hdrbloom);
1148     glBindTexture(GL_TEXTURE_RECTANGLE, ptex);
1149     screenquad(pw, ph);
1150 
1151     if(bloomblur)
1152     {
1153         float blurweights[MAXBLURRADIUS+1], bluroffsets[MAXBLURRADIUS+1];
1154         setupblurkernel(bloomblur, blurweights, bluroffsets);
1155         loopi(2 + 2*bloomiter)
1156         {
1157             glBindFramebuffer_(GL_FRAMEBUFFER, b1fbo);
1158             glViewport(0, 0, b1w, b1h);
1159             setblurshader(i%2, 1, bloomblur, blurweights, bluroffsets, GL_TEXTURE_RECTANGLE);
1160             glBindTexture(GL_TEXTURE_RECTANGLE, b0tex);
1161             screenquad(b0w, b0h);
1162             swap(b0w, b1w);
1163             swap(b0h, b1h);
1164             swap(b0tex, b1tex);
1165             swap(b0fbo, b1fbo);
1166         }
1167     }
1168 
1169     if(aa >= AA_SPLIT)
1170     {
1171         glBindFramebuffer_(GL_FRAMEBUFFER, outfbo);
1172         glViewport(0, 0, vieww, viewh);
1173         glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mshdrtex);
1174         glActiveTexture_(GL_TEXTURE1);
1175         glBindTexture(GL_TEXTURE_RECTANGLE, b0tex);
1176         glActiveTexture_(GL_TEXTURE0);
1177         switch(aa)
1178         {
1179             case AA_SPLIT_LUMA: SETSHADER(msaatonemapsplitluma); break;
1180             case AA_SPLIT_VELOCITY:
1181                 SETSHADER(msaatonemapsplitvelocity);
1182                 setaavelocityparams(GL_TEXTURE3);
1183                 break;
1184             case AA_SPLIT_VELOCITY_MASKED:
1185                 SETSHADER(msaatonemapsplitvelocitymasked);
1186                 setaavelocityparams(GL_TEXTURE3);
1187                 break;
1188             default: SETSHADER(msaatonemapsplit); break;
1189         }
1190         screenquad(vieww, viewh, b0w, b0h);
1191     }
1192     else if(!msaasamples || !(hasMSS || msaasamples==2) || !msaatonemap)
1193     {
1194         glBindFramebuffer_(GL_FRAMEBUFFER, outfbo);
1195         glViewport(0, 0, vieww, viewh);
1196         glBindTexture(GL_TEXTURE_RECTANGLE, hdrtex);
1197         glActiveTexture_(GL_TEXTURE1);
1198         glBindTexture(GL_TEXTURE_RECTANGLE, b0tex);
1199         glActiveTexture_(GL_TEXTURE0);
1200         switch(aa)
1201         {
1202             case AA_LUMA: SETSHADER(hdrtonemapluma); break;
1203             case AA_VELOCITY:
1204                 SETSHADER(hdrtonemapvelocity);
1205                 setaavelocityparams(GL_TEXTURE3);
1206                 break;
1207             case AA_VELOCITY_MASKED:
1208                 if(!msaasamples && ghasstencil)
1209                 {
1210                     glStencilFunc(GL_EQUAL, 0, 0x80);
1211                     glEnable(GL_STENCIL_TEST);
1212                     SETSHADER(hdrtonemapvelocity);
1213                     setaavelocityparams(GL_TEXTURE3);
1214                     screenquad(vieww, viewh, b0w, b0h);
1215 
1216                     glStencilFunc(GL_EQUAL, 0x80, 0x80);
1217                     SETSHADER(hdrtonemap);
1218                     screenquad(vieww, viewh, b0w, b0h);
1219                     glDisable(GL_STENCIL_TEST);
1220                     goto done;
1221                 }
1222                 SETSHADER(hdrtonemapvelocitymasked);
1223                 setaavelocityparams(GL_TEXTURE3);
1224                 break;
1225             default: SETSHADER(hdrtonemap); break;
1226         }
1227         screenquad(vieww, viewh, b0w, b0h);
1228     }
1229     else
1230     {
1231         bool blit = hasMSS && msaatonemapblit && (!aa || !outfbo);
1232 
1233         glBindFramebuffer_(GL_FRAMEBUFFER, blit ? msrefractfbo : outfbo);
1234         glViewport(0, 0, vieww, viewh);
1235         glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mshdrtex);
1236         glActiveTexture_(GL_TEXTURE1);
1237         glBindTexture(GL_TEXTURE_RECTANGLE, b0tex);
1238         glActiveTexture_(GL_TEXTURE0);
1239 
1240         if(blit) SETSHADER(msaatonemapsample);
1241         else switch(aa)
1242         {
1243             case AA_LUMA: SETSHADER(msaatonemapluma); break;
1244             case AA_VELOCITY:
1245                 SETSHADER(msaatonemapvelocity);
1246                 setaavelocityparams(GL_TEXTURE3);
1247                 break;
1248             case AA_VELOCITY_MASKED:
1249                 SETSHADER(msaatonemapvelocitymasked);
1250                 setaavelocityparams(GL_TEXTURE3);
1251                 break;
1252             default: SETSHADER(msaatonemap); break;
1253         }
1254         screenquad(vieww, viewh, b0w, b0h);
1255 
1256         if(blit)
1257         {
1258             glBindFramebuffer_(GL_READ_FRAMEBUFFER, msrefractfbo);
1259             glBindFramebuffer_(GL_DRAW_FRAMEBUFFER, aa || !outfbo ? refractfbo : outfbo);
1260             glBlitFramebuffer_(0, 0, vieww, viewh, 0, 0, vieww, viewh, GL_COLOR_BUFFER_BIT, GL_NEAREST);
1261 
1262             if(!outfbo)
1263             {
1264                 glBindFramebuffer_(GL_FRAMEBUFFER, outfbo);
1265                 glViewport(0, 0, vieww, viewh);
1266                 if(!blit) SETSHADER(hdrnop);
1267                 else switch(aa)
1268                 {
1269                     case AA_LUMA: SETSHADER(hdrnopluma); break;
1270                     case AA_VELOCITY:
1271                         SETSHADER(hdrnopvelocity);
1272                         setaavelocityparams(GL_TEXTURE3);
1273                         break;
1274                     case AA_VELOCITY_MASKED:
1275                         SETSHADER(hdrnopvelocitymasked);
1276                         setaavelocityparams(GL_TEXTURE3);
1277                         break;
1278                     default: SETSHADER(hdrnop); break;
1279                 }
1280                 glBindTexture(GL_TEXTURE_RECTANGLE, refracttex);
1281                 screenquad(vieww, viewh);
1282             }
1283         }
1284     }
1285 
1286 done:
1287     if(bloompbo) gle::disablecolor();
1288 
1289     endtimer(hdrtimer);
1290 }
1291 
1292 VAR(debugdepth, 0, 0, 1);
1293 
viewdepth()1294 void viewdepth()
1295 {
1296     int w = min(hudw, hudh)/2, h = (w*hudh)/hudw;
1297     SETSHADER(hudrect);
1298     gle::colorf(1, 1, 1);
1299     glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
1300     debugquad(0, 0, w, h, 0, 0, gw, gh);
1301 }
1302 
1303 VAR(debugstencil, 0, 0, 0xFF);
1304 
viewstencil()1305 void viewstencil()
1306 {
1307     if(!ghasstencil || !hdrfbo) return;
1308     glBindFramebuffer_(GL_FRAMEBUFFER, hdrfbo);
1309     glViewport(0, 0, gw, gh);
1310 
1311     glClearColor(0, 0, 0, 0);
1312     glClear(GL_COLOR_BUFFER_BIT);
1313 
1314     glStencilFunc(GL_NOTEQUAL, 0, debugstencil);
1315     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
1316     glEnable(GL_STENCIL_TEST);
1317     SETSHADER(hudnotexture);
1318     gle::colorf(1, 1, 1);
1319     debugquad(0, 0, hudw, hudh, 0, 0, gw, gh);
1320     glDisable(GL_STENCIL_TEST);
1321 
1322     glBindFramebuffer_(GL_FRAMEBUFFER, 0);
1323     glViewport(0, 0, hudw, hudh);
1324 
1325     int w = min(hudw, hudh)/2, h = (w*hudh)/hudw;
1326     SETSHADER(hudrect);
1327     gle::colorf(1, 1, 1);
1328     glBindTexture(GL_TEXTURE_RECTANGLE, hdrtex);
1329     debugquad(0, 0, w, h, 0, 0, gw, gh);
1330 }
1331 
1332 VAR(debugrefract, 0, 0, 1);
1333 
viewrefract()1334 void viewrefract()
1335 {
1336     int w = min(hudw, hudh)/2, h = (w*hudh)/hudw;
1337     SETSHADER(hudrect);
1338     gle::colorf(1, 1, 1);
1339     glBindTexture(GL_TEXTURE_RECTANGLE, refracttex);
1340     debugquad(0, 0, w, h, 0, 0, gw, gh);
1341 }
1342 
1343 #define RH_MAXSPLITS 4
1344 #define RH_MAXGRID 64
1345 
1346 GLuint rhtex[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }, rhfbo = 0;
1347 uint rhclearmasks[2][RH_MAXSPLITS][(RH_MAXGRID+2+31)/32];
1348 GLuint rsmdepthtex = 0, rsmcolortex = 0, rsmnormaltex = 0, rsmfbo = 0;
1349 
1350 extern int rhrect, rhgrid, rhsplits, rhborder, rhprec, rhtaps, rhcache, rhforce, rsmprec, rsmdepthprec, rsmsize;
1351 
1352 static Shader *radiancehintsshader = NULL;
1353 Shader *rsmworldshader = NULL;
1354 
loadradiancehintsshader()1355 Shader *loadradiancehintsshader()
1356 {
1357     defformatstring(name, "radiancehints%d", rhtaps);
1358     return generateshader(name, "radiancehintsshader %d", rhtaps);
1359 }
1360 
loadrhshaders()1361 void loadrhshaders()
1362 {
1363     if(rhborder) useshaderbyname("radiancehintsborder");
1364     if(rhcache) useshaderbyname("radiancehintscached");
1365     useshaderbyname("radiancehintsdisable");
1366     radiancehintsshader = loadradiancehintsshader();
1367     rsmworldshader = useshaderbyname("rsmworld");
1368     useshaderbyname("rsmsky");
1369 }
1370 
clearrhshaders()1371 void clearrhshaders()
1372 {
1373     radiancehintsshader = NULL;
1374     rsmworldshader = NULL;
1375 }
1376 
setupradiancehints()1377 void setupradiancehints()
1378 {
1379     GLenum rhformat = hasTF && rhprec >= 1 ? GL_RGBA16F : GL_RGBA8;
1380 
1381     loopi(!rhrect && rhcache ? 8 : 4)
1382     {
1383         if(!rhtex[i]) glGenTextures(1, &rhtex[i]);
1384         create3dtexture(rhtex[i], rhgrid+2*rhborder, rhgrid+2*rhborder, (rhgrid+2*rhborder)*rhsplits, NULL, 7, 1, rhformat);
1385         if(rhborder)
1386         {
1387             glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
1388             glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
1389             glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
1390             GLfloat border[4] = { 0.5f, 0.5f, 0.5f, 0 };
1391             glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, border);
1392         }
1393     }
1394 
1395     if(!rhfbo) glGenFramebuffers_(1, &rhfbo);
1396     glBindFramebuffer_(GL_FRAMEBUFFER, rhfbo);
1397 
1398     if(rhrect) loopi(4)
1399     {
1400         if(!rhtex[4+i]) glGenTextures(1, &rhtex[4+i]);
1401         createtexture(rhtex[4+i], (rhgrid + 2*rhborder)*(rhgrid + 2*rhborder), (rhgrid + 2*rhborder)*rhsplits, NULL, 3, 0, rhformat, GL_TEXTURE_RECTANGLE);
1402         glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_RECTANGLE, rhtex[4+i], 0);
1403     }
1404     else loopi(4) glFramebufferTexture3D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_3D, rhtex[i], 0, 0);
1405 
1406     static const GLenum drawbufs[4] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 };
1407     glDrawBuffers_(4, drawbufs);
1408 
1409     if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
1410         fatal("failed allocating radiance hints buffer!");
1411 
1412     if(!rsmdepthtex) glGenTextures(1, &rsmdepthtex);
1413     if(!rsmcolortex) glGenTextures(1, &rsmcolortex);
1414     if(!rsmnormaltex) glGenTextures(1, &rsmnormaltex);
1415 
1416     if(!rsmfbo) glGenFramebuffers_(1, &rsmfbo);
1417 
1418     glBindFramebuffer_(GL_FRAMEBUFFER, rsmfbo);
1419 
1420     GLenum rsmformat = gethdrformat(rsmprec, GL_RGBA8);
1421 
1422     createtexture(rsmdepthtex, rsmsize, rsmsize, NULL, 3, 0, rsmdepthprec > 1 ? GL_DEPTH_COMPONENT32 : (rsmdepthprec ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16), GL_TEXTURE_RECTANGLE);
1423     createtexture(rsmcolortex, rsmsize, rsmsize, NULL, 3, 0, rsmformat, GL_TEXTURE_RECTANGLE);
1424     createtexture(rsmnormaltex, rsmsize, rsmsize, NULL, 3, 0, rsmformat, GL_TEXTURE_RECTANGLE);
1425 
1426     glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_RECTANGLE, rsmdepthtex, 0);
1427     glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, rsmcolortex, 0);
1428     glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_RECTANGLE, rsmnormaltex, 0);
1429 
1430     glDrawBuffers_(2, drawbufs);
1431 
1432     if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
1433         fatal("failed allocating RSM buffer!");
1434 
1435     glBindFramebuffer_(GL_FRAMEBUFFER, 0);
1436 
1437     loadrhshaders();
1438 
1439     clearradiancehintscache();
1440 }
1441 
cleanupradiancehints()1442 void cleanupradiancehints()
1443 {
1444     clearradiancehintscache();
1445 
1446     loopi(8) if(rhtex[i]) { glDeleteTextures(1, &rhtex[i]); rhtex[i] = 0; }
1447     if(rhfbo) { glDeleteFramebuffers_(1, &rhfbo); rhfbo = 0; }
1448     if(rsmdepthtex) { glDeleteTextures(1, &rsmdepthtex); rsmdepthtex = 0; }
1449     if(rsmcolortex) { glDeleteTextures(1, &rsmcolortex); rsmcolortex = 0; }
1450     if(rsmnormaltex) { glDeleteTextures(1, &rsmnormaltex); rsmnormaltex = 0; }
1451     if(rsmfbo) { glDeleteFramebuffers_(1, &rsmfbo); rsmfbo = 0; }
1452 
1453     clearrhshaders();
1454 }
1455 
1456 VARF(rhrect, 0, 0, 1, cleanupradiancehints());
1457 VARF(rhsplits, 1, 2, RH_MAXSPLITS, { cleardeferredlightshaders(); cleanupradiancehints(); });
1458 VARF(rhborder, 0, 1, 1, cleanupradiancehints());
1459 VARF(rsmsize, 64, 384, 2048, cleanupradiancehints());
1460 VARF(rhnearplane, 1, 1, 16, clearradiancehintscache());
1461 VARF(rhfarplane, 64, 1024, 16384, clearradiancehintscache());
1462 FVARF(rsmpradiustweak, 1e-3f, 1, 1e3f, clearradiancehintscache());
1463 FVARF(rhpradiustweak, 1e-3f, 1, 1e3f, clearradiancehintscache());
1464 FVARF(rsmdepthrange, 0, 1024, 1e6f, clearradiancehintscache());
1465 FVARF(rsmdepthmargin, 0, 0.1f, 1e3f, clearradiancehintscache());
1466 VARFP(rhprec, 0, 0, 1, cleanupradiancehints());
1467 VARFP(rsmprec, 0, 0, 3, cleanupradiancehints());
1468 VARFP(rsmdepthprec, 0, 0, 2, cleanupradiancehints());
1469 FVAR(rhnudge, 0, 0.5f, 4);
1470 FVARF(rhworldbias, 0, 0.5f, 10, clearradiancehintscache());
1471 FVARF(rhsplitweight, 0.20f, 0.6f, 0.95f, clearradiancehintscache());
1472 VARF(rhgrid, 3, 27, RH_MAXGRID, cleanupradiancehints());
1473 FVARF(rsmspread, 0, 0.15f, 1, clearradiancehintscache());
1474 VAR(rhclipgrid, 0, 1, 1);
1475 VARF(rhcache, 0, 1, 1, cleanupradiancehints());
1476 VARF(rhforce, 0, 0, 1, cleanupradiancehints());
1477 VAR(rsmcull, 0, 1, 1);
1478 VARFP(rhtaps, 0, 20, 32, cleanupradiancehints());
1479 VAR(rhdyntex, 0, 0, 1);
1480 VAR(rhdynmm, 0, 0, 1);
1481 VARFR(gidist, 0, 384, 1024, { clearradiancehintscache(); cleardeferredlightshaders(); if(!gidist) cleanupradiancehints(); });
1482 FVARFR(giscale, 0, 1.5f, 1e3f, { cleardeferredlightshaders(); if(!giscale) cleanupradiancehints(); });
1483 FVARR(giaoscale, 0, 3, 1e3f);
1484 VARFP(gi, 0, 1, 1, { cleardeferredlightshaders(); cleanupradiancehints(); });
1485 
1486 VAR(debugrsm, 0, 0, 2);
viewrsm()1487 void viewrsm()
1488 {
1489     int w = min(hudw, hudh)/2, h = (w*hudh)/hudw, x = hudw-w, y = hudh-h;
1490     SETSHADER(hudrect);
1491     gle::colorf(1, 1, 1);
1492     glBindTexture(GL_TEXTURE_RECTANGLE, debugrsm == 2 ? rsmnormaltex : rsmcolortex);
1493     debugquad(x, y, w, h, 0, 0, rsmsize, rsmsize);
1494 }
1495 
1496 VAR(debugrh, -1, 0, RH_MAXSPLITS*(RH_MAXGRID + 2));
viewrh()1497 void viewrh()
1498 {
1499     int w = min(hudw, hudh)/2, h = (w*hudh)/hudw, x = hudw-w, y = hudh-h;
1500     gle::colorf(1, 1, 1);
1501     if(debugrh < 0 && rhrect)
1502     {
1503         SETSHADER(hudrect);
1504         glBindTexture(GL_TEXTURE_RECTANGLE, rhtex[5]);
1505         float tw = (rhgrid+2*rhborder)*(rhgrid+2*rhborder), th = (rhgrid+2*rhborder)*rhsplits;
1506         gle::defvertex(2);
1507         gle::deftexcoord0(2);
1508         gle::begin(GL_TRIANGLE_STRIP);
1509         gle::attribf(x,   y);   gle::attribf(0,  0);
1510         gle::attribf(x+w, y);   gle::attribf(tw, 0);
1511         gle::attribf(x,   y+h); gle::attribf(0,  th);
1512         gle::attribf(x+w, y+h); gle::attribf(tw, th);
1513         gle::end();
1514     }
1515     else
1516     {
1517         SETSHADER(hud3d);
1518         glBindTexture(GL_TEXTURE_3D, rhtex[1]);
1519         float z = (max(debugrh, 1)-1+0.5f)/float((rhgrid+2*rhborder)*rhsplits);
1520         gle::defvertex(2);
1521         gle::deftexcoord0(3);
1522         gle::begin(GL_TRIANGLE_STRIP);
1523         gle::attribf(x,   y);   gle::attribf(0, 0, z);
1524         gle::attribf(x+w, y);   gle::attribf(1, 0, z);
1525         gle::attribf(x,   y+h); gle::attribf(0, 1, z);
1526         gle::attribf(x+w, y+h); gle::attribf(1, 1, z);
1527         gle::end();
1528     }
1529     gle::disable();
1530 }
1531 
1532 #define SHADOWATLAS_SIZE 4096
1533 
1534 PackNode shadowatlaspacker(0, 0, SHADOWATLAS_SIZE, SHADOWATLAS_SIZE);
1535 
1536 extern int smminradius;
1537 
1538 struct lightinfo
1539 {
1540     float sx1, sy1, sx2, sy2, sz1, sz2;
1541     int ent, shadowmap, flags;
1542     vec o, color;
1543     float radius;
1544     vec dir, spotx, spoty;
1545     int spot;
1546     float dist;
1547     occludequery *query;
1548 
calcspotlightinfo1549     void calcspot(const vec &spotdir, int spotangle)
1550     {
1551         dir = spotdir;
1552         spot = spotangle;
1553         quat orient(dir, vec(0, 0, dir.z < 0 ? -1 : 1));
1554         spotx = orient.invertedrotate(vec(1, 0, 0));
1555         spoty = orient.invertedrotate(vec(0, 1, 0));
1556     }
1557 
noshadowlightinfo1558     bool noshadow() const { return flags&L_NOSHADOW || radius <= smminradius; }
1559 
addscissorlightinfo1560     void addscissor(float &dx1, float &dy1, float &dx2, float &dy2, float &dz1, float &dz2) const
1561     {
1562         dx1 = min(dx1, sx1);
1563         dy1 = min(dy1, sy1);
1564         dx2 = max(dx2, sx2);
1565         dy2 = max(dy2, sy2);
1566         dz1 = min(dz1, sz1);
1567         dz2 = max(dz2, sz2);
1568     }
1569 };
1570 
1571 struct shadowcachekey
1572 {
1573     vec o;
1574     float radius;
1575     vec dir;
1576     int spot;
1577 
shadowcachekeyshadowcachekey1578     shadowcachekey() {}
shadowcachekeyshadowcachekey1579     shadowcachekey(const lightinfo &l) : o(l.o), radius(l.radius), dir(l.dir), spot(l.spot) {}
1580 };
1581 
hthash(const shadowcachekey & k)1582 static inline uint hthash(const shadowcachekey &k)
1583 {
1584     return hthash(k.o);
1585 }
1586 
htcmp(const shadowcachekey & x,const shadowcachekey & y)1587 static inline bool htcmp(const shadowcachekey &x, const shadowcachekey &y)
1588 {
1589     return x.o == y.o && x.radius == y.radius && x.dir == y.dir && x.spot == y.spot;
1590 }
1591 
1592 struct shadowcacheval;
1593 
1594 struct shadowmapinfo
1595 {
1596     ushort x, y, size, sidemask;
1597     int light;
1598     shadowcacheval *cached;
1599 };
1600 
1601 struct shadowcacheval
1602 {
1603     ushort x, y, size, sidemask;
1604 
shadowcachevalshadowcacheval1605     shadowcacheval() {}
shadowcachevalshadowcacheval1606     shadowcacheval(const shadowmapinfo &sm) : x(sm.x), y(sm.y), size(sm.size), sidemask(sm.sidemask) {}
1607 };
1608 
1609 struct shadowcache : hashtable<shadowcachekey, shadowcacheval>
1610 {
shadowcacheshadowcache1611     shadowcache() : hashtable<shadowcachekey, shadowcacheval>(256) {}
1612 
resetshadowcache1613     void reset()
1614     {
1615         clear();
1616     }
1617 };
1618 
1619 extern int smcache, smfilter, smgather;
1620 
1621 #define SHADOWCACHE_EVICT 2
1622 
1623 GLuint shadowatlastex = 0, shadowatlasfbo = 0;
1624 GLenum shadowatlastarget = GL_NONE;
1625 shadowcache shadowcache;
1626 bool shadowcachefull = false;
1627 int evictshadowcache = 0;
1628 
setsmnoncomparemode()1629 static inline void setsmnoncomparemode() // use texture gather
1630 {
1631     glTexParameteri(shadowatlastarget, GL_TEXTURE_COMPARE_MODE, GL_NONE);
1632     glTexParameteri(shadowatlastarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1633     glTexParameteri(shadowatlastarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1634 }
1635 
setsmcomparemode()1636 static inline void setsmcomparemode() // use embedded shadow cmp
1637 {
1638     glTexParameteri(shadowatlastarget, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
1639     glTexParameteri(shadowatlastarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1640     glTexParameteri(shadowatlastarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1641 }
1642 
1643 extern int usetexgather;
usegatherforsm()1644 static inline bool usegatherforsm() { return smfilter > 1 && smgather && hasTG && usetexgather; }
usesmcomparemode()1645 static inline bool usesmcomparemode() { return !usegatherforsm() || (hasTG && hasGPU5 && usetexgather > 1); }
1646 
viewshadowatlas()1647 void viewshadowatlas()
1648 {
1649     int w = min(hudw, hudh)/2, h = (w*hudh)/hudw, x = hudw-w, y = hudh-h;
1650     float tw = 1, th = 1;
1651     if(shadowatlastarget == GL_TEXTURE_RECTANGLE)
1652     {
1653         tw = shadowatlaspacker.w;
1654         th = shadowatlaspacker.h;
1655         SETSHADER(hudrect);
1656     }
1657     else hudshader->set();
1658     gle::colorf(1, 1, 1);
1659     glBindTexture(shadowatlastarget, shadowatlastex);
1660     if(usesmcomparemode()) setsmnoncomparemode();
1661     debugquad(x, y, w, h, 0, 0, tw, th);
1662     if(usesmcomparemode()) setsmcomparemode();
1663 }
1664 VAR(debugshadowatlas, 0, 0, 1);
1665 
1666 extern int smdepthprec, smsize;
1667 
setupshadowatlas()1668 void setupshadowatlas()
1669 {
1670     int size = min((1<<smsize), hwtexsize);
1671     shadowatlaspacker.resize(size, size);
1672 
1673     if(!shadowatlastex) glGenTextures(1, &shadowatlastex);
1674 
1675     shadowatlastarget = usegatherforsm() ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE;
1676     createtexture(shadowatlastex, shadowatlaspacker.w, shadowatlaspacker.h, NULL, 3, 1, smdepthprec > 1 ? GL_DEPTH_COMPONENT32 : (smdepthprec ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16), shadowatlastarget);
1677     glTexParameteri(shadowatlastarget, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
1678     glTexParameteri(shadowatlastarget, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
1679 
1680     if(!shadowatlasfbo) glGenFramebuffers_(1, &shadowatlasfbo);
1681 
1682     glBindFramebuffer_(GL_FRAMEBUFFER, shadowatlasfbo);
1683 
1684     glDrawBuffer(GL_NONE);
1685     glReadBuffer(GL_NONE);
1686 
1687     glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadowatlastarget, shadowatlastex, 0);
1688 
1689     if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
1690         fatal("failed allocating shadow atlas!");
1691 
1692     glBindFramebuffer_(GL_FRAMEBUFFER, 0);
1693 }
1694 
cleanupshadowatlas()1695 void cleanupshadowatlas()
1696 {
1697     if(shadowatlastex) { glDeleteTextures(1, &shadowatlastex); shadowatlastex = 0; }
1698     if(shadowatlasfbo) { glDeleteFramebuffers_(1, &shadowatlasfbo); shadowatlasfbo = 0; }
1699     clearshadowcache();
1700 }
1701 
1702 const matrix4 cubeshadowviewmatrix[6] =
1703 {
1704     // sign-preserving cubemap projections
1705     matrix4(vec(0, 0, 1), vec(0, 1, 0), vec(-1, 0, 0)), // +X
1706     matrix4(vec(0, 0, 1), vec(0, 1, 0), vec( 1, 0, 0)), // -X
1707     matrix4(vec(1, 0, 0), vec(0, 0, 1), vec(0, -1, 0)), // +Y
1708     matrix4(vec(1, 0, 0), vec(0, 0, 1), vec(0,  1, 0)), // -Y
1709     matrix4(vec(1, 0, 0), vec(0, 1, 0), vec(0, 0, -1)), // +Z
1710     matrix4(vec(1, 0, 0), vec(0, 1, 0), vec(0, 0,  1))  // -Z
1711 };
1712 
1713 FVAR(smpolyfactor, -1e3f, 1, 1e3f);
1714 FVAR(smpolyoffset, -1e3f, 0, 1e3f);
1715 FVAR(smbias, -1e6f, 0.01f, 1e6f);
1716 FVAR(smpolyfactor2, -1e3f, 1.5f, 1e3f);
1717 FVAR(smpolyoffset2, -1e3f, 0, 1e3f);
1718 FVAR(smbias2, -1e6f, 0.02f, 1e6f);
1719 FVAR(smprec, 1e-3f, 1, 1e3f);
1720 FVAR(smcubeprec, 1e-3f, 1, 1e3f);
1721 FVAR(smspotprec, 1e-3f, 1, 1e3f);
1722 
1723 VARFP(smsize, 10, 12, 14, cleanupshadowatlas());
1724 VARFP(smdepthprec, 0, 0, 2, cleanupshadowatlas());
1725 VAR(smsidecull, 0, 1, 1);
1726 VAR(smviscull, 0, 1, 1);
1727 VAR(smborder, 0, 3, 16);
1728 VAR(smborder2, 0, 4, 16);
1729 VAR(smminradius, 0, 16, 10000);
1730 VAR(smminsize, 1, 96, 1024);
1731 VAR(smmaxsize, 1, 384, 1024);
1732 //VAR(smmaxsize, 1, 4096, 4096);
1733 VAR(smused, 1, 0, 0);
1734 VAR(smquery, 0, 1, 1);
1735 VARF(smcullside, 0, 1, 1, cleanupshadowatlas());
1736 VARF(smcache, 0, 1, 2, cleanupshadowatlas());
1737 VARFP(smfilter, 0, 2, 3, { cleardeferredlightshaders(); cleanupshadowatlas(); });
1738 VARFP(smgather, 0, 0, 1, { cleardeferredlightshaders(); cleanupshadowatlas(); });
1739 VAR(smnoshadow, 0, 0, 1);
1740 VAR(smdynshadow, 0, 1, 1);
1741 VAR(lighttilesused, 1, 0, 0);
1742 VAR(lightpassesused, 1, 0, 0);
1743 
1744 int shadowmapping = 0;
1745 
1746 struct lightstrip
1747 {
1748     short x, y, w;
1749 
insidelightstrip1750     bool inside(int tx1, int ty1, int tx2, int ty2, const uint *tilemask) const
1751     {
1752         return x + w > tx1 && x < tx2 && y >= ty1 && y < ty2 && (!tilemask || (tilemask[y]>>x)&((1<<w)-1));
1753     }
1754 
extendlightstrip1755     bool extend(int ex, int ey)
1756     {
1757         if(y != ey || x+w != ex) return false;
1758         ++w;
1759         return true;
1760     }
1761 };
1762 
1763 struct lighttile
1764 {
1765     int band;
1766     vector<ushort> lights;
1767 
resetlighttile1768     void reset()
1769     {
1770         lights.setsize(0);
1771     }
1772 };
1773 
1774 struct lighttileslice
1775 {
1776     lighttile *tile;
1777     ushort priority, offset, numlights;
1778 
lighttileslicelighttileslice1779     lighttileslice() {}
lighttileslicelighttileslice1780     lighttileslice(lighttile *tile, int priority, int offset, int numlights) : tile(tile), priority(priority), offset(offset), numlights(numlights) {}
1781 };
1782 
1783 struct lightbatch : lighttileslice
1784 {
1785     vector<lightstrip> strips;
1786 
resetlightbatch1787     void reset()
1788     {
1789         strips.setsize(0);
1790     }
1791 
insidelightbatch1792     bool inside(int tx1, int ty1, int tx2, int ty2, const uint *tilemask) const
1793     {
1794         if(!tx1 && !ty1 && tx2 >= lighttilew && ty2 >= lighttileh && !tilemask) return true;
1795         loopv(strips) if(strips[i].inside(tx1, ty1, tx2, ty2, tilemask)) return true;
1796         return false;
1797     }
1798 };
1799 
htrecycle(lightbatch & l)1800 static inline void htrecycle(lightbatch &l)
1801 {
1802     l.reset();
1803 }
1804 
hthash(const lighttileslice & l)1805 static inline uint hthash(const lighttileslice &l)
1806 {
1807     uint h = 0;
1808     loopi(l.numlights) h = ((h<<8)+h)^l.tile->lights[l.offset + i];
1809     return h;
1810 }
1811 
htcmp(const lighttileslice & x,const lighttileslice & y)1812 static inline bool htcmp(const lighttileslice &x, const lighttileslice &y)
1813 {
1814     return x.tile->band == y.tile->band &&
1815            x.priority == y.priority &&
1816            x.numlights == y.numlights &&
1817            (!x.numlights || !memcmp(&x.tile->lights[x.offset], &y.tile->lights[y.offset], x.numlights*sizeof(ushort)));
1818 }
1819 
1820 vector<lightinfo> lights;
1821 vector<int> lightorder;
1822 lighttile lighttiles[LIGHTTILE_MAXH][LIGHTTILE_MAXW];
1823 hashset<lightbatch> lightbatcher(128);
1824 vector<lightbatch *> lightbatches;
1825 vector<shadowmapinfo> shadowmaps;
1826 
clearshadowcache()1827 void clearshadowcache()
1828 {
1829     shadowmaps.setsize(0);
1830 
1831     clearradiancehintscache();
1832     clearshadowmeshes();
1833 }
1834 
addshadowmap(ushort x,ushort y,int size,int & idx)1835 static shadowmapinfo *addshadowmap(ushort x, ushort y, int size, int &idx)
1836 {
1837     idx = shadowmaps.length();
1838     shadowmapinfo *sm = &shadowmaps.add();
1839     sm->x = x;
1840     sm->y = y;
1841     sm->size = size;
1842     sm->light = -1;
1843     sm->sidemask = 0;
1844     sm->cached = NULL;
1845     return sm;
1846 }
1847 
1848 #define CSM_MAXSPLITS 8
1849 
1850 VARF(csmmaxsize, 256, 768, 2048, clearshadowcache());
1851 VARF(csmsplits, 1, 3, CSM_MAXSPLITS, { cleardeferredlightshaders(); clearshadowcache(); });
1852 FVAR(csmsplitweight, 0.20f, 0.75f, 0.95f);
1853 VARF(csmshadowmap, 0, 1, 1, { cleardeferredlightshaders(); clearshadowcache(); });
1854 
1855 // cascaded shadow maps
1856 struct cascadedshadowmap
1857 {
1858     struct splitinfo
1859     {
1860         float nearplane;     // split distance to near plane
1861         float farplane;      // split distance to farplane
1862         matrix4 proj;      // one projection per split
1863         vec scale, offset;   // scale and offset of the projection
1864         int idx;             // shadowmapinfo indices
1865         vec center, bounds;  // max extents of shadowmap in sunlight model space
1866         plane cull[4];       // world space culling planes of the split's projected sides
1867     };
1868     matrix4 model;                // model view is shared by all splits
1869     splitinfo splits[CSM_MAXSPLITS]; // per-split parameters
1870     vec lightview;                  // view vector for light
1871     void setup();                   // insert shadowmaps for each split frustum if there is sunlight
1872     void updatesplitdist();         // compute split frustum distances
1873     void getmodelmatrix();          // compute the shared model matrix
1874     void getprojmatrix();           // compute each cropped projection matrix
1875     void gencullplanes();           // generate culling planes for the mvp matrix
1876     void bindparams();              // bind any shader params necessary for lighting
1877 };
1878 
setup()1879 void cascadedshadowmap::setup()
1880 {
1881     int size = (csmmaxsize * shadowatlaspacker.w) / SHADOWATLAS_SIZE;
1882     loopi(csmsplits)
1883     {
1884         ushort smx = USHRT_MAX, smy = USHRT_MAX;
1885         splits[i].idx = -1;
1886         if(shadowatlaspacker.insert(smx, smy, size, size))
1887             addshadowmap(smx, smy, size, splits[i].idx);
1888     }
1889     getmodelmatrix();
1890     getprojmatrix();
1891     gencullplanes();
1892 }
1893 
1894 VAR(csmnearplane, 1, 1, 16);
1895 VAR(csmfarplane, 64, 1024, 16384);
1896 FVAR(csmpradiustweak, 1e-3f, 1, 1e3f);
1897 FVAR(csmdepthrange, 0, 1024, 1e6f);
1898 FVAR(csmdepthmargin, 0, 0.1f, 1e3f);
1899 FVAR(csmpolyfactor, -1e3f, 2, 1e3f);
1900 FVAR(csmpolyoffset, -1e4f, 0, 1e4f);
1901 FVAR(csmbias, -1e6f, 1e-4f, 1e6f);
1902 FVAR(csmpolyfactor2, -1e3f, 3, 1e3f);
1903 FVAR(csmpolyoffset2, -1e4f, 0, 1e4f);
1904 FVAR(csmbias2, -1e16f, 2e-4f, 1e6f);
1905 VAR(csmcull, 0, 1, 1);
1906 
updatesplitdist()1907 void cascadedshadowmap::updatesplitdist()
1908 {
1909     float lambda = csmsplitweight, nd = csmnearplane, fd = csmfarplane, ratio = fd/nd;
1910     splits[0].nearplane = nd;
1911     for(int i = 1; i < csmsplits; ++i)
1912     {
1913         float si = i / float(csmsplits);
1914         splits[i].nearplane = lambda*(nd*pow(ratio, si)) + (1-lambda)*(nd + (fd - nd)*si);
1915         splits[i-1].farplane = splits[i].nearplane * 1.005f;
1916     }
1917     splits[csmsplits-1].farplane = fd;
1918 }
1919 
getmodelmatrix()1920 void cascadedshadowmap::getmodelmatrix()
1921 {
1922     model = viewmatrix;
1923     model.rotate_around_x(sunlightpitch*RAD);
1924     model.rotate_around_z((180-sunlightyaw)*RAD);
1925 }
1926 
getprojmatrix()1927 void cascadedshadowmap::getprojmatrix()
1928 {
1929     lightview = vec(sunlightdir).neg();
1930 
1931     // compute the split frustums
1932     updatesplitdist();
1933 
1934     // find z extent
1935     float minz = lightview.project_bb(worldmin, worldmax), maxz = lightview.project_bb(worldmax, worldmin),
1936           zmargin = max((maxz - minz)*csmdepthmargin, 0.5f*(csmdepthrange - (maxz - minz)));
1937     minz -= zmargin;
1938     maxz += zmargin;
1939 
1940     // compute each split projection matrix
1941     loopi(csmsplits)
1942     {
1943         splitinfo &split = splits[i];
1944         if(split.idx < 0) continue;
1945         const shadowmapinfo &sm = shadowmaps[split.idx];
1946 
1947         vec c;
1948         float radius = calcfrustumboundsphere(split.nearplane, split.farplane, camera1->o, camdir, c);
1949 
1950         // compute the projected bounding box of the sphere
1951         vec tc;
1952         model.transform(c, tc);
1953         int border = smfilter > 2 ? smborder2 : smborder;
1954         const float pradius = ceil(radius * csmpradiustweak), step = (2*pradius) / (sm.size - 2*border);
1955         vec2 offset = vec2(tc).sub(pradius).div(step);
1956         offset.x = floor(offset.x);
1957         offset.y = floor(offset.y);
1958         split.center = vec(vec2(offset).mul(step).add(pradius), -0.5f*(minz + maxz));
1959         split.bounds = vec(pradius, pradius, 0.5f*(maxz - minz));
1960 
1961         // modify mvp with a scale and offset
1962         // now compute the update model view matrix for this split
1963         split.scale = vec(1/step, 1/step, -1/(maxz - minz));
1964         split.offset = vec(border - offset.x, border - offset.y, -minz/(maxz - minz));
1965 
1966         split.proj.identity();
1967         split.proj.settranslation(2*split.offset.x/sm.size - 1, 2*split.offset.y/sm.size - 1, 2*split.offset.z - 1);
1968         split.proj.setscale(2*split.scale.x/sm.size, 2*split.scale.y/sm.size, 2*split.scale.z);
1969 
1970         const float bias = (smfilter > 2 ? csmbias2 : csmbias) * (-512.0f / sm.size) * (split.farplane - split.nearplane) / (splits[0].farplane - splits[0].nearplane);
1971         split.offset.add(vec(sm.x, sm.y, bias));
1972     }
1973 }
1974 
gencullplanes()1975 void cascadedshadowmap::gencullplanes()
1976 {
1977     loopi(csmsplits)
1978     {
1979         splitinfo &split = splits[i];
1980         matrix4 mvp;
1981         mvp.mul(split.proj, model);
1982         vec4 px = mvp.rowx(), py = mvp.rowy(), pw = mvp.roww();
1983         split.cull[0] = plane(vec4(pw).add(px)).normalize(); // left plane
1984         split.cull[1] = plane(vec4(pw).sub(px)).normalize(); // right plane
1985         split.cull[2] = plane(vec4(pw).add(py)).normalize(); // bottom plane
1986         split.cull[3] = plane(vec4(pw).sub(py)).normalize(); // top plane
1987     }
1988 }
1989 
bindparams()1990 void cascadedshadowmap::bindparams()
1991 {
1992     static GlobalShaderParam splitcenter("splitcenter"), splitbounds("splitbounds"), splitscale("splitscale"), splitoffset("splitoffset");
1993     vec *splitcenterv = splitcenter.reserve<vec>(csmsplits),
1994         *splitboundsv = splitbounds.reserve<vec>(csmsplits),
1995         *splitscalev = splitscale.reserve<vec>(csmsplits),
1996         *splitoffsetv = splitoffset.reserve<vec>(csmsplits);
1997     loopi(csmsplits)
1998     {
1999         cascadedshadowmap::splitinfo &split = splits[i];
2000         if(split.idx < 0) continue;
2001         splitcenterv[i] = split.center;
2002         splitboundsv[i] = split.bounds;
2003         splitscalev[i] = split.scale;
2004         splitoffsetv[i] = split.offset;
2005     }
2006     GLOBALPARAM(csmmatrix, matrix3(model));
2007 }
2008 
2009 cascadedshadowmap csm;
2010 
calcbbcsmsplits(const ivec & bbmin,const ivec & bbmax)2011 int calcbbcsmsplits(const ivec &bbmin, const ivec &bbmax)
2012 {
2013     int mask = (1<<csmsplits)-1;
2014     if(!csmcull) return mask;
2015     loopi(csmsplits)
2016     {
2017         const cascadedshadowmap::splitinfo &split = csm.splits[i];
2018         int k;
2019         for(k = 0; k < 4; k++)
2020         {
2021             const plane &p = split.cull[k];
2022             ivec omin, omax;
2023             if(p.x > 0) { omin.x = bbmin.x; omax.x = bbmax.x; } else { omin.x = bbmax.x; omax.x = bbmin.x; }
2024             if(p.y > 0) { omin.y = bbmin.y; omax.y = bbmax.y; } else { omin.y = bbmax.y; omax.y = bbmin.y; }
2025             if(p.z > 0) { omin.z = bbmin.z; omax.z = bbmax.z; } else { omin.z = bbmax.z; omax.z = bbmin.z; }
2026             if(omax.dist(p) < 0) { mask &= ~(1<<i); goto nextsplit; }
2027             if(omin.dist(p) < 0) goto notinside;
2028         }
2029         mask &= (2<<i)-1;
2030         break;
2031     notinside:
2032         while(++k < 4)
2033         {
2034             const plane &p = split.cull[k];
2035             ivec omax(p.x > 0 ? bbmax.x : bbmin.x, p.y > 0 ? bbmax.y : bbmin.y, p.z > 0 ? bbmax.z : bbmin.z);
2036             if(omax.dist(p) < 0) { mask &= ~(1<<i); break; }
2037         }
2038     nextsplit:;
2039     }
2040     return mask;
2041 }
2042 
calcspherecsmsplits(const vec & center,float radius)2043 int calcspherecsmsplits(const vec &center, float radius)
2044 {
2045     int mask = (1<<csmsplits)-1;
2046     if(!csmcull) return mask;
2047     loopi(csmsplits)
2048     {
2049         const cascadedshadowmap::splitinfo &split = csm.splits[i];
2050         int k;
2051         for(k = 0; k < 4; k++)
2052         {
2053             const plane &p = split.cull[k];
2054             float dist = p.dist(center);
2055             if(dist < -radius) { mask &= ~(1<<i); goto nextsplit; }
2056             if(dist < radius) goto notinside;
2057         }
2058         mask &= (2<<i)-1;
2059         break;
2060     notinside:
2061         while(++k < 4)
2062         {
2063             const plane &p = split.cull[k];
2064             if(p.dist(center) < -radius) { mask &= ~(1<<i); break; }
2065         }
2066     nextsplit:;
2067     }
2068     return mask;
2069 }
2070 
2071 struct reflectiveshadowmap
2072 {
2073     matrix4 model, proj;
2074     vec lightview;
2075     plane cull[4];
2076     vec scale, offset;
2077     vec center, bounds;
2078     void setup();
2079     void getmodelmatrix();
2080     void getprojmatrix();
2081     void gencullplanes();
2082 } rsm;
2083 
setup()2084 void reflectiveshadowmap::setup()
2085 {
2086     getmodelmatrix();
2087     getprojmatrix();
2088     gencullplanes();
2089 }
2090 
getmodelmatrix()2091 void reflectiveshadowmap::getmodelmatrix()
2092 {
2093     model = viewmatrix;
2094     model.rotate_around_x(sunlightpitch*RAD);
2095     model.rotate_around_z((180-sunlightyaw)*RAD);
2096 }
2097 
getprojmatrix()2098 void reflectiveshadowmap::getprojmatrix()
2099 {
2100     lightview = vec(sunlightdir).neg();
2101 
2102     // find z extent
2103     float minz = lightview.project_bb(worldmin, worldmax), maxz = lightview.project_bb(worldmax, worldmin),
2104           zmargin = max((maxz - minz)*rsmdepthmargin, 0.5f*(rsmdepthrange - (maxz - minz)));
2105     minz -= zmargin;
2106     maxz += zmargin;
2107 
2108     vec c;
2109     float radius = calcfrustumboundsphere(rhnearplane, rhfarplane, camera1->o, camdir, c);
2110 
2111     // compute the projected bounding box of the sphere
2112     vec tc;
2113     model.transform(c, tc);
2114     const float pradius = ceil((radius + gidist) * rsmpradiustweak), step = (2*pradius) / rsmsize;
2115     vec2 tcoff = vec2(tc).sub(pradius).div(step);
2116     tcoff.x = floor(tcoff.x);
2117     tcoff.y = floor(tcoff.y);
2118     center = vec(vec2(tcoff).mul(step).add(pradius), -0.5f*(minz + maxz));
2119     bounds = vec(pradius, pradius, 0.5f*(maxz - minz));
2120 
2121     scale = vec(1/step, 1/step, -1/(maxz - minz));
2122     offset = vec(-tcoff.x, -tcoff.y, -minz/(maxz - minz));
2123 
2124     proj.identity();
2125     proj.settranslation(2*offset.x/rsmsize - 1, 2*offset.y/rsmsize - 1, 2*offset.z - 1);
2126     proj.setscale(2*scale.x/rsmsize, 2*scale.y/rsmsize, 2*scale.z);
2127 }
2128 
gencullplanes()2129 void reflectiveshadowmap::gencullplanes()
2130 {
2131     matrix4 mvp;
2132     mvp.mul(proj, model);
2133     vec4 px = mvp.rowx(), py = mvp.rowy(), pw = mvp.roww();
2134     cull[0] = plane(vec4(pw).add(px)).normalize(); // left plane
2135     cull[1] = plane(vec4(pw).sub(px)).normalize(); // right plane
2136     cull[2] = plane(vec4(pw).add(py)).normalize(); // bottom plane
2137     cull[3] = plane(vec4(pw).sub(py)).normalize(); // top plane
2138 }
2139 
calcbbrsmsplits(const ivec & bbmin,const ivec & bbmax)2140 int calcbbrsmsplits(const ivec &bbmin, const ivec &bbmax)
2141 {
2142     if(!rsmcull) return 1;
2143     loopk(4)
2144     {
2145         const plane &p = rsm.cull[k];
2146         ivec omin, omax;
2147         if(p.x > 0) { omin.x = bbmin.x; omax.x = bbmax.x; } else { omin.x = bbmax.x; omax.x = bbmin.x; }
2148         if(p.y > 0) { omin.y = bbmin.y; omax.y = bbmax.y; } else { omin.y = bbmax.y; omax.y = bbmin.y; }
2149         if(p.z > 0) { omin.z = bbmin.z; omax.z = bbmax.z; } else { omin.z = bbmax.z; omax.z = bbmin.z; }
2150         if(omax.dist(p) < 0) return 0;
2151         if(omin.dist(p) < 0) while(++k < 4)
2152         {
2153             const plane &p = rsm.cull[k];
2154             ivec omax(p.x > 0 ? bbmax.x : bbmin.x, p.y > 0 ? bbmax.y : bbmin.y, p.z > 0 ? bbmax.z : bbmin.z);
2155             if(omax.dist(p) < 0) return 0;
2156         }
2157     }
2158     return 1;
2159 }
2160 
calcspherersmsplits(const vec & center,float radius)2161 int calcspherersmsplits(const vec &center, float radius)
2162 {
2163     if(!rsmcull) return 1;
2164     loopk(4)
2165     {
2166         const plane &p = rsm.cull[k];
2167         float dist = p.dist(center);
2168         if(dist < -radius) return 0;
2169         if(dist < radius) while(++k < 4)
2170         {
2171             const plane &p = rsm.cull[k];
2172             if(p.dist(center) < -radius) return 0;
2173         }
2174     }
2175     return 1;
2176 }
2177 
2178 struct radiancehints
2179 {
2180     struct splitinfo
2181     {
2182         float nearplane, farplane;
2183         vec offset, scale;
2184         vec center; float bounds;
2185         vec cached;
2186 
splitinforadiancehints::splitinfo2187         splitinfo() : center(-1e16f, -1e16f, -1e16f), bounds(-1e16f), cached(-1e16f, -1e16f, -1e16f) {}
2188 
clearcacheradiancehints::splitinfo2189         void clearcache() { bounds = -1e16f; }
2190     } splits[RH_MAXSPLITS];
2191 
2192     vec dynmin, dynmax, prevdynmin, prevdynmax;
2193 
radiancehintsradiancehints2194     radiancehints() : dynmin(1e16f, 1e16f, 1e16f), dynmax(-1e16f, -1e16f, -1e16f), prevdynmin(1e16f, 1e16f, 1e16f), prevdynmax(-1e16f, -1e16f, -1e16f) {}
2195 
2196     void setup();
2197     void updatesplitdist();
2198     void bindparams();
2199     void renderslices();
2200 
clearcacheradiancehints2201     void clearcache() { loopi(RH_MAXSPLITS) splits[i].clearcache(); }
allcachedradiancehints2202     bool allcached() const { loopi(rhsplits) if(splits[i].cached != splits[i].center) return false; return true; }
2203 } rh;
2204 
clearradiancehintscache()2205 void clearradiancehintscache()
2206 {
2207     rh.clearcache();
2208     memset(rhclearmasks, 0, sizeof(rhclearmasks));
2209 }
2210 
updatesplitdist()2211 void radiancehints::updatesplitdist()
2212 {
2213     float lambda = rhsplitweight, nd = rhnearplane, fd = rhfarplane, ratio = fd/nd;
2214     splits[0].nearplane = nd;
2215     for(int i = 1; i < rhsplits; ++i)
2216     {
2217         float si = i / float(rhsplits);
2218         splits[i].nearplane = lambda*(nd*pow(ratio, si)) + (1-lambda)*(nd + (fd - nd)*si);
2219         splits[i-1].farplane = splits[i].nearplane * 1.005f;
2220     }
2221     splits[rhsplits-1].farplane = fd;
2222 }
2223 
setup()2224 void radiancehints::setup()
2225 {
2226     updatesplitdist();
2227 
2228     loopi(rhsplits)
2229     {
2230         splitinfo &split = splits[i];
2231 
2232         vec c;
2233         float radius = calcfrustumboundsphere(split.nearplane, split.farplane, camera1->o, camdir, c);
2234 
2235         // compute the projected bounding box of the sphere
2236         const float pradius = ceil(radius * rhpradiustweak), step = (2*pradius) / rhgrid;
2237         vec offset = vec(c).sub(pradius).div(step);
2238         offset.x = floor(offset.x);
2239         offset.y = floor(offset.y);
2240         offset.z = floor(offset.z);
2241         split.cached = split.bounds == pradius ? split.center : vec(-1e16f, -1e16f, -1e16f);
2242         split.center = vec(offset).mul(step).add(pradius);
2243         split.bounds = pradius;
2244 
2245         // modify mvp with a scale and offset
2246         // now compute the update model view matrix for this split
2247         split.scale = vec(1/(step*(rhgrid+2*rhborder)), 1/(step*(rhgrid+2*rhborder)), 1/(step*(rhgrid+2*rhborder)*rhsplits));
2248         split.offset = vec(-(offset.x-rhborder)/(rhgrid+2*rhborder), -(offset.y-rhborder)/(rhgrid+2*rhborder), (i - (offset.z-rhborder)/(rhgrid+2*rhborder))/float(rhsplits));
2249     }
2250 }
2251 
bindparams()2252 void radiancehints::bindparams()
2253 {
2254     static GlobalShaderParam rhbb("rhbb"), rhscale("rhscale"), rhoffset("rhoffset");
2255     vec4 *rhbbv = rhbb.reserve<vec4>(rhsplits);
2256     vec *rhscalev = rhscale.reserve<vec>(rhsplits),
2257         *rhoffsetv = rhoffset.reserve<vec>(rhsplits);
2258     loopi(rhsplits)
2259     {
2260         splitinfo &split = splits[i];
2261         rhbbv[i] = vec4(split.center, split.bounds*(1 + rhborder*2*0.5f/rhgrid));
2262         rhscalev[i] = split.scale;
2263         rhoffsetv[i] = split.offset;
2264     }
2265     float step = 2*splits[0].bounds/rhgrid;
2266     GLOBALPARAMF(rhnudge, rhnudge*step);
2267 }
2268 
useradiancehints()2269 bool useradiancehints()
2270 {
2271     return !sunlight.iszero() && csmshadowmap && gi && giscale && gidist;
2272 }
2273 
2274 FVAR(avatarshadowdist, 0, 8, 100);
2275 FVAR(avatarshadowbias, 0, 8, 100);
2276 VARF(avatarshadowstencil, 0, 1, 2, initwarning("g-buffer setup", INIT_LOAD, CHANGE_SHADERS));
2277 
2278 int avatarmask = 0;
2279 
useavatarmask()2280 bool useavatarmask() { return avatarshadowstencil && ghasstencil && (!msaasamples || avatarshadowstencil > 1); }
2281 
enableavatarmask()2282 void enableavatarmask()
2283 {
2284     if(useavatarmask())
2285     {
2286         avatarmask = 0x40;
2287         glStencilFunc(GL_ALWAYS, avatarmask, ~0);
2288         glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
2289         glEnable(GL_STENCIL_TEST);
2290     }
2291 }
2292 
disableavatarmask()2293 void disableavatarmask()
2294 {
2295     if(avatarmask)
2296     {
2297         avatarmask = 0;
2298         glDisable(GL_STENCIL_TEST);
2299     }
2300 }
2301 
2302 static Shader *deferredlightshader = NULL, *deferredminimapshader = NULL, *deferredmsaapixelshader = NULL, *deferredmsaasampleshader = NULL;
2303 
cleardeferredlightshaders()2304 void cleardeferredlightshaders()
2305 {
2306     deferredlightshader = NULL;
2307     deferredminimapshader = NULL;
2308     deferredmsaapixelshader = NULL;
2309     deferredmsaasampleshader = NULL;
2310 }
2311 
2312 VARF(lighttilebatch, 0, 8, 8, cleardeferredlightshaders());
2313 VARF(batchsunlight, 0, 2, 2, cleardeferredlightshaders());
2314 
2315 extern int spotlights;
2316 
loaddeferredlightshader(const char * type=NULL)2317 Shader *loaddeferredlightshader(const char *type = NULL)
2318 {
2319     string common, shadow, sun;
2320     int commonlen = 0, shadowlen = 0, sunlen = 0;
2321 
2322     bool minimap = false;
2323     if(type)
2324     {
2325         if(type[0] == 'm') minimap = true;
2326         copystring(common, type);
2327         commonlen = strlen(common);
2328     }
2329     if(lighttilebatch)
2330     {
2331         common[commonlen++] = 'n';
2332         common[commonlen++] = '0' + lighttilebatch;
2333     }
2334     if(usegatherforsm()) common[commonlen++] = smfilter > 2 ? 'G' : 'g';
2335     else if(smfilter) common[commonlen++] = smfilter > 2 ? 'E' : (smfilter > 1 ? 'F' : 'f');
2336     if(spotlights) common[commonlen++] = 's';
2337     if(!minimap) common[commonlen++] = 't';
2338     if(useavatarmask()) common[commonlen++] = 'd';
2339     common[commonlen] = '\0';
2340 
2341     shadow[shadowlen++] = 'p';
2342     shadow[shadowlen] = '\0';
2343 
2344     int usecsm = 0, userh = 0;
2345     if(!minimap && ao) sun[sunlen++] = 'a';
2346     if(!sunlight.iszero() && csmshadowmap)
2347     {
2348         usecsm = csmsplits;
2349         sun[sunlen++] = 'c';
2350         sun[sunlen++] = '0' + csmsplits;
2351         if(!minimap)
2352         {
2353             if(ao && aosun) sun[sunlen++] = 'A';
2354             if(gi && giscale && gidist)
2355             {
2356                 userh = rhsplits;
2357                 sun[sunlen++] = 'r';
2358                 sun[sunlen++] = '0' + rhsplits;
2359             }
2360         }
2361     }
2362     if(lighttilebatch && (!usecsm || batchsunlight > (userh ? 1 : 0))) sun[sunlen++] = 'b';
2363     sun[sunlen] = '\0';
2364 
2365     defformatstring(name, "deferredlight%s%s%s", common, shadow, sun);
2366     return generateshader(name, "deferredlightshader \"%s\" \"%s\" \"%s\" %d %d %d", common, shadow, sun, usecsm, userh, lighttilebatch);
2367 }
2368 
loaddeferredlightshaders()2369 void loaddeferredlightshaders()
2370 {
2371     if(msaasamples)
2372     {
2373         string opts;
2374         if(hasMSS) copystring(opts, "MS");
2375         else if(msaasamples==2) copystring(opts, ghasstencil || !msaaedgedetect ? "MO" : "MOT");
2376         else formatstring(opts, ghasstencil || !msaaedgedetect ? "MR%d" : "MRT%d", msaasamples);
2377         deferredmsaasampleshader = loaddeferredlightshader(opts);
2378         deferredmsaapixelshader = loaddeferredlightshader("M");
2379         deferredlightshader = deferredmsaapixelshader;
2380     }
2381     else deferredlightshader = loaddeferredlightshader();
2382 }
2383 
sortlights(int x,int y)2384 static inline bool sortlights(int x, int y)
2385 {
2386     const lightinfo &xl = lights[x], &yl = lights[y];
2387     if(!xl.spot) { if(yl.spot) return true; }
2388     else if(!yl.spot) return false;
2389     if(!xl.noshadow()) { if(yl.noshadow()) return true; }
2390     else if(!yl.noshadow()) return false;
2391     if(xl.sz1 < yl.sz1) return true;
2392     else if(xl.sz1 > yl.sz1) return false;
2393     return xl.dist - xl.radius < yl.dist - yl.radius;
2394 }
2395 
2396 VAR(lighttilealignw, 1, 16, 256);
2397 VAR(lighttilealignh, 1, 16, 256);
2398 VARN(lighttilew, lighttilemaxw, 1, 10, LIGHTTILE_MAXW);
2399 VARN(lighttileh, lighttilemaxh, 1, 10, LIGHTTILE_MAXH);
2400 
2401 int lighttilew = 0, lighttileh = 0, lighttilevieww = 0, lighttileviewh = 0;
2402 
calctilesize()2403 void calctilesize()
2404 {
2405     lighttilevieww = (vieww + lighttilealignw - 1)/lighttilealignw;
2406     lighttileviewh = (viewh + lighttilealignh - 1)/lighttilealignh;
2407     lighttilew = min(lighttilevieww, lighttilemaxw);
2408     lighttileh = min(lighttileviewh, lighttilemaxh);
2409 }
2410 
resetlights()2411 void resetlights()
2412 {
2413     shadowcache.reset();
2414     if(smcache)
2415     {
2416         int evictx = ((evictshadowcache%SHADOWCACHE_EVICT)*shadowatlaspacker.w)/SHADOWCACHE_EVICT,
2417             evicty = ((evictshadowcache/SHADOWCACHE_EVICT)*shadowatlaspacker.h)/SHADOWCACHE_EVICT,
2418             evictx2 = (((evictshadowcache%SHADOWCACHE_EVICT)+1)*shadowatlaspacker.w)/SHADOWCACHE_EVICT,
2419             evicty2 = (((evictshadowcache/SHADOWCACHE_EVICT)+1)*shadowatlaspacker.h)/SHADOWCACHE_EVICT;
2420         loopv(shadowmaps)
2421         {
2422             shadowmapinfo &sm = shadowmaps[i];
2423             if(sm.light < 0) continue;
2424             lightinfo &l = lights[sm.light];
2425             if(sm.cached && shadowcachefull)
2426             {
2427                 int w = l.spot ? sm.size : sm.size*3, h = l.spot ? sm.size : sm.size*2;
2428                 if(sm.x < evictx2 && sm.x + w > evictx && sm.y < evicty2 && sm.y + h > evicty) continue;
2429             }
2430             shadowcache[l] = sm;
2431         }
2432         if(shadowcachefull)
2433         {
2434             evictshadowcache = (evictshadowcache + 1)%(SHADOWCACHE_EVICT*SHADOWCACHE_EVICT);
2435             shadowcachefull = false;
2436         }
2437     }
2438 
2439     lights.setsize(0);
2440     lightorder.setsize(0);
2441     loopi(LIGHTTILE_MAXH) loopj(LIGHTTILE_MAXW) lighttiles[i][j].reset();
2442 
2443     shadowmaps.setsize(0);
2444     shadowatlaspacker.reset();
2445 
2446     calctilesize();
2447 }
2448 
2449 static vec *lightsphereverts = NULL;
2450 static GLushort *lightsphereindices = NULL;
2451 static int lightspherenumverts = 0, lightspherenumindices = 0;
2452 static GLuint lightspherevbuf = 0, lightsphereebuf = 0;
2453 
initlightsphere(int slices,int stacks)2454 static void initlightsphere(int slices, int stacks)
2455 {
2456     lightspherenumverts = (stacks+1)*(slices+1);
2457     lightsphereverts = new vec[lightspherenumverts];
2458     float ds = 1.0f/slices, dt = 1.0f/stacks, t = 1.0f;
2459     loopi(stacks+1)
2460     {
2461         float rho = M_PI*(1-t), s = 0.0f;
2462         loopj(slices+1)
2463         {
2464             float theta = j==slices ? 0 : 2*M_PI*s;
2465             lightsphereverts[i*(slices+1) + j] = vec(-sin(theta)*sin(rho), -cos(theta)*sin(rho), cos(rho));
2466             s += ds;
2467         }
2468         t -= dt;
2469     }
2470 
2471     lightspherenumindices = stacks*slices*3*2;
2472     lightsphereindices = new ushort[lightspherenumindices];
2473     GLushort *curindex = lightsphereindices;
2474     loopi(stacks)
2475     {
2476         loopk(slices)
2477         {
2478             int j = i%2 ? slices-k-1 : k;
2479 
2480             *curindex++ = i*(slices+1)+j;
2481             *curindex++ = i*(slices+1)+j+1;
2482             *curindex++ = (i+1)*(slices+1)+j;
2483 
2484             *curindex++ = i*(slices+1)+j+1;
2485             *curindex++ = (i+1)*(slices+1)+j+1;
2486             *curindex++ = (i+1)*(slices+1)+j;
2487         }
2488     }
2489 
2490     if(!lightspherevbuf) glGenBuffers_(1, &lightspherevbuf);
2491     glBindBuffer_(GL_ARRAY_BUFFER, lightspherevbuf);
2492     glBufferData_(GL_ARRAY_BUFFER, lightspherenumverts*sizeof(vec), lightsphereverts, GL_STATIC_DRAW);
2493     DELETEA(lightsphereverts);
2494 
2495     if(!lightsphereebuf) glGenBuffers_(1, &lightsphereebuf);
2496     glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, lightsphereebuf);
2497     glBufferData_(GL_ELEMENT_ARRAY_BUFFER, lightspherenumindices*sizeof(GLushort), lightsphereindices, GL_STATIC_DRAW);
2498     DELETEA(lightsphereindices);
2499 }
2500 
cleanuplightsphere()2501 void cleanuplightsphere()
2502 {
2503     if(lightspherevbuf) { glDeleteBuffers_(1, &lightspherevbuf); lightspherevbuf = 0; }
2504     if(lightsphereebuf) { glDeleteBuffers_(1, &lightsphereebuf); lightsphereebuf = 0; }
2505 }
2506 
2507 VAR(depthtestlights, 0, 2, 2);
2508 FVAR(depthtestlightsclamp, 0, 0.999995f, 1);
2509 VAR(depthfaillights, 0, 1, 1);
2510 FVAR(lightradiustweak, 1, 1.11f, 2);
2511 
2512 VAR(lighttilestrip, 0, 1, 1);
2513 VAR(lighttilebands, 0, 1, LIGHTTILE_MAXH);
2514 
lightquad(float z=-1,float sx1=-1,float sy1=-1,float sx2=1,float sy2=1)2515 static inline void lightquad(float z = -1, float sx1 = -1, float sy1 = -1, float sx2 = 1, float sy2 = 1)
2516 {
2517     gle::begin(GL_TRIANGLE_STRIP);
2518     gle::attribf(sx2, sy1, z);
2519     gle::attribf(sx1, sy1, z);
2520     gle::attribf(sx2, sy2, z);
2521     gle::attribf(sx1, sy2, z);
2522     gle::end();
2523 }
2524 
lightquads(float z,float sx1,float sy1,float sx2,float sy2)2525 static inline void lightquads(float z, float sx1, float sy1, float sx2, float sy2)
2526 {
2527     gle::attribf(sx1, sy2, z);
2528     gle::attribf(sx2, sy2, z);
2529     gle::attribf(sx2, sy1, z);
2530     gle::attribf(sx1, sy1, z);
2531 }
2532 
lightquad(float sz1,float bsx1,float bsy1,float bsx2,float bsy2,const uint * tilemask)2533 static void lightquad(float sz1, float bsx1, float bsy1, float bsx2, float bsy2, const uint *tilemask)
2534 {
2535     int btx1, bty1, btx2, bty2;
2536     calctilebounds(bsx1, bsy1, bsx2, bsy2, btx1, bty1, btx2, bty2);
2537 
2538     gle::begin(GL_QUADS);
2539     for(int y = bty1; y < bty2; y++) for(int x = btx1, end = btx2; x < end;)
2540     {
2541         int start;
2542         if(tilemask)
2543         {
2544             while(x < end && !(tilemask[y]&(1<<x))) x++;
2545             if(x >= end) break;
2546             start = x;
2547             do ++x; while(x < end && tilemask[y]&(1<<x));
2548         }
2549         else
2550         {
2551             start = x;
2552             x = end;
2553         }
2554         int tx1 = max(int(floor((bsx1*0.5f+0.5f)*vieww)), ((start*lighttilevieww)/lighttilew)*lighttilealignw),
2555             ty1 = max(int(floor((bsy1*0.5f+0.5f)*viewh)), ((y*lighttileviewh)/lighttileh)*lighttilealignh),
2556             tx2 = min(int(ceil((bsx2*0.5f+0.5f)*vieww)), min(((x*lighttilevieww)/lighttilew)*lighttilealignw, vieww)),
2557             ty2 = min(int(ceil((bsy2*0.5f+0.5f)*viewh)), min((((y+1)*lighttileviewh)/lighttileh)*lighttilealignh, viewh));
2558         lightquads(sz1, (tx1*2.0f)/vieww-1.0f, (ty1*2.0f)/viewh-1.0f, (tx2*2.0f)/vieww-1.0f, (ty2*2.0f)/viewh-1.0f);
2559     }
2560     gle::end();
2561 }
2562 
bindlighttexs(int msaapass=0,bool transparent=false)2563 static void bindlighttexs(int msaapass = 0, bool transparent = false)
2564 {
2565     if(msaapass) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mscolortex);
2566     else glBindTexture(GL_TEXTURE_RECTANGLE, gcolortex);
2567     glActiveTexture_(GL_TEXTURE1);
2568     if(msaapass) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msnormaltex);
2569     else glBindTexture(GL_TEXTURE_RECTANGLE, gnormaltex);
2570     if(transparent)
2571     {
2572         glActiveTexture_(GL_TEXTURE2);
2573         if(msaapass) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msglowtex);
2574         else glBindTexture(GL_TEXTURE_RECTANGLE, gglowtex);
2575     }
2576     glActiveTexture_(GL_TEXTURE3);
2577     if(msaapass) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
2578     else glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
2579     glActiveTexture_(GL_TEXTURE4);
2580     glBindTexture(shadowatlastarget, shadowatlastex);
2581     if(usesmcomparemode()) setsmcomparemode(); else setsmnoncomparemode();
2582     if(ao)
2583     {
2584         glActiveTexture_(GL_TEXTURE5);
2585         glBindTexture(GL_TEXTURE_RECTANGLE, aotex[2] ? aotex[2] : aotex[0]);
2586     }
2587     if(useradiancehints()) loopi(4)
2588     {
2589         glActiveTexture_(GL_TEXTURE6 + i);
2590         glBindTexture(GL_TEXTURE_3D, rhtex[i]);
2591     }
2592     glActiveTexture_(GL_TEXTURE0);
2593 }
2594 
setlightglobals(bool transparent=false)2595 static inline void setlightglobals(bool transparent = false)
2596 {
2597     GLOBALPARAMF(shadowatlasscale, 1.0f/shadowatlaspacker.w, 1.0f/shadowatlaspacker.h);
2598     if(ao)
2599     {
2600         if(transparent || drawtex || (editmode && fullbright))
2601         {
2602             GLOBALPARAMF(aoscale, 0.0f, 0.0f);
2603             GLOBALPARAMF(aoparams, 1.0f, 0.0f, 1.0f, 0.0f);
2604         }
2605         else
2606         {
2607             GLOBALPARAM(aoscale, aotex[2] ? vec2(1, 1) : vec2(float(aow)/vieww, float(aoh)/viewh));
2608             GLOBALPARAMF(aoparams, aomin, 1.0f-aomin, aosunmin, 1.0f-aosunmin);
2609         }
2610     }
2611     float lightscale = 2.0f*ldrscaleb;
2612     if(!drawtex && editmode && fullbright)
2613         GLOBALPARAMF(lightscale, fullbrightlevel*lightscale, fullbrightlevel*lightscale, fullbrightlevel*lightscale, 255*lightscale);
2614     else
2615         GLOBALPARAMF(lightscale, ambient.x*lightscale*ambientscale, ambient.y*lightscale*ambientscale, ambient.z*lightscale*ambientscale, 255*lightscale);
2616 
2617     if(!sunlight.iszero() && csmshadowmap)
2618     {
2619         csm.bindparams();
2620         rh.bindparams();
2621         if(!drawtex && editmode && fullbright)
2622         {
2623             GLOBALPARAMF(sunlightdir, 0, 0, 0);
2624             GLOBALPARAMF(sunlightcolor, 0, 0, 0);
2625             GLOBALPARAMF(giscale, 0);
2626             GLOBALPARAMF(skylightcolor, 0, 0, 0);
2627         }
2628         else
2629         {
2630             GLOBALPARAM(sunlightdir, sunlightdir);
2631             GLOBALPARAMF(sunlightcolor, sunlight.x*lightscale*sunlightscale, sunlight.y*lightscale*sunlightscale, sunlight.z*lightscale*sunlightscale);
2632             GLOBALPARAMF(giscale, 2*giscale);
2633             GLOBALPARAMF(skylightcolor, 2*giaoscale*skylight.x*lightscale*skylightscale, 2*giaoscale*skylight.y*lightscale*skylightscale, 2*giaoscale*skylight.z*lightscale*skylightscale);
2634         }
2635     }
2636 
2637     matrix4 lightmatrix;
2638     lightmatrix.identity();
2639     GLOBALPARAM(lightmatrix, lightmatrix);
2640 }
2641 
2642 static LocalShaderParam lightpos("lightpos"), lightcolor("lightcolor"), spotparams("spotparams"), shadowparams("shadowparams"), shadowoffset("shadowoffset");
2643 static vec4 lightposv[8], spotparamsv[8], shadowparamsv[8];
2644 static vec lightcolorv[8];
2645 static vec2 shadowoffsetv[8];
2646 
setlightparams(int i,const lightinfo & l)2647 static inline void setlightparams(int i, const lightinfo &l)
2648 {
2649     lightposv[i] = vec4(l.o, 1).div(l.radius);
2650     lightcolorv[i] = vec(l.color).mul(2*ldrscaleb);
2651     if(l.spot > 0) spotparamsv[i] = vec4(l.dir, 1/(1 - cos360(l.spot)));
2652     if(l.shadowmap >= 0)
2653     {
2654         shadowmapinfo &sm = shadowmaps[l.shadowmap];
2655         float smnearclip = SQRT3 / l.radius, smfarclip = SQRT3,
2656               bias = (smfilter > 2 ? smbias2 : smbias) * (smcullside ? 1 : -1) * smnearclip * (1024.0f / sm.size);
2657         int border = smfilter > 2 ? smborder2 : smborder;
2658         if(l.spot > 0)
2659         {
2660             shadowparamsv[i] = vec4(
2661                 0.5f * sm.size * cotan360(l.spot),
2662                 (-smnearclip * smfarclip / (smfarclip - smnearclip) - 0.5f*bias),
2663                 1 / (1 + fabs(l.dir.z)),
2664                 0.5f + 0.5f * (smfarclip + smnearclip) / (smfarclip - smnearclip));
2665         }
2666         else
2667         {
2668             shadowparamsv[i] = vec4(
2669                 0.5f * (sm.size - border),
2670                 -smnearclip * smfarclip / (smfarclip - smnearclip) - 0.5f*bias,
2671                 sm.size,
2672                 0.5f + 0.5f * (smfarclip + smnearclip) / (smfarclip - smnearclip));
2673         }
2674         shadowoffsetv[i] = vec2(sm.x + 0.5f*sm.size, sm.y + 0.5f*sm.size);
2675     }
2676 }
2677 
setlightshader(Shader * s,int n,bool baselight,bool shadowmap,bool spotlight,bool transparent=false,bool avatar=false)2678 static inline void setlightshader(Shader *s, int n, bool baselight, bool shadowmap, bool spotlight, bool transparent = false, bool avatar = false)
2679 {
2680     s->setvariant(n-1, (shadowmap ? 1 : 0) + (baselight ? 0 : 2) + (spotlight ? 4 : 0) + (transparent ? 8 : 0) + (avatar ? 24 : 0));
2681     lightpos.setv(lightposv, n);
2682     lightcolor.setv(lightcolorv, n);
2683     if(spotlight) spotparams.setv(spotparamsv, n);
2684     if(shadowmap)
2685     {
2686         shadowparams.setv(shadowparamsv, n);
2687         shadowoffset.setv(shadowoffsetv, n);
2688     }
2689 }
2690 
setavatarstencil(int stencilref,bool on)2691 static inline void setavatarstencil(int stencilref, bool on)
2692 {
2693     glStencilFunc(GL_EQUAL, (on ? 0x40 : 0) | stencilref, !(stencilref&0x08) && !hasMSS && msaasamples==2 ? 0x47 : 0x4F);
2694 }
2695 
rendersunpass(Shader * s,int stencilref,bool transparent,float bsx1,float bsy1,float bsx2,float bsy2,const uint * tilemask)2696 static void rendersunpass(Shader *s, int stencilref, bool transparent, float bsx1, float bsy1, float bsx2, float bsy2, const uint *tilemask)
2697 {
2698     if(hasDBT && depthtestlights > 1) glDepthBounds_(0, depthtestlightsclamp);
2699 
2700     int tx1 = max(int(floor((bsx1*0.5f+0.5f)*vieww)), 0), ty1 = max(int(floor((bsy1*0.5f+0.5f)*viewh)), 0),
2701         tx2 = min(int(ceil((bsx2*0.5f+0.5f)*vieww)), vieww), ty2 = min(int(ceil((bsy2*0.5f+0.5f)*viewh)), viewh);
2702     s->setvariant(transparent ? 0 : -1, 16);
2703     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);
2704     lightpassesused++;
2705 
2706     if(stencilref >= 0)
2707     {
2708         setavatarstencil(stencilref, true);
2709 
2710         s->setvariant(0, 17);
2711         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);
2712         lightpassesused++;
2713 
2714         setavatarstencil(stencilref, false);
2715     }
2716 }
2717 
renderlightsnobatch(Shader * s,int stencilref,bool transparent,float bsx1,float bsy1,float bsx2,float bsy2)2718 static void renderlightsnobatch(Shader *s, int stencilref, bool transparent, float bsx1, float bsy1, float bsx2, float bsy2)
2719 {
2720     if(!lightspherevbuf) initlightsphere(8, 4);
2721     glBindBuffer_(GL_ARRAY_BUFFER, lightspherevbuf);
2722     glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, lightsphereebuf);
2723     gle::vertexpointer(sizeof(vec), lightsphereverts);
2724     gle::enablevertex();
2725 
2726     glEnable(GL_SCISSOR_TEST);
2727 
2728     bool outside = true;
2729     loop(avatarpass, stencilref >= 0 ? 2 : 1)
2730     {
2731         if(avatarpass) setavatarstencil(stencilref, true);
2732 
2733         loopv(lightorder)
2734         {
2735             const lightinfo &l = lights[lightorder[i]];
2736             float sx1 = max(bsx1, l.sx1), sy1 = max(bsy1, l.sy1),
2737                   sx2 = min(bsx2, l.sx2), sy2 = min(bsy2, l.sy2);
2738             if(sx1 >= sx2 || sy1 >= sy2 || l.sz1 >= l.sz2 || (avatarpass && l.dist - l.radius > avatarshadowdist)) continue;
2739 
2740             matrix4 lightmatrix = camprojmatrix;
2741             lightmatrix.translate(l.o);
2742             lightmatrix.scale(l.radius*lightradiustweak);
2743             GLOBALPARAM(lightmatrix, lightmatrix);
2744 
2745             setlightparams(0, l);
2746             setlightshader(s, 1, false, l.shadowmap >= 0, l.spot > 0, transparent, avatarpass > 0);
2747 
2748             int tx1 = int(floor((sx1*0.5f+0.5f)*vieww)), ty1 = int(floor((sy1*0.5f+0.5f)*viewh)),
2749                 tx2 = int(ceil((sx2*0.5f+0.5f)*vieww)), ty2 = int(ceil((sy2*0.5f+0.5f)*viewh));
2750             glScissor(tx1, ty1, tx2-tx1, ty2-ty1);
2751 
2752             if(hasDBT && depthtestlights > 1) glDepthBounds_(l.sz1*0.5f + 0.5f, min(l.sz2*0.5f + 0.5f, depthtestlightsclamp));
2753 
2754             if(camera1->o.dist(l.o) <= l.radius*lightradiustweak + nearplane + 1 && depthfaillights)
2755             {
2756                 if(outside)
2757                 {
2758                     outside = false;
2759                     glDepthFunc(GL_GEQUAL);
2760                     glCullFace(GL_FRONT);
2761                 }
2762             }
2763             else if(!outside)
2764             {
2765                 outside = true;
2766                 glDepthFunc(GL_LESS);
2767                 glCullFace(GL_BACK);
2768             }
2769 
2770             glDrawRangeElements_(GL_TRIANGLES, 0, lightspherenumverts-1, lightspherenumindices, GL_UNSIGNED_SHORT, lightsphereindices);
2771             xtraverts += lightspherenumindices;
2772             glde++;
2773 
2774             lightpassesused++;
2775         }
2776 
2777         if(avatarpass) setavatarstencil(stencilref, false);
2778     }
2779 
2780     if(!outside)
2781     {
2782         outside = true;
2783         glDepthFunc(GL_LESS);
2784         glCullFace(GL_BACK);
2785     }
2786 
2787     glDisable(GL_SCISSOR_TEST);
2788 
2789     gle::disablevertex();
2790     glBindBuffer_(GL_ARRAY_BUFFER, 0);
2791     glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, 0);
2792 }
2793 
renderlightbatches(Shader * s,int stencilref,bool transparent,float bsx1,float bsy1,float bsx2,float bsy2,const uint * tilemask)2794 static void renderlightbatches(Shader *s, int stencilref, bool transparent, float bsx1, float bsy1, float bsx2, float bsy2, const uint *tilemask)
2795 {
2796     bool sunpass = !sunlight.iszero() && csmshadowmap && batchsunlight <= (gi && giscale && gidist ? 1 : 0);
2797     int btx1, bty1, btx2, bty2;
2798     calctilebounds(bsx1, bsy1, bsx2, bsy2, btx1, bty1, btx2, bty2);
2799     loopv(lightbatches)
2800     {
2801         lightbatch &batch = *lightbatches[i];
2802         if(!batch.inside(btx1, bty1, btx2, bty2, tilemask)) continue;
2803 
2804         lighttile &tile = *batch.tile;
2805         int offset = batch.offset, n = batch.numlights;
2806         float sx1 = 1, sy1 = 1, sx2 = -1, sy2 = -1, sz1 = 1, sz2 = -1;
2807         loopj(n)
2808         {
2809             const lightinfo &l = lights[tile.lights[offset+j]];
2810             setlightparams(j, l);
2811             l.addscissor(sx1, sy1, sx2, sy2, sz1, sz2);
2812         }
2813 
2814         if(!offset && !sunpass) { sx1 = bsx1; sy1 = bsy1; sx2 = bsx2; sy2 = bsy2; sz1 = -1; sz2 = 1; }
2815         else
2816         {
2817             sx1 = max(sx1, bsx1); sy1 = max(sy1, bsy1); sx2 = min(sx2, bsx2); sy2 = min(sy2, bsy2);
2818             if(sx1 >= sx2 || sy1 >= sy2 || sz1 >= sz2) continue;
2819         }
2820 
2821         if(n)
2822         {
2823             const lightinfo &l = lights[tile.lights[offset]];
2824             setlightshader(s, n, !offset && !sunpass, l.shadowmap >= 0, l.spot > 0, transparent);
2825         }
2826         else s->setvariant(transparent ? 0 : -1, 16);
2827 
2828         lightpassesused++;
2829 
2830         if(hasDBT && depthtestlights > 1) glDepthBounds_(sz1*0.5f + 0.5f, min(sz2*0.5f + 0.5f, depthtestlightsclamp));
2831         gle::begin(GL_QUADS);
2832         loopvj(batch.strips)
2833         {
2834             lightstrip &s = batch.strips[j];
2835             if(s.y >= bty1 && s.y < bty2) for(int x = max(int(s.x), btx1), end = min(int(s.x + s.w), btx2); x < end;)
2836             {
2837                 int start;
2838                 if(tilemask)
2839                 {
2840                     while(x < end && !(tilemask[s.y]&(1<<x))) x++;
2841                     if(x >= end) break;
2842                     start = x;
2843                     do ++x; while(x < end && tilemask[s.y]&(1<<x));
2844                 }
2845                 else
2846                 {
2847                     start = x;
2848                     x = end;
2849                 }
2850                 int tx1 = max(int(floor((sx1*0.5f+0.5f)*vieww)), ((start*lighttilevieww)/lighttilew)*lighttilealignw),
2851                     ty1 = max(int(floor((sy1*0.5f+0.5f)*viewh)), ((s.y*lighttileviewh)/lighttileh)*lighttilealignh),
2852                     tx2 = min(int(ceil((sx2*0.5f+0.5f)*vieww)), min(((x*lighttilevieww)/lighttilew)*lighttilealignw, vieww)),
2853                     ty2 = min(int(ceil((sy2*0.5f+0.5f)*viewh)), min((((s.y+1)*lighttileviewh)/lighttileh)*lighttilealignh, viewh));
2854                 lightquads(sz1, (tx1*2.0f)/vieww-1.0f, (ty1*2.0f)/viewh-1.0f, (tx2*2.0f)/vieww-1.0f, (ty2*2.0f)/viewh-1.0f);
2855             }
2856         }
2857         gle::end();
2858     }
2859 
2860     if(stencilref >= 0)
2861     {
2862         setavatarstencil(stencilref, true);
2863 
2864         bool baselight = !sunpass;
2865         for(int offset = 0; baselight || offset < lightorder.length(); baselight = false)
2866         {
2867             int n = 0;
2868             bool shadowmap = false, spotlight = false;
2869             float sx1 = 1, sy1 = 1, sx2 = -1, sy2 = -1, sz1 = 1, sz2 = -1;
2870             for(; offset < lightorder.length(); offset++)
2871             {
2872                 const lightinfo &l = lights[lightorder[offset]];
2873                 if(l.dist - l.radius > avatarshadowdist) continue;
2874                 if(!n)
2875                 {
2876                     shadowmap = l.shadowmap >= 0;
2877                     spotlight = l.spot > 0;
2878                 }
2879                 else if(n >= lighttilebatch || (l.shadowmap >= 0) != shadowmap || (l.spot > 0) != spotlight) break;
2880                 setlightparams(n++, l);
2881                 l.addscissor(sx1, sy1, sx2, sy2, sz1, sz2);
2882             }
2883             if(baselight) { sx1 = bsx1; sy1 = bsy1; sx2 = bsx2; sy2 = bsy2; sz1 = -1; sz2 = 1; }
2884             else
2885             {
2886                 if(!n) break;
2887                 sx1 = max(sx1, bsx1); sy1 = max(sy1, bsy1); sx2 = min(sx2, bsx2); sy2 = min(sy2, bsy2);
2888                 if(sx1 >= sx2 || sy1 >= sy2 || sz1 >= sz2) continue;
2889             }
2890 
2891             if(n) setlightshader(s, n, baselight, shadowmap, spotlight, false, true);
2892             else s->setvariant(0, 17);
2893 
2894             if(hasDBT && depthtestlights > 1) glDepthBounds_(sz1*0.5f + 0.5f, min(sz2*0.5f + 0.5f, depthtestlightsclamp));
2895             lightquad(sz1, sx1, sy1, sx2, sy2, tilemask);
2896             lightpassesused++;
2897         }
2898 
2899         setavatarstencil(stencilref, false);
2900     }
2901 }
2902 
renderlights(float bsx1=-1,float bsy1=-1,float bsx2=1,float bsy2=1,const uint * tilemask=NULL,int stencilmask=0,int msaapass=0,bool transparent=false)2903 void renderlights(float bsx1 = -1, float bsy1 = -1, float bsx2 = 1, float bsy2 = 1, const uint *tilemask = NULL, int stencilmask = 0, int msaapass = 0, bool transparent = false)
2904 {
2905     Shader *s = drawtex == DRAWTEX_MINIMAP ? deferredminimapshader : (msaapass <= 0 ? deferredlightshader : (msaapass > 1 ? deferredmsaasampleshader : deferredmsaapixelshader));
2906     if(!s || s == nullshader) return;
2907 
2908     bool depth = true;
2909     if(!depthtestlights) { glDisable(GL_DEPTH_TEST); depth = false; }
2910     else glDepthMask(GL_FALSE);
2911 
2912     gle::defvertex(3);
2913 
2914     bool avatar = useavatarmask() && !transparent && !drawtex;
2915     int stencilref = -1;
2916     if(msaapass == 1 && ghasstencil)
2917     {
2918         int tx1 = max(int(floor((bsx1*0.5f+0.5f)*vieww)), 0), ty1 = max(int(floor((bsy1*0.5f+0.5f)*viewh)), 0),
2919             tx2 = min(int(ceil((bsx2*0.5f+0.5f)*vieww)), vieww), ty2 = min(int(ceil((bsy2*0.5f+0.5f)*viewh)), viewh);
2920         glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
2921         if(stencilmask) glStencilFunc(GL_EQUAL, stencilmask|0x08, 0x07);
2922         else
2923         {
2924             glStencilFunc(GL_ALWAYS, 0x08, ~0);
2925             glEnable(GL_STENCIL_TEST);
2926         }
2927         if(avatar) glStencilMask(~0x40);
2928         if(depthtestlights && depth) { glDisable(GL_DEPTH_TEST); depth = false; }
2929         glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2930         SETSHADER(msaaedgedetect);
2931         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);
2932         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2933         glStencilFunc(GL_EQUAL, stencilref = stencilmask, (avatar ? 0x40 : 0) | (!hasMSS && msaasamples==2 ? 0x07 : 0x0F));
2934         glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
2935         if(avatar) glStencilMask(~0);
2936         else if(!hasMSS && msaasamples==2 && !stencilmask) glDisable(GL_STENCIL_TEST);
2937     }
2938     else if(msaapass == 2)
2939     {
2940         if(ghasstencil) glStencilFunc(GL_EQUAL, stencilref = stencilmask|0x08, avatar ? 0x4F : 0x0F);
2941         if(!hasMSS && msaasamples==2) { glSampleMaski_(0, 2); glEnable(GL_SAMPLE_MASK); }
2942     }
2943     else if(ghasstencil && (stencilmask || avatar))
2944     {
2945         if(!stencilmask) glEnable(GL_STENCIL_TEST);
2946         glStencilFunc(GL_EQUAL, stencilref = stencilmask, avatar ? 0x4F : 0x0F);
2947         glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
2948     }
2949 
2950     if(!avatar) stencilref = -1;
2951 
2952     bindlighttexs(msaapass, transparent);
2953 
2954     setlightglobals(transparent);
2955 
2956     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
2957     glEnable(GL_BLEND);
2958 
2959     if(hasDBT && depthtestlights > 1) glEnable(GL_DEPTH_BOUNDS_TEST_EXT);
2960 
2961     bool sunpass = !lighttilebatch || (!sunlight.iszero() && csmshadowmap && batchsunlight <= (gi && giscale && gidist ? 1 : 0));
2962     if(sunpass)
2963     {
2964         if(depthtestlights && depth) { glDisable(GL_DEPTH_TEST); depth = false; }
2965         rendersunpass(s, stencilref, transparent, bsx1, bsy1, bsx2, bsy2, tilemask);
2966     }
2967 
2968     if(depthtestlights && !depth) { glEnable(GL_DEPTH_TEST); depth = true; }
2969 
2970     if(!lighttilebatch)
2971     {
2972         gle::disable();
2973         renderlightsnobatch(s, stencilref, transparent, bsx1, bsy1, bsx2, bsy2);
2974     }
2975     else
2976     {
2977         renderlightbatches(s, stencilref, transparent, bsx1, bsy1, bsx2, bsy2, tilemask);
2978         gle::disable();
2979     }
2980 
2981     if(msaapass == 1 && ghasstencil)
2982     {
2983         if(!hasMSS && msaasamples==2 && !stencilmask && !avatar) glEnable(GL_STENCIL_TEST);
2984     }
2985     else if(msaapass == 2)
2986     {
2987         if(ghasstencil && !stencilmask) glDisable(GL_STENCIL_TEST);
2988         if(!hasMSS && msaasamples==2) glDisable(GL_SAMPLE_MASK);
2989     }
2990     else if(avatar && !stencilmask) glDisable(GL_STENCIL_TEST);
2991 
2992     glDisable(GL_BLEND);
2993 
2994     if(!depthtestlights) glEnable(GL_DEPTH_TEST);
2995     else
2996     {
2997         glDepthMask(GL_TRUE);
2998         if(hasDBT && depthtestlights > 1) glDisable(GL_DEPTH_BOUNDS_TEST_EXT);
2999     }
3000 }
3001 
3002 VAR(oqlights, 0, 1, 1);
3003 VAR(debuglightscissor, 0, 0, 1);
3004 
viewlightscissor()3005 void viewlightscissor()
3006 {
3007     vector<extentity *> &ents = entities::getents();
3008     gle::defvertex(2);
3009     loopv(entgroup)
3010     {
3011         int idx = entgroup[i];
3012         if(ents.inrange(idx) && ents[idx]->type == ET_LIGHT)
3013         {
3014             extentity &e = *ents[idx];
3015             loopvj(lights) if(lights[j].o == e.o)
3016             {
3017                 lightinfo &l = lights[j];
3018                 if(l.sx1 >= l.sx2 || l.sy1 >= l.sy2 || l.sz1 >= l.sz2) break;
3019                 gle::colorf(l.color.x/255, l.color.y/255, l.color.z/255);
3020                 float x1 = (l.sx1+1)/2*hudw, x2 = (l.sx2+1)/2*hudw,
3021                       y1 = (1-l.sy1)/2*hudh, y2 = (1-l.sy2)/2*hudh;
3022                 gle::begin(GL_TRIANGLE_STRIP);
3023                 gle::attribf(x1, y1);
3024                 gle::attribf(x2, y1);
3025                 gle::attribf(x1, y2);
3026                 gle::attribf(x2, y2);
3027                 gle::end();
3028             }
3029         }
3030     }
3031     gle::disable();
3032 }
3033 
calclightscissor(lightinfo & l)3034 static inline bool calclightscissor(lightinfo &l)
3035 {
3036     float sx1 = -1, sy1 = -1, sx2 = 1, sy2 = 1, sz1 = -1, sz2 = 1;
3037     if(l.spot > 0) calcspotscissor(l.o, l.radius, l.dir, l.spot, l.spotx, l.spoty, sx1, sy1, sx2, sy2, sz1, sz2);
3038     else calcspherescissor(l.o, l.radius, sx1, sy1, sx2, sy2, sz1, sz2);
3039     l.sx1 = sx1;
3040     l.sx2 = sx2;
3041     l.sy1 = sy1;
3042     l.sy2 = sy2;
3043     l.sz1 = sz1;
3044     l.sz2 = sz2;
3045     return sx1 < sx2 && sy1 < sy2 && sz1 < sz2;
3046 }
3047 
collectlights()3048 void collectlights()
3049 {
3050     // point lights processed here
3051     const vector<extentity *> &ents = entities::getents();
3052     if(!editmode || !fullbright) loopv(ents)
3053     {
3054         extentity *e = ents[i];
3055         if(e->type != ET_LIGHT || e->attr1 <= 0) continue;
3056 
3057         if(smviscull)
3058         {
3059             if(isfoggedsphere(e->attr1, e->o)) continue;
3060             if(pvsoccludedsphere(e->o, e->attr1)) continue;
3061         }
3062 
3063         lightinfo &l = lights.add();
3064         l.ent = i;
3065         l.shadowmap = -1;
3066         l.flags = e->attr5;
3067         l.query = NULL;
3068         l.o = e->o;
3069         l.color = vec(e->attr2, e->attr3, e->attr4);
3070         l.radius = e->attr1;
3071         if(e->attached && e->attached->type == ET_SPOTLIGHT)
3072         {
3073             l.calcspot(vec(e->attached->o).sub(e->o).normalize(), clamp(int(e->attached->attr1), 1, 89));
3074         }
3075         else
3076         {
3077             l.dir = vec(0, 0, 0);
3078             l.spot = 0;
3079         }
3080         l.dist = camera1->o.dist(e->o);
3081 
3082         if(calclightscissor(l)) lightorder.add(lights.length()-1);
3083     }
3084 
3085     int numdynlights = 0;
3086     if(!drawtex)
3087     {
3088         updatedynlights();
3089         numdynlights = finddynlights();
3090     }
3091     loopi(numdynlights)
3092     {
3093         vec o, color, dir;
3094         float radius;
3095         int spot;
3096         if(!getdynlight(i, o, radius, color, dir, spot)) continue;
3097 
3098         lightinfo &l = lights.add();
3099         l.ent = -1;
3100         l.shadowmap = -1;
3101         l.flags = 0;
3102         l.query = NULL;
3103         l.o = o;
3104         l.color = vec(color).mul(255);
3105         l.radius = radius;
3106         if(spot > 0)
3107         {
3108             l.calcspot(dir, spot);
3109         }
3110         else
3111         {
3112             l.dir = vec(0, 0, 0);
3113             l.spot = 0;
3114         }
3115         l.dist = camera1->o.dist(o);
3116 
3117         if(calclightscissor(l)) lightorder.add(lights.length()-1);
3118     }
3119 
3120     lightorder.sort(sortlights);
3121 
3122     bool queried = false;
3123     if(!drawtex && smquery && oqfrags && oqlights) loopv(lightorder)
3124     {
3125         int idx = lightorder[i];
3126         lightinfo &l = lights[idx];
3127         if(l.noshadow() || l.radius >= worldsize) continue;
3128         vec bbmin, bbmax;
3129         if(l.spot > 0)
3130         {
3131             float spotscale = l.radius * tan360(l.spot);
3132             vec up = vec(l.spotx).mul(spotscale).abs(), right = vec(l.spoty).mul(spotscale).abs(), center = vec(l.dir).mul(l.radius).add(l.o);
3133             bbmin = bbmax = center;
3134             bbmin.sub(up).sub(right);
3135             bbmax.add(up).add(right);
3136             bbmin.min(l.o);
3137             bbmax.max(l.o);
3138         }
3139         else
3140         {
3141             bbmin = vec(l.o).sub(l.radius);
3142             bbmax = vec(l.o).add(l.radius);
3143         }
3144         if(camera1->o.x < bbmin.x - 2 || camera1->o.x > bbmax.x + 2 ||
3145            camera1->o.y < bbmin.y - 2 || camera1->o.y > bbmax.y + 2 ||
3146            camera1->o.z < bbmin.z - 2 || camera1->o.z > bbmax.z + 2)
3147         {
3148             l.query = newquery(&l);
3149             if(l.query)
3150             {
3151                 if(!queried)
3152                 {
3153                     gle::defvertex();
3154                     queried = true;
3155                 }
3156                 startquery(l.query);
3157                 ivec bo = bbmin, br = bbmax;
3158                 br.sub(bo).add(1);
3159                 drawbb(bo, br);
3160                 endquery(l.query);
3161             }
3162         }
3163     }
3164     if(queried)
3165     {
3166         gle::disable();
3167         glFlush();
3168     }
3169 
3170     if(rhinoq && oqfrags && !drawtex && (!wireframe || !editmode)) renderradiancehints();
3171 }
3172 
addlighttiles(const lightinfo & l,int idx)3173 static inline void addlighttiles(const lightinfo &l, int idx)
3174 {
3175     int tx1, ty1, tx2, ty2;
3176     calctilebounds(l.sx1, l.sy1, l.sx2, l.sy2, tx1, ty1, tx2, ty2);
3177     for(int y = ty1; y < ty2; y++) for(int x = tx1; x < tx2; x++) { lighttiles[y][x].lights.add(idx); lighttilesused++; }
3178 }
3179 
3180 VAR(lightsvisible, 1, 0, 0);
3181 VAR(lightsoccluded, 1, 0, 0);
3182 VARN(lightbatches, lightbatchesused, 1, 0, 0);
3183 
sortlightbatches(const lightbatch * x,const lightbatch * y)3184 static inline bool sortlightbatches(const lightbatch *x, const lightbatch *y)
3185 {
3186     if(x->tile->band < y->tile->band) return true;
3187     if(x->tile->band > y->tile->band) return false;
3188     if(x->priority < y->priority) return true;
3189     if(x->priority > y->priority) return false;
3190     return x->numlights > y->numlights;
3191 }
3192 
packlights()3193 void packlights()
3194 {
3195     lightsvisible = lightsoccluded = 0;
3196     lighttilesused = lightpassesused = 0;
3197     smused = 0;
3198 
3199     if(smcache && !smnoshadow && shadowcache.numelems) loopv(lightorder)
3200     {
3201         int idx = lightorder[i];
3202         lightinfo &l = lights[idx];
3203         if(l.noshadow()) continue;
3204         if(l.query && l.query->owner == &l && checkquery(l.query)) continue;
3205 
3206         float prec = smprec, lod;
3207         int w, h;
3208         if(l.spot) { w = 1; h = 1; prec *= tan360(l.spot); lod = smspotprec; }
3209         else { w = 3; h = 2; lod = smcubeprec; }
3210         lod *= clamp(l.radius * prec / sqrtf(max(1.0f, l.dist/l.radius)), float(smminsize), float(smmaxsize));
3211         int size = clamp(int(ceil((lod * shadowatlaspacker.w) / SHADOWATLAS_SIZE)), 1, shadowatlaspacker.w / w);
3212         w *= size;
3213         h *= size;
3214         ushort x = USHRT_MAX, y = USHRT_MAX;
3215         shadowmapinfo *sm = NULL;
3216         int smidx = -1;
3217         shadowcacheval *cached = shadowcache.access(l);
3218         if(!cached || cached->size != size) continue;
3219         x = cached->x;
3220         y = cached->y;
3221         shadowatlaspacker.reserve(x, y, w, h);
3222         sm = addshadowmap(x, y, size, smidx);
3223         sm->light = idx;
3224         sm->cached = cached;
3225         l.shadowmap = smidx;
3226         smused += w*h;
3227 
3228         addlighttiles(l, idx);
3229     }
3230     loopv(lightorder)
3231     {
3232         int idx = lightorder[i];
3233         lightinfo &l = lights[idx];
3234         if(l.shadowmap >= 0) continue;
3235 
3236         if(!l.noshadow() && !smnoshadow)
3237         {
3238             if(l.query && l.query->owner == &l && checkquery(l.query)) { lightsoccluded++; continue; }
3239             float prec = smprec, lod;
3240             int w, h;
3241             if(l.spot) { w = 1; h = 1; prec *= tan360(l.spot); lod = smspotprec; }
3242             else { w = 3; h = 2; lod = smcubeprec; }
3243             lod *= clamp(l.radius * prec / sqrtf(max(1.0f, l.dist/l.radius)), float(smminsize), float(smmaxsize));
3244             int size = clamp(int(ceil((lod * shadowatlaspacker.w) / SHADOWATLAS_SIZE)), 1, shadowatlaspacker.w / w);
3245             w *= size;
3246             h *= size;
3247             ushort x = USHRT_MAX, y = USHRT_MAX;
3248             shadowmapinfo *sm = NULL;
3249             int smidx = -1;
3250             if(shadowatlaspacker.insert(x, y, w, h))
3251             {
3252                 sm = addshadowmap(x, y, size, smidx);
3253                 sm->light = idx;
3254                 l.shadowmap = smidx;
3255                 smused += w*h;
3256             }
3257             else if(smcache) shadowcachefull = true;
3258         }
3259 
3260         addlighttiles(l, idx);
3261     }
3262 
3263     lightsvisible = lightorder.length() - lightsoccluded;
3264 
3265     lightbatcher.recycle();
3266     lightbatches.setsize(0);
3267     if(lighttilebatch) loop(y, lighttileh)
3268     {
3269         int band = lighttilebands && lighttilebands < lighttileh ? (y * lighttilebands) / lighttileh : y;
3270         bool sunpass = !sunlight.iszero() && csmshadowmap && batchsunlight < (gi && giscale && gidist ? 1 : 0);
3271         loop(x, lighttilew)
3272         {
3273             lighttile &tile = lighttiles[y][x];
3274             tile.band = band;
3275             for(int offset = 0;;)
3276             {
3277                 int n = min(tile.lights.length() - offset, lighttilebatch);
3278                 bool shadowmap = false, spotlight = false;
3279                 if(n)
3280                 {
3281                     lightinfo &l = lights[tile.lights[offset]];
3282                     shadowmap = l.shadowmap >= 0;
3283                     spotlight = l.spot > 0;
3284                 }
3285                 loopj(n)
3286                 {
3287                     lightinfo &l = lights[tile.lights[offset+j]];
3288                     if((l.shadowmap >= 0) != shadowmap || (l.spot > 0) != spotlight) { n = j; break; }
3289                 }
3290                 int priority = (offset || sunpass ? 4 : 0) + (shadowmap ? 0 : 2) + (spotlight ? 1 : 0);
3291                 lighttileslice slice(&tile, priority, offset, n);
3292                 lightbatch &batch = lightbatcher[slice];
3293                 if(batch.strips.empty() || !lighttilestrip || !batch.strips.last().extend(x, y))
3294                 {
3295                     if(batch.strips.empty())
3296                     {
3297                         (lighttileslice &)batch = slice;
3298                         lightbatches.add(&batch);
3299                     }
3300                     lightstrip &strip = batch.strips.add();
3301                     strip.x = x;
3302                     strip.y = y;
3303                     strip.w = 1;
3304                 }
3305                 offset += n;
3306                 if(offset >= tile.lights.length()) break;
3307             }
3308         }
3309     }
3310     lightbatches.sort(sortlightbatches);
3311     lightbatchesused = lightbatches.length();
3312 }
3313 
nogiquad(int x,int y,int w,int h)3314 static inline void nogiquad(int x, int y, int w, int h)
3315 {
3316     gle::attribf(x, y+h);
3317     gle::attribf(x+w, y+h);
3318     gle::attribf(x+w, y);
3319     gle::attribf(x, y);
3320 }
3321 
rendernogi(cube * c,const ivec & o,int size,const ivec & bbmin,const ivec & bbmax,int minsize)3322 static inline bool rendernogi(cube *c, const ivec &o, int size, const ivec &bbmin, const ivec &bbmax, int minsize)
3323 {
3324     ivec mid = ivec(o).add(size);
3325     uchar overlap = 0;
3326     if(bbmin.y < mid.y)
3327     {
3328         if(bbmin.x < mid.x)
3329         {
3330             if((bbmin.z < mid.z && (c[0].children ? rendernogi(c[0].children, ivec(o.x, o.y, o.z), size>>1, bbmin, bbmax, minsize) : c[0].material&MAT_NOGI)) ||
3331                (bbmax.z > mid.z && (c[4].children ? rendernogi(c[4].children, ivec(o.x, o.y, mid.z), size>>1, bbmin, bbmax, minsize) : c[4].material&MAT_NOGI)))
3332                 overlap |= 1;
3333         }
3334         if(bbmax.x > mid.x)
3335         {
3336             if((bbmin.z < mid.z && (c[1].children ? rendernogi(c[1].children, ivec(mid.x, o.y, o.z), size>>1, bbmin, bbmax, minsize) : c[1].material&MAT_NOGI)) ||
3337                (bbmax.z > mid.z && (c[5].children ? rendernogi(c[5].children, ivec(mid.x, o.y, mid.z), size>>1, bbmin, bbmax, minsize) : c[5].material&MAT_NOGI)))
3338                 overlap |= 2;
3339         }
3340     }
3341     if(bbmax.y > mid.y)
3342     {
3343         if(bbmin.x < mid.x)
3344         {
3345             if((bbmin.z < mid.z && (c[2].children ? rendernogi(c[2].children, ivec(o.x, mid.y, o.z), size>>1, bbmin, bbmax, minsize) : c[2].material&MAT_NOGI)) ||
3346                (bbmax.z > mid.z && (c[6].children ? rendernogi(c[6].children, ivec(o.x, mid.y, mid.z), size>>1, bbmin, bbmax, minsize) : c[6].material&MAT_NOGI)))
3347                 overlap |= 4;
3348         }
3349         if(bbmax.x > mid.x)
3350         {
3351             if((bbmin.z < mid.z && (c[3].children ? rendernogi(c[3].children, ivec(mid.x, mid.y, o.z), size>>1, bbmin, bbmax, minsize) : c[3].material&MAT_NOGI)) ||
3352                (bbmax.z > mid.z && (c[7].children ? rendernogi(c[7].children, ivec(mid.x, mid.y, mid.z), size>>1, bbmin, bbmax, minsize) : c[7].material&MAT_NOGI)))
3353                 overlap |= 8;
3354         }
3355     }
3356     if(!overlap) return false;
3357     if(overlap == 0xF || size <= minsize) return true;
3358     if(overlap&1)
3359     {
3360         if(overlap&2) nogiquad(o.x, o.y, 2*size, size);
3361         else nogiquad(o.x, o.y, size, size);
3362     }
3363     else if(overlap&2) nogiquad(o.x+size, o.y, size, size);
3364     if(overlap&4)
3365     {
3366         if(overlap&8) nogiquad(o.x, o.y+size, 2*size, size);
3367         else nogiquad(o.x, o.y+size, size, size);
3368     }
3369     else if(overlap&8) nogiquad(o.x+size, o.y+size, size, size);
3370     return false;
3371 }
3372 
rendernogi(const ivec & bbmin,const ivec & bbmax,int minsize)3373 static inline void rendernogi(const ivec &bbmin, const ivec &bbmax, int minsize)
3374 {
3375     if(rendernogi(worldroot, ivec(0, 0, 0), worldsize>>1, ivec(bbmin).max(nogimin), ivec(bbmax).min(nogimax), minsize))
3376         nogiquad(0, 0, worldsize, worldsize);
3377 }
3378 
rhquad(float x1,float y1,float x2,float y2,float tx1,float ty1,float tx2,float ty2,float tz)3379 static inline void rhquad(float x1, float y1, float x2, float y2, float tx1, float ty1, float tx2, float ty2, float tz)
3380 {
3381     gle::begin(GL_TRIANGLE_STRIP);
3382     gle::attribf(x2, y1); gle::attribf(tx2, ty1, tz);
3383     gle::attribf(x1, y1); gle::attribf(tx1, ty1, tz);
3384     gle::attribf(x2, y2); gle::attribf(tx2, ty2, tz);
3385     gle::attribf(x1, y2); gle::attribf(tx1, ty2, tz);
3386     gle::end();
3387 }
3388 
rhquad(float dx1,float dy1,float dx2,float dy2,float dtx1,float dty1,float dtx2,float dty2,float dtz,float px1,float py1,float px2,float py2,float ptx1,float pty1,float ptx2,float pty2,float ptz)3389 static inline void rhquad(float dx1, float dy1, float dx2, float dy2, float dtx1, float dty1, float dtx2, float dty2, float dtz,
3390                           float px1, float py1, float px2, float py2, float ptx1, float pty1, float ptx2, float pty2, float ptz)
3391 {
3392     gle::begin(GL_TRIANGLE_STRIP);
3393     gle::attribf(dx2, dy1); gle::attribf(dtx2, dty1, dtz);
3394         gle::attribf(px2, py1); gle::attribf(ptx2, pty1, ptz);
3395     gle::attribf(dx1, dy1); gle::attribf(dtx1, dty1, dtz);
3396         gle::attribf(px1, py1); gle::attribf(ptx1, pty1, ptz);
3397     gle::attribf(dx1, dy2); gle::attribf(dtx1, dty2, dtz);
3398         gle::attribf(px1, py2); gle::attribf(ptx1, pty2, ptz);
3399     gle::attribf(dx2, dy2); gle::attribf(dtx2, dty2, dtz);
3400         gle::attribf(px2, py2); gle::attribf(ptx2, pty2, ptz);
3401     gle::attribf(dx2, dy1); gle::attribf(dtx2, dty1, dtz);
3402         gle::attribf(px2, py1); gle::attribf(ptx2, pty1, ptz);
3403     gle::end();
3404 }
3405 
renderslices()3406 void radiancehints::renderslices()
3407 {
3408     int sw = rhgrid+2*rhborder, sh = rhgrid+2*rhborder;
3409     glBindFramebuffer_(GL_FRAMEBUFFER, rhfbo);
3410     if(!rhrect)
3411     {
3412         glViewport(0, 0, sw, sh);
3413         if(rhcache) loopi(4) swap(rhtex[i], rhtex[i+4]);
3414     }
3415 
3416     GLOBALPARAMF(rhatten, 1.0f/(gidist*gidist));
3417     GLOBALPARAMF(rsmspread, gidist*rsmspread*rsm.scale.x, gidist*rsmspread*rsm.scale.y);
3418     GLOBALPARAMF(rhaothreshold, splits[0].bounds/rhgrid);
3419     GLOBALPARAMF(rhaoatten, 1.0f/(gidist*rsmspread));
3420     GLOBALPARAMF(rhaoheight, gidist*rsmspread);
3421 
3422     matrix4 rsmtcmatrix;
3423     rsmtcmatrix.identity();
3424     rsmtcmatrix.settranslation(rsm.offset);
3425     rsmtcmatrix.setscale(rsm.scale);
3426     rsmtcmatrix.mul(rsm.model);
3427     GLOBALPARAM(rsmtcmatrix, rsmtcmatrix);
3428 
3429     matrix4 rsmworldmatrix;
3430     rsmworldmatrix.invert(rsmtcmatrix);
3431     GLOBALPARAM(rsmworldmatrix, rsmworldmatrix);
3432 
3433     glBindTexture(GL_TEXTURE_RECTANGLE, rsmdepthtex);
3434     glActiveTexture_(GL_TEXTURE1);
3435     glBindTexture(GL_TEXTURE_RECTANGLE, rsmcolortex);
3436     glActiveTexture_(GL_TEXTURE2);
3437     glBindTexture(GL_TEXTURE_RECTANGLE, rsmnormaltex);
3438     if(rhborder) loopi(4)
3439     {
3440         glActiveTexture_(GL_TEXTURE3 + i);
3441         glBindTexture(GL_TEXTURE_3D, rhtex[i]);
3442     }
3443     if(rhcache) loopi(4)
3444     {
3445         glActiveTexture_(GL_TEXTURE7 + i);
3446         glBindTexture(GL_TEXTURE_3D, rhtex[rhrect ? i : 4+i]);
3447     }
3448     glActiveTexture_(GL_TEXTURE0);
3449 
3450     glClearColor(0.5f, 0.5f, 0.5f, 0);
3451     if(rhrect) glEnable(GL_SCISSOR_TEST);
3452 
3453     gle::defvertex(2);
3454     gle::deftexcoord0(3);
3455 
3456     memset(rhclearmasks[0], 0xFF, sizeof(rhclearmasks[0]));
3457 
3458     loopirev(rhsplits)
3459     {
3460         splitinfo &split = splits[i];
3461         if((rhrect || !rhcache || hasCI) && split.cached == split.center && !rhforce)
3462         {
3463             bool bordercached = true;
3464             if(rhborder) for(int k = i+1; k < rhsplits; k++) if(splits[k].cached != splits[k].center) { bordercached = false; break; }
3465             if(bordercached)
3466             {
3467                 if(rhrect || !rhcache) continue;
3468                 loopk(4) glCopyImageSubData_(rhtex[4+k], GL_TEXTURE_3D, 0, 0, 0, i*sh, rhtex[k], GL_TEXTURE_3D, 0, 0, 0, i*sh, sw, sh, sh);
3469                 continue;
3470             }
3471         }
3472 
3473         float cellradius = split.bounds/rhgrid, step = 2*cellradius, nudge = rhnudge*2*splits[0].bounds/rhgrid + rhworldbias*step;
3474         GLOBALPARAM(rhcenter, split.center);
3475         GLOBALPARAMF(rhbounds, split.bounds);
3476         GLOBALPARAMF(rhspread, cellradius);
3477 
3478         vec cmin, cmax, bmin(1e16f, 1e16f, 1e16f), bmax(-1e16f, -1e16f, -1e16f), dmin(1e16f, 1e16f, 1e16f), dmax(-1e16f, -1e16f, -1e16f);
3479         loopk(3)
3480         {
3481             cmin[k] = floor((worldmin[k] - nudge - (split.center[k] - split.bounds))/step)*step + split.center[k] - split.bounds;
3482             cmax[k] = ceil((worldmax[k] + nudge - (split.center[k] - split.bounds))/step)*step + split.center[k] - split.bounds;
3483         }
3484         if(prevdynmin.z < prevdynmax.z) loopk(3)
3485         {
3486             dmin[k] = min(dmin[k], (float)floor((prevdynmin[k] - gidist - cellradius - (split.center[k] - split.bounds))/step)*step + split.center[k] - split.bounds);
3487             dmax[k] = max(dmax[k], (float)ceil((prevdynmax[k] + gidist + cellradius - (split.center[k] - split.bounds))/step)*step + split.center[k] - split.bounds);
3488         }
3489         if(dynmin.z < dynmax.z) loopk(3)
3490         {
3491             dmin[k] = min(dmin[k], (float)floor((dynmin[k] - gidist - cellradius - (split.center[k] - split.bounds))/step)*step + split.center[k] - split.bounds);
3492             dmax[k] = max(dmax[k], (float)ceil((dynmax[k] + gidist + cellradius - (split.center[k] - split.bounds))/step)*step + split.center[k] - split.bounds);
3493         }
3494 
3495         if(rhborder && i + 1 < rhsplits)
3496         {
3497             GLOBALPARAMF(bordercenter, 0.5f, 0.5f, float(i+1 + 0.5f)/rhsplits);
3498             GLOBALPARAMF(borderrange, 0.5f - 0.5f/(rhgrid+2), 0.5f - 0.5f/(rhgrid+2), (0.5f - 0.5f/(rhgrid+2))/rhsplits);
3499             GLOBALPARAMF(borderscale, rhgrid+2, rhgrid+2, (rhgrid+2)*rhsplits);
3500             splitinfo &next = splits[i+1];
3501             loopk(3)
3502             {
3503                 bmin[k] = floor((max(float(worldmin[k] - nudge), next.center[k] - next.bounds) - (split.center[k] - split.bounds))/step)*step + split.center[k] - split.bounds;
3504                 bmax[k] = ceil((min(float(worldmax[k] + nudge), next.center[k] + next.bounds) - (split.center[k] - split.bounds))/step)*step + split.center[k] - split.bounds;
3505             }
3506         }
3507 
3508         int sy = rhrect ? i*sh : 0;
3509         loopjrev(sh)
3510         {
3511             int sx = rhrect ? j*sw : 0;
3512             if(rhrect)
3513             {
3514                 glViewport(sx, sy, sw, sh);
3515                 glScissor(sx, sy, sw, sh);
3516             }
3517             else
3518             {
3519                 glFramebufferTexture3D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_3D, rhtex[0], 0, i*sh + j);
3520                 glFramebufferTexture3D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_3D, rhtex[1], 0, i*sh + j);
3521                 glFramebufferTexture3D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_3D, rhtex[2], 0, i*sh + j);
3522                 glFramebufferTexture3D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_3D, rhtex[3], 0, i*sh + j);
3523             }
3524 
3525             float x1 = split.center.x - split.bounds, x2 = split.center.x + split.bounds,
3526                   y1 = split.center.y - split.bounds, y2 = split.center.y + split.bounds,
3527                   z = split.center.z - split.bounds + (j-rhborder+0.5f)*step,
3528                   vx1 = -1 + rhborder*2.0f/(rhgrid+2), vx2 = 1 - rhborder*2.0f/(rhgrid+2), vy1 = -1 + rhborder*2.0f/(rhgrid+2), vy2 = 1 - rhborder*2.0f/(rhgrid+2),
3529                   tx1 = x1, tx2 = x2, ty1 = y1, ty2 = y2;
3530 
3531             if(rhborder && i + 1 < rhsplits)
3532             {
3533                 splitinfo &next = splits[i+1];
3534                 float bx1 = x1-step, bx2 = x2+step, by1 = y1-step, by2 = y2+step, bz = z,
3535                       bvx1 = -1, bvx2 = 1, bvy1 = -1, bvy2 = 1,
3536                       btx1 = bx1, btx2 = bx2, bty1 = by1, bty2 = by2;
3537 
3538                 if(rhclipgrid)
3539                 {
3540                     if(bz < bmin.z || bz > bmax.z) goto noborder;
3541                     if(bx1 < bmin.x || bx2 > bmax.x || by1 < bmin.y || by2 > bmax.y)
3542                     {
3543                         btx1 = max(bx1, bmin.x);
3544                         btx2 = min(bx2, bmax.x);
3545                         bty1 = max(by1, bmin.y);
3546                         bty2 = min(by2, bmax.y);
3547                         if(btx1 > tx2 || bty1 > bty2) goto noborder;
3548                         bvx1 += 2*(btx1 - bx1)/(bx2 - bx1);
3549                         bvx2 += 2*(btx2 - bx2)/(bx2 - bx1);
3550                         bvy1 += 2*(bty1 - by1)/(by2 - by1);
3551                         bvy2 += 2*(bty2 - by2)/(by2 - by1);
3552 
3553                         glClear(GL_COLOR_BUFFER_BIT);
3554                     }
3555                 }
3556 
3557                 btx1 = btx1*next.scale.x + next.offset.x;
3558                 btx2 = btx2*next.scale.x + next.offset.x;
3559                 bty1 = bty1*next.scale.y + next.offset.y;
3560                 bty2 = bty2*next.scale.y + next.offset.y;
3561                 bz = bz*next.scale.z + next.offset.z;
3562 
3563                 SETSHADER(radiancehintsborder);
3564                 rhquad(bvx1, bvy1, bvx2, bvy2, btx1, bty1, btx2, bty2, bz);
3565 
3566                 rhclearmasks[0][i][j/32] &= ~(1 << (j%32));
3567             }
3568             else
3569             {
3570             noborder:
3571                 if(rhborder) glClear(GL_COLOR_BUFFER_BIT);
3572             }
3573 
3574             if(j < rhborder || j >= rhgrid + rhborder) continue;
3575 
3576             if(rhclipgrid)
3577             {
3578                 if(z < cmin.z || z > cmax.z)
3579                 {
3580                     if(!rhborder) glClear(GL_COLOR_BUFFER_BIT);
3581                     continue;
3582                 }
3583                 if(x1 < cmin.x || x2 > cmax.x || y1 < cmin.y || y2 > cmax.y)
3584                 {
3585                     if(!rhborder) glClear(GL_COLOR_BUFFER_BIT);
3586                     tx1 = max(x1, cmin.x);
3587                     tx2 = min(x2, cmax.x);
3588                     ty1 = max(y1, cmin.y);
3589                     ty2 = min(y2, cmax.y);
3590                     if(tx1 > tx2 || ty1 > ty2) continue;
3591                     vx1 += 2*rhgrid/float(sw)*(tx1 - x1)/(x2 - x1);
3592                     vx2 += 2*rhgrid/float(sw)*(tx2 - x2)/(x2 - x1);
3593                     vy1 += 2*rhgrid/float(sh)*(ty1 - y1)/(y2 - y1);
3594                     vy2 += 2*rhgrid/float(sh)*(ty2 - y2)/(y2 - y1);
3595                 }
3596             }
3597 
3598             rhclearmasks[0][i][j/32] &= ~(1 << (j%32));
3599 
3600             if(rhcache && z > split.cached.z - split.bounds && z < split.cached.z + split.bounds)
3601             {
3602                 float px1 = max(tx1, split.cached.x - split.bounds), px2 = min(tx2, split.cached.x + split.bounds),
3603                       py1 = max(ty1, split.cached.y - split.bounds), py2 = min(ty2, split.cached.y + split.bounds);
3604                 if(px1 < px2 && py1 < py2)
3605                 {
3606                     float pvx1 = -1 + rhborder*2.0f/(rhgrid+2) + 2*rhgrid/float(sw)*(px1 - x1)/(x2 - x1),
3607                           pvx2 = 1 - rhborder*2.0f/(rhgrid+2) + 2*rhgrid/float(sw)*(px2 - x2)/(x2 - x1),
3608                           pvy1 = -1 + rhborder*2.0f/(rhgrid+2) + 2*rhgrid/float(sh)*(py1 - y1)/(y2 - y1),
3609                           pvy2 = 1 - rhborder*2.0f/(rhgrid+2) + 2*rhgrid/float(sh)*(py2 - y2)/(y2 - y1),
3610                           ptx1 = (px1 + split.center.x - split.cached.x)*split.scale.x + split.offset.x,
3611                           ptx2 = (px2 + split.center.x - split.cached.x)*split.scale.x + split.offset.x,
3612                           pty1 = (py1 + split.center.y - split.cached.y)*split.scale.y + split.offset.y,
3613                           pty2 = (py2 + split.center.y - split.cached.y)*split.scale.y + split.offset.y,
3614                           pz = (z + split.center.z - split.cached.z)*split.scale.z + split.offset.z;
3615 
3616                     if(px1 != tx1 || px2 != tx2 || py1 != ty1 || py2 != ty2)
3617                     {
3618                         radiancehintsshader->set();
3619                         rhquad(pvx1, pvy1, pvx2, pvy2, px1, py1, px2, py2, z,
3620                                 vx1,  vy1,  vx2,  vy2, tx1, ty1, tx2, ty2, z);
3621                     }
3622 
3623                     if(z > dmin.z && z < dmax.z)
3624                     {
3625                         float dx1 = max(px1, dmin.x), dx2 = min(px2, dmax.x),
3626                               dy1 = max(py1, dmin.y), dy2 = min(py2, dmax.y);
3627                         if(dx1 < dx2 && dy1 < dy2)
3628                         {
3629                             float dvx1 = -1 + rhborder*2.0f/(rhgrid+2) + 2*rhgrid/float(sw)*(dx1 - x1)/(x2 - x1),
3630                                   dvx2 = 1 - rhborder*2.0f/(rhgrid+2) + 2*rhgrid/float(sw)*(dx2 - x2)/(x2 - x1),
3631                                   dvy1 = -1 + rhborder*2.0f/(rhgrid+2) + 2*rhgrid/float(sh)*(dy1 - y1)/(y2 - y1),
3632                                   dvy2 = 1 - rhborder*2.0f/(rhgrid+2) + 2*rhgrid/float(sh)*(dy2 - y2)/(y2 - y1),
3633                                   dtx1 = (dx1 + split.center.x - split.cached.x)*split.scale.x + split.offset.x,
3634                                   dtx2 = (dx2 + split.center.x - split.cached.x)*split.scale.x + split.offset.x,
3635                                   dty1 = (dy1 + split.center.y - split.cached.y)*split.scale.y + split.offset.y,
3636                                   dty2 = (dy2 + split.center.y - split.cached.y)*split.scale.y + split.offset.y,
3637                                   dz = (z + split.center.z - split.cached.z)*split.scale.z + split.offset.z;
3638 
3639                             if(dx1 != px1 || dx2 != px2 || dy1 != py1 || dy2 != py2)
3640                             {
3641                                 SETSHADER(radiancehintscached);
3642                                 rhquad(dvx1, dvy1, dvx2, dvy2, dtx1, dty1, dtx2, dty2, dz,
3643                                        pvx1, pvy1, pvx2, pvy2, ptx1, pty1, ptx2, pty2, pz);
3644                             }
3645 
3646                             radiancehintsshader->set();
3647                             rhquad(dvx1, dvy1, dvx2, dvy2, dx1, dy1, dx2, dy2, z);
3648                             goto maskslice;
3649                         }
3650                     }
3651 
3652                     SETSHADER(radiancehintscached);
3653                     rhquad(pvx1, pvy1, pvx2, pvy2, ptx1, pty1, ptx2, pty2, pz);
3654                     goto maskslice;
3655                 }
3656             }
3657 
3658             radiancehintsshader->set();
3659             rhquad(vx1, vy1, vx2, vy2, tx1, ty1, tx2, ty2, z);
3660 
3661         maskslice:
3662             if(i) continue;
3663             rendernogi(ivec::floor(vec(x1, y1, z - 0.5f*step)), ivec::ceil(vec(x2, y2, z + 0.5f*step)), int(step));
3664             if(gle::attribbuf.empty()) continue;
3665             SETSHADER(radiancehintsdisable);
3666             if(rhborder)
3667             {
3668                 glScissor(sx + rhborder, sy + rhborder, sw - 2*rhborder, sh - 2*rhborder);
3669                 if(!rhrect) glEnable(GL_SCISSOR_TEST);
3670             }
3671             gle::defvertex(2);
3672             gle::begin(GL_QUADS);
3673             gle::end();
3674             if(rhborder && !rhrect) glDisable(GL_SCISSOR_TEST);
3675             gle::defvertex(2);
3676             gle::deftexcoord0(3);
3677         }
3678         if(rhrect) loopk(4)
3679         {
3680             glReadBuffer(GL_COLOR_ATTACHMENT0+k);
3681             glBindTexture(GL_TEXTURE_3D, rhtex[k]);
3682             loopj(sh)
3683             {
3684                 if(rhclearmasks[0][i][j/32] & rhclearmasks[1][i][j/32] & (1 << (j%32))) continue;
3685                 glCopyTexSubImage3D_(GL_TEXTURE_3D, 0, 0, 0, sy+j, j*sw, sy, sw, sh);
3686             }
3687         }
3688         memcpy(rhclearmasks[1][i], rhclearmasks[0][i], sizeof(rhclearmasks[0][i]));
3689     }
3690 
3691     gle::disable();
3692 
3693     if(rhrect) glDisable(GL_SCISSOR_TEST);
3694 }
3695 
3696 VAR(rhinoq, 0, 1, 1);
3697 
renderradiancehints()3698 void renderradiancehints()
3699 {
3700     if(!useradiancehints()) return;
3701 
3702     timer *rhcputimer = begintimer("radiance hints", false);
3703     timer *rhtimer = begintimer("radiance hints");
3704 
3705     rh.setup();
3706     rsm.setup();
3707 
3708     shadowmapping = SM_REFLECT;
3709     shadowside = 0;
3710     shadoworigin = vec(0, 0, 0);
3711     shadowdir = rsm.lightview;
3712     shadowbias = rsm.lightview.project_bb(worldmin, worldmax);
3713     shadowradius = fabs(rsm.lightview.project_bb(worldmax, worldmin));
3714 
3715     findshadowvas();
3716     findshadowmms();
3717 
3718     shadowmaskbatchedmodels(false);
3719     batchshadowmapmodels();
3720 
3721     rh.prevdynmin = rh.dynmin;
3722     rh.prevdynmax = rh.dynmax;
3723     rh.dynmin = vec(1e16f, 1e16f, 1e16f);
3724     rh.dynmax = vec(-1e16f, -1e16f, -1e16f);
3725     if(rhdyntex) dynamicshadowvabounds(1<<shadowside, rh.dynmin, rh.dynmax);
3726     if(rhdynmm) batcheddynamicmodelbounds(1<<shadowside, rh.dynmin, rh.dynmax);
3727 
3728     if(rhforce || rh.prevdynmin.z < rh.prevdynmax.z || rh.dynmin.z < rh.dynmax.z || !rh.allcached())
3729     {
3730         if(rhinoq && oqfrags && !drawtex)
3731         {
3732             glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
3733             glDepthMask(GL_TRUE);
3734         }
3735 
3736         glBindFramebuffer_(GL_FRAMEBUFFER, rsmfbo);
3737 
3738         shadowmatrix.mul(rsm.proj, rsm.model);
3739         GLOBALPARAM(rsmmatrix, shadowmatrix);
3740         GLOBALPARAMF(rsmdir, -rsm.lightview.x, -rsm.lightview.y, -rsm.lightview.z);
3741 
3742         glViewport(0, 0, rsmsize, rsmsize);
3743         glClearColor(0, 0, 0, 0);
3744         glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
3745 
3746         renderrsmgeom(rhdyntex!=0);
3747         rendershadowmodelbatches(rhdynmm!=0);
3748 
3749         rh.renderslices();
3750 
3751         if(rhinoq && oqfrags && !drawtex)
3752         {
3753             glBindFramebuffer_(GL_FRAMEBUFFER, msaasamples ? msfbo : gfbo);
3754             glViewport(0, 0, vieww, viewh);
3755 
3756             glFlush();
3757         }
3758     }
3759 
3760     clearbatchedmapmodels();
3761 
3762     shadowmapping = 0;
3763 
3764     endtimer(rhtimer);
3765     endtimer(rhcputimer);
3766 }
3767 
rendercsmshadowmaps()3768 void rendercsmshadowmaps()
3769 {
3770     shadowmapping = SM_CASCADE;
3771     shadoworigin = vec(0, 0, 0);
3772     shadowdir = csm.lightview;
3773     shadowbias = csm.lightview.project_bb(worldmin, worldmax);
3774     shadowradius = fabs(csm.lightview.project_bb(worldmax, worldmin));
3775 
3776     float polyfactor = csmpolyfactor, polyoffset = csmpolyoffset;
3777     if(smfilter > 2) { polyfactor = csmpolyfactor2; polyoffset = csmpolyoffset2; }
3778     if(polyfactor || polyoffset)
3779     {
3780         glPolygonOffset(polyfactor, polyoffset);
3781         glEnable(GL_POLYGON_OFFSET_FILL);
3782     }
3783 
3784     findshadowvas();
3785     findshadowmms();
3786 
3787     shadowmaskbatchedmodels(smdynshadow!=0);
3788     batchshadowmapmodels();
3789 
3790     loopi(csmsplits) if(csm.splits[i].idx >= 0)
3791     {
3792         const shadowmapinfo &sm = shadowmaps[csm.splits[i].idx];
3793 
3794         shadowmatrix.mul(csm.splits[i].proj, csm.model);
3795         GLOBALPARAM(shadowmatrix, shadowmatrix);
3796 
3797         glViewport(sm.x, sm.y, sm.size, sm.size);
3798         glScissor(sm.x, sm.y, sm.size, sm.size);
3799         glClear(GL_DEPTH_BUFFER_BIT);
3800 
3801         shadowside = i;
3802 
3803         rendershadowmapworld();
3804         rendershadowmodelbatches();
3805     }
3806 
3807     clearbatchedmapmodels();
3808 
3809     if(polyfactor || polyoffset) glDisable(GL_POLYGON_OFFSET_FILL);
3810 
3811     shadowmapping = 0;
3812 }
3813 
calcshadowinfo(const extentity & e,vec & origin,float & radius,vec & spotloc,int & spotangle,float & bias)3814 int calcshadowinfo(const extentity &e, vec &origin, float &radius, vec &spotloc, int &spotangle, float &bias)
3815 {
3816     if(e.attr5&L_NOSHADOW || e.attr1 <= smminradius) return SM_NONE;
3817 
3818     origin = e.o;
3819     radius = e.attr1;
3820     int type, w, border;
3821     float lod;
3822     if(e.attached && e.attached->type == ET_SPOTLIGHT)
3823     {
3824         type = SM_SPOT;
3825         w = 1;
3826         border = 0;
3827         lod = smspotprec;
3828         spotloc = e.attached->o;
3829         spotangle = clamp(int(e.attached->attr1), 1, 89);
3830     }
3831     else
3832     {
3833         type = SM_CUBEMAP;
3834         w = 3;
3835         lod = smcubeprec;
3836         border = smfilter > 2 ? smborder2 : smborder;
3837         spotloc = e.o;
3838         spotangle = 0;
3839     }
3840 
3841     lod *= smminsize;
3842     int size = clamp(int(ceil((lod * shadowatlaspacker.w) / SHADOWATLAS_SIZE)), 1, shadowatlaspacker.w / w);
3843     bias = border / float(size - border);
3844 
3845     return type;
3846 }
3847 
3848 matrix4 shadowmatrix;
3849 
rendershadowmaps()3850 void rendershadowmaps()
3851 {
3852     float polyfactor = smpolyfactor, polyoffset = smpolyoffset;
3853     if(smfilter > 2) { polyfactor = smpolyfactor2; polyoffset = smpolyoffset2; }
3854     if(polyfactor || polyoffset)
3855     {
3856         glPolygonOffset(polyfactor, polyoffset);
3857         glEnable(GL_POLYGON_OFFSET_FILL);
3858     }
3859 
3860     const vector<extentity *> &ents = entities::getents();
3861     loopv(shadowmaps)
3862     {
3863         shadowmapinfo &sm = shadowmaps[i];
3864         if(sm.light < 0) continue;
3865 
3866         lightinfo &l = lights[sm.light];
3867         extentity *e = l.ent >= 0 ? ents[l.ent] : NULL;
3868 
3869         int border, sidemask;
3870         if(l.spot)
3871         {
3872             shadowmapping = SM_SPOT;
3873             border = 0;
3874             sidemask = 1;
3875         }
3876         else
3877         {
3878             shadowmapping = SM_CUBEMAP;
3879             border = smfilter > 2 ? smborder2 : smborder;
3880             sidemask = smsidecull ? cullfrustumsides(l.o, l.radius, sm.size, border) : 0x3F;
3881         }
3882 
3883         sm.sidemask = sidemask;
3884 
3885         shadoworigin = l.o;
3886         shadowradius = l.radius;
3887         shadowbias = border / float(sm.size - border);
3888         shadowdir = l.dir;
3889         shadowspot = l.spot;
3890 
3891         shadowmesh *mesh = e ? findshadowmesh(l.ent, *e) : NULL;
3892 
3893         findshadowvas();
3894         findshadowmms();
3895 
3896         shadowmaskbatchedmodels(!(l.flags&L_NODYNSHADOW) && smdynshadow);
3897         batchshadowmapmodels(mesh != NULL);
3898 
3899         shadowcacheval *cached = NULL;
3900         int cachemask = 0;
3901         if(smcache)
3902         {
3903             int dynmask = smcache <= 1 ? batcheddynamicmodels() : 0;
3904             cached = sm.cached;
3905             if(cached)
3906             {
3907                 if(!debugshadowatlas) cachemask = cached->sidemask & ~dynmask;
3908                 sm.sidemask |= cachemask;
3909             }
3910             sm.sidemask &= ~dynmask;
3911 
3912             sidemask &= ~cachemask;
3913             if(!sidemask) { clearbatchedmapmodels(); continue; }
3914         }
3915 
3916         float smnearclip = SQRT3 / l.radius, smfarclip = SQRT3;
3917         matrix4 smprojmatrix(vec4(float(sm.size - border) / sm.size, 0, 0, 0),
3918                               vec4(0, float(sm.size - border) / sm.size, 0, 0),
3919                               vec4(0, 0, -(smfarclip + smnearclip) / (smfarclip - smnearclip), -1),
3920                               vec4(0, 0, -2*smnearclip*smfarclip / (smfarclip - smnearclip), 0));
3921 
3922         if(shadowmapping == SM_SPOT)
3923         {
3924             glViewport(sm.x, sm.y, sm.size, sm.size);
3925             glScissor(sm.x, sm.y, sm.size, sm.size);
3926             glClear(GL_DEPTH_BUFFER_BIT);
3927 
3928             float invradius = 1.0f / l.radius, spotscale = invradius * cotan360(l.spot);
3929             matrix4 spotmatrix(vec(l.spotx).mul(spotscale), vec(l.spoty).mul(spotscale), vec(l.dir).mul(-invradius));
3930             spotmatrix.translate(vec(l.o).neg());
3931             shadowmatrix.mul(smprojmatrix, spotmatrix);
3932             GLOBALPARAM(shadowmatrix, shadowmatrix);
3933 
3934             glCullFace((l.dir.z >= 0) == (smcullside != 0) ? GL_BACK : GL_FRONT);
3935 
3936             shadowside = 0;
3937 
3938             if(mesh) rendershadowmesh(mesh); else rendershadowmapworld();
3939             rendershadowmodelbatches();
3940         }
3941         else
3942         {
3943             if(!cachemask)
3944             {
3945                 int cx1 = sidemask & 0x03 ? 0 : (sidemask & 0xC ? sm.size : 2 * sm.size),
3946                     cx2 = sidemask & 0x30 ? 3 * sm.size : (sidemask & 0xC ? 2 * sm.size : sm.size),
3947                     cy1 = sidemask & 0x15 ? 0 : sm.size,
3948                     cy2 = sidemask & 0x2A ? 2 * sm.size : sm.size;
3949                 glScissor(sm.x + cx1, sm.y + cy1, cx2 - cx1, cy2 - cy1);
3950                 glClear(GL_DEPTH_BUFFER_BIT);
3951             }
3952             loop(side, 6) if(sidemask&(1<<side))
3953             {
3954                 int sidex = (side>>1)*sm.size, sidey = (side&1)*sm.size;
3955                 glViewport(sm.x + sidex, sm.y + sidey, sm.size, sm.size);
3956                 glScissor(sm.x + sidex, sm.y + sidey, sm.size, sm.size);
3957                 if(cachemask) glClear(GL_DEPTH_BUFFER_BIT);
3958 
3959                 matrix4 cubematrix(cubeshadowviewmatrix[side]);
3960                 cubematrix.scale(1.0f/l.radius);
3961                 cubematrix.translate(vec(l.o).neg());
3962                 shadowmatrix.mul(smprojmatrix, cubematrix);
3963                 GLOBALPARAM(shadowmatrix, shadowmatrix);
3964 
3965                 glCullFace((side & 1) ^ (side >> 2) ^ smcullside ? GL_FRONT : GL_BACK);
3966 
3967                 shadowside = side;
3968 
3969                 if(mesh) rendershadowmesh(mesh); else rendershadowmapworld();
3970                 rendershadowmodelbatches();
3971             }
3972         }
3973 
3974         clearbatchedmapmodels();
3975     }
3976 
3977     if(polyfactor || polyoffset) glDisable(GL_POLYGON_OFFSET_FILL);
3978 
3979     shadowmapping = 0;
3980 }
3981 
rendershadowatlas()3982 void rendershadowatlas()
3983 {
3984     timer *smcputimer = begintimer("shadow map", false);
3985     timer *smtimer = begintimer("shadow map");
3986 
3987     glBindFramebuffer_(GL_FRAMEBUFFER, shadowatlasfbo);
3988     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
3989 
3990     if(debugshadowatlas)
3991     {
3992         glClearDepth(0);
3993         glClear(GL_DEPTH_BUFFER_BIT);
3994         glClearDepth(1);
3995     }
3996 
3997     glEnable(GL_SCISSOR_TEST);
3998 
3999     // sun light
4000     if(!sunlight.iszero() && csmshadowmap)
4001     {
4002         csm.setup();
4003         rendercsmshadowmaps();
4004     }
4005 
4006     packlights();
4007 
4008     // point lights
4009     rendershadowmaps();
4010 
4011     glCullFace(GL_BACK);
4012     glDisable(GL_SCISSOR_TEST);
4013     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
4014 
4015     endtimer(smtimer);
4016     endtimer(smcputimer);
4017 }
4018 
4019 FVAR(refractmargin, 0, 0.1f, 1);
4020 FVAR(refractdepth, 1e-3f, 16, 1e3f);
4021 
rendertransparent()4022 void rendertransparent()
4023 {
4024     int hasalphavas = findalphavas();
4025     int hasmats = findmaterials();
4026     bool hasmodels = transmdlsx1 < transmdlsx2 && transmdlsy1 < transmdlsy2;
4027     if(!hasalphavas && !hasmats && !hasmodels)
4028     {
4029         if(!editmode) renderparticles();
4030         return;
4031     }
4032 
4033     if(!editmode && particlelayers && ghasstencil) renderparticles(PL_UNDER);
4034 
4035     timer *transtimer = begintimer("transparent");
4036 
4037     if(hasalphavas&4 || hasmats&4)
4038     {
4039         glBindFramebuffer_(GL_FRAMEBUFFER, msaasamples ? msrefractfbo : refractfbo);
4040         glDepthMask(GL_FALSE);
4041         if(msaasamples) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
4042         else glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
4043         float sx1 = min(alpharefractsx1, matrefractsx1), sy1 = min(alpharefractsy1, matrefractsy1),
4044               sx2 = max(alpharefractsx2, matrefractsx2), sy2 = max(alpharefractsy2, matrefractsy2);
4045         bool scissor = sx1 > -1 || sy1 > -1 || sx2 < 1 || sy2 < 1;
4046         if(scissor)
4047         {
4048             int x1 = int(floor(max(sx1*0.5f+0.5f-refractmargin*viewh/vieww, 0.0f)*vieww)),
4049                 y1 = int(floor(max(sy1*0.5f+0.5f-refractmargin, 0.0f)*viewh)),
4050                 x2 = int(ceil(min(sx2*0.5f+0.5f+refractmargin*viewh/vieww, 1.0f)*vieww)),
4051                 y2 = int(ceil(min(sy2*0.5f+0.5f+refractmargin, 1.0f)*viewh));
4052             glEnable(GL_SCISSOR_TEST);
4053             glScissor(x1, y1, x2 - x1, y2 - y1);
4054         }
4055         glClearColor(0, 0, 0, 0);
4056         glClear(GL_COLOR_BUFFER_BIT);
4057         if(scissor) glDisable(GL_SCISSOR_TEST);
4058         GLOBALPARAMF(refractdepth, 1.0f/refractdepth);
4059         SETSHADER(refractmask);
4060         if(hasalphavas&4) renderrefractmask();
4061         if(hasmats&4) rendermaterialmask();
4062 
4063         glDepthMask(GL_TRUE);
4064     }
4065 
4066     glActiveTexture_(GL_TEXTURE7);
4067     if(msaasamples) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msrefracttex);
4068     else glBindTexture(GL_TEXTURE_RECTANGLE, refracttex);
4069     glActiveTexture_(GL_TEXTURE8);
4070     if(msaasamples) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mshdrtex);
4071     else glBindTexture(GL_TEXTURE_RECTANGLE, hdrtex);
4072     glActiveTexture_(GL_TEXTURE9);
4073     if(msaasamples) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
4074     else glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
4075     glActiveTexture_(GL_TEXTURE0);
4076 
4077     if(ghasstencil) glEnable(GL_STENCIL_TEST);
4078 
4079     matrix4 raymatrix(vec(-0.5f*vieww*projmatrix.a.x, 0, 0.5f*vieww - 0.5f*vieww*projmatrix.c.x),
4080                       vec(0, -0.5f*viewh*projmatrix.b.y, 0.5f*viewh - 0.5f*viewh*projmatrix.c.y));
4081     raymatrix.muld(cammatrix);
4082     GLOBALPARAM(raymatrix, raymatrix);
4083     GLOBALPARAM(linearworldmatrix, linearworldmatrix);
4084 
4085     uint tiles[LIGHTTILE_MAXH];
4086     float allsx1 = 1, allsy1 = 1, allsx2 = -1, allsy2 = -1, sx1, sy1, sx2, sy2;
4087 
4088     loop(layer, 4)
4089     {
4090         switch(layer)
4091         {
4092         case 0:
4093             if(!(hasmats&1)) continue;
4094             sx1 = matliquidsx1; sy1 = matliquidsy1; sx2 = matliquidsx2; sy2 = matliquidsy2;
4095             memcpy(tiles, matliquidtiles, sizeof(tiles));
4096             break;
4097         case 1:
4098             if(!(hasalphavas&1)) continue;
4099             sx1 = alphabacksx1; sy1 = alphabacksy1; sx2 = alphabacksx2; sy2 = alphabacksy2;
4100             memcpy(tiles, alphatiles, sizeof(tiles));
4101             break;
4102         case 2:
4103             if(!(hasalphavas&2) && !(hasmats&2)) continue;
4104             sx1 = alphafrontsx1; sy1 = alphafrontsy1; sx2 = alphafrontsx2; sy2 = alphafrontsy2;
4105             memcpy(tiles, alphatiles, sizeof(tiles));
4106             if(hasmats&2)
4107             {
4108                 sx1 = min(sx1, matsolidsx1);
4109                 sy1 = min(sy1, matsolidsy1);
4110                 sx2 = max(sx2, matsolidsx2);
4111                 sy2 = max(sy2, matsolidsy2);
4112                 loopj(LIGHTTILE_MAXH) tiles[j] |= matsolidtiles[j];
4113             }
4114             break;
4115         case 3:
4116             if(!hasmodels) continue;
4117             sx1 = transmdlsx1; sy1 = transmdlsy1; sx2 = transmdlsx2; sy2 = transmdlsy2;
4118             memcpy(tiles, transmdltiles, sizeof(tiles));
4119             break;
4120 
4121         default:
4122             continue;
4123         }
4124 
4125         allsx1 = min(allsx1, sx1);
4126         allsy1 = min(allsy1, sy1);
4127         allsx2 = max(allsx2, sx2);
4128         allsy2 = max(allsy2, sy2);
4129 
4130         glBindFramebuffer_(GL_FRAMEBUFFER, msaasamples ? msfbo : gfbo);
4131         if(ghasstencil)
4132         {
4133             glStencilFunc(GL_ALWAYS, layer+1, ~0);
4134             glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
4135         }
4136         else
4137         {
4138             bool scissor = sx1 > -1 || sy1 > -1 || sx2 < 1 || sy2 < 1;
4139             if(scissor)
4140             {
4141                 int x1 = int(floor((sx1*0.5f+0.5f)*vieww)), y1 = int(floor((sy1*0.5f+0.5f)*viewh)),
4142                     x2 = int(ceil((sx2*0.5f+0.5f)*vieww)), y2 = int(ceil((sy2*0.5f+0.5f)*viewh));
4143                 glEnable(GL_SCISSOR_TEST);
4144                 glScissor(x1, y1, x2 - x1, y2 - y1);
4145             }
4146 
4147             maskgbuffer("n");
4148             glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
4149             glClearColor(0, 0, 0, 0);
4150             glClear(GL_COLOR_BUFFER_BIT);
4151             glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
4152             if(scissor) glDisable(GL_SCISSOR_TEST);
4153         }
4154         maskgbuffer("cndg");
4155 
4156         if(wireframe && editmode) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
4157 
4158         switch(layer)
4159         {
4160         case 0:
4161             renderliquidmaterials();
4162             break;
4163         case 1:
4164             renderalphageom(1);
4165             break;
4166         case 2:
4167             if(hasalphavas&2) renderalphageom(2);
4168             if(hasmats&2) rendersolidmaterials();
4169             renderstains(STAINBUF_TRANSPARENT, true);
4170             break;
4171         case 3:
4172             rendertransparentmodelbatches(layer+1);
4173             break;
4174         }
4175 
4176         if(wireframe && editmode) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
4177 
4178         if(msaasamples)
4179         {
4180             glBindFramebuffer_(GL_FRAMEBUFFER, mshdrfbo);
4181             if((ghasstencil && msaaedgedetect) || (!hasMSS && msaasamples==2)) loopi(2) renderlights(sx1, sy1, sx2, sy2, tiles, layer+1, i+1, true);
4182             else renderlights(sx1, sy1, sx2, sy2, tiles, layer+1, 3, true);
4183         }
4184         else
4185         {
4186             glBindFramebuffer_(GL_FRAMEBUFFER, hdrfbo);
4187             renderlights(sx1, sy1, sx2, sy2, tiles, layer+1, 0, true);
4188         }
4189 
4190         switch(layer)
4191         {
4192         case 2:
4193             if(ghasstencil)
4194             {
4195                 glStencilFunc(GL_EQUAL, layer+1, 0x07);
4196                 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
4197             }
4198             renderstains(STAINBUF_TRANSPARENT, false);
4199             break;
4200         }
4201     }
4202 
4203     if(ghasstencil) glDisable(GL_STENCIL_TEST);
4204 
4205     endtimer(transtimer);
4206 
4207     if(editmode) return;
4208 
4209     if(particlelayers && ghasstencil)
4210     {
4211         bool scissor = allsx1 > -1 || allsy1 > -1 || allsx2 < 1 || allsy2 < 1;
4212         if(scissor)
4213         {
4214             int x1 = int(floor((allsx1*0.5f+0.5f)*vieww)), y1 = int(floor((allsy1*0.5f+0.5f)*viewh)),
4215                 x2 = int(ceil((allsx2*0.5f+0.5f)*vieww)), y2 = int(ceil((allsy2*0.5f+0.5f)*viewh));
4216             glEnable(GL_SCISSOR_TEST);
4217             glScissor(x1, y1, x2 - x1, y2 - y1);
4218         }
4219         glStencilFunc(GL_NOTEQUAL, 0, 0x07);
4220         glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
4221         glEnable(GL_STENCIL_TEST);
4222         renderparticles(PL_OVER);
4223         glDisable(GL_STENCIL_TEST);
4224         if(scissor) glDisable(GL_SCISSOR_TEST);
4225 
4226         renderparticles(PL_NOLAYER);
4227     }
4228     else renderparticles();
4229 }
4230 
4231 VAR(gdepthclear, 0, 1, 1);
4232 VAR(gcolorclear, 0, 1, 1);
4233 
preparegbuffer(bool depthclear)4234 void preparegbuffer(bool depthclear)
4235 {
4236     glBindFramebuffer_(GL_FRAMEBUFFER, msaasamples ? msfbo : gfbo);
4237     glViewport(0, 0, vieww, viewh);
4238 
4239     if(drawtex && gdepthinit)
4240     {
4241         glEnable(GL_SCISSOR_TEST);
4242         glScissor(0, 0, vieww, viewh);
4243     }
4244     if(gdepthformat && gdepthclear)
4245     {
4246         maskgbuffer("d");
4247         if(gdepthformat == 1) glClearColor(1, 1, 1, 1);
4248         else glClearColor(-farplane, 0, 0, 0);
4249         glClear(GL_COLOR_BUFFER_BIT);
4250         maskgbuffer("cn");
4251     }
4252     else maskgbuffer("cnd");
4253     if(gcolorclear) glClearColor(0, 0, 0, 0);
4254     glClear((depthclear ? GL_DEPTH_BUFFER_BIT : 0)|(gcolorclear ? GL_COLOR_BUFFER_BIT : 0)|(depthclear && ghasstencil ? GL_STENCIL_BUFFER_BIT : 0));
4255     if(gdepthformat && gdepthclear) maskgbuffer("cnd");
4256     if(drawtex && gdepthinit) glDisable(GL_SCISSOR_TEST);
4257     gdepthinit = true;
4258 
4259     matrix4 invscreenmatrix;
4260     invscreenmatrix.identity();
4261     invscreenmatrix.settranslation(-1.0f, -1.0f, -1.0f);
4262     invscreenmatrix.setscale(2.0f/vieww, 2.0f/viewh, 2.0f);
4263     eyematrix.muld(invprojmatrix, invscreenmatrix);
4264     if(drawtex == DRAWTEX_MINIMAP)
4265     {
4266         linearworldmatrix.muld(invcamprojmatrix, invscreenmatrix);
4267         if(!gdepthformat) worldmatrix = linearworldmatrix;
4268         linearworldmatrix.a.z = invcammatrix.a.z;
4269         linearworldmatrix.b.z = invcammatrix.b.z;
4270         linearworldmatrix.c.z = invcammatrix.c.z;
4271         linearworldmatrix.d.z = invcammatrix.d.z;
4272         if(gdepthformat) worldmatrix = linearworldmatrix;
4273 
4274         GLOBALPARAMF(radialfogscale, 0, 0, 0, 0);
4275     }
4276     else
4277     {
4278         float xscale = eyematrix.a.x, yscale = eyematrix.b.y, xoffset = eyematrix.d.x, yoffset = eyematrix.d.y, zscale = eyematrix.d.z;
4279         matrix4 depthmatrix(vec(xscale/zscale, 0, xoffset/zscale), vec(0, yscale/zscale, yoffset/zscale));
4280         linearworldmatrix.muld(invcammatrix, depthmatrix);
4281         if(gdepthformat) worldmatrix = linearworldmatrix;
4282         else worldmatrix.muld(invcamprojmatrix, invscreenmatrix);
4283 
4284         GLOBALPARAMF(radialfogscale, xscale/zscale, yscale/zscale, xoffset/zscale, yoffset/zscale);
4285     }
4286 
4287     screenmatrix.identity();
4288     screenmatrix.settranslation(0.5f*vieww, 0.5f*viewh, 0.5f);
4289     screenmatrix.setscale(0.5f*vieww, 0.5f*viewh, 0.5f);
4290     screenmatrix.muld(camprojmatrix);
4291 
4292     GLOBALPARAMF(viewsize, vieww, viewh, 1.0f/vieww, 1.0f/viewh);
4293     GLOBALPARAMF(gdepthscale, eyematrix.d.z, eyematrix.c.w, eyematrix.d.w);
4294     GLOBALPARAMF(gdepthpackparams, -1.0f/farplane, -255.0f/farplane, -(255.0f*255.0f)/farplane);
4295     GLOBALPARAMF(gdepthunpackparams, -farplane, -farplane/255.0f, -farplane/(255.0f*255.0f));
4296     GLOBALPARAM(worldmatrix, worldmatrix);
4297 
4298     GLOBALPARAMF(ldrscale, ldrscale);
4299     GLOBALPARAMF(hdrgamma, hdrgamma, 1.0f/hdrgamma);
4300     GLOBALPARAM(camera, camera1->o);
4301     GLOBALPARAMF(millis, lastmillis/1000.0f);
4302 
4303     GLERROR;
4304 }
4305 
rendergbuffer(bool depthclear)4306 void rendergbuffer(bool depthclear)
4307 {
4308     timer *gcputimer = drawtex ? NULL : begintimer("g-buffer", false);
4309     timer *gtimer = drawtex ? NULL : begintimer("g-buffer");
4310 
4311     preparegbuffer(depthclear);
4312 
4313     if(limitsky())
4314     {
4315         renderexplicitsky();
4316         GLERROR;
4317     }
4318     rendergeom();
4319     GLERROR;
4320     renderdecals();
4321     GLERROR;
4322     resetmodelbatches();
4323     rendermapmodels();
4324     GLERROR;
4325 
4326     if(drawtex == DRAWTEX_MINIMAP)
4327     {
4328         renderminimapmaterials();
4329     }
4330     else if(!drawtex)
4331     {
4332         game::rendergame();
4333         rendermodelbatches();
4334         GLERROR;
4335         renderstains(STAINBUF_OPAQUE, true);
4336         GLERROR;
4337         renderavatar();
4338         GLERROR;
4339     }
4340 
4341     endtimer(gtimer);
4342     endtimer(gcputimer);
4343 }
4344 
shademinimap(const vec & color)4345 void shademinimap(const vec &color)
4346 {
4347     GLERROR;
4348 
4349     glBindFramebuffer_(GL_FRAMEBUFFER, msaasamples ? mshdrfbo : hdrfbo);
4350     glViewport(0, 0, vieww, viewh);
4351 
4352     if(color.x >= 0)
4353     {
4354         glClearColor(color.x, color.y, color.z, 0);
4355         glClear(GL_COLOR_BUFFER_BIT);
4356     }
4357 
4358     renderlights(-1, -1, 1, 1, NULL, 0, msaasamples ? -1 : 0);
4359     GLERROR;
4360 }
4361 
shademodelpreview(int x,int y,int w,int h,bool background,bool scissor)4362 void shademodelpreview(int x, int y, int w, int h, bool background, bool scissor)
4363 {
4364     GLERROR;
4365 
4366     glBindFramebuffer_(GL_FRAMEBUFFER, ovr::lensfbo[viewidx]);
4367     glViewport(x, y, w, h);
4368 
4369     if(msaasamples) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mscolortex);
4370     else glBindTexture(GL_TEXTURE_RECTANGLE, gcolortex);
4371     glActiveTexture_(GL_TEXTURE1);
4372     if(msaasamples) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msnormaltex);
4373     else glBindTexture(GL_TEXTURE_RECTANGLE, gnormaltex);
4374     glActiveTexture_(GL_TEXTURE3);
4375     if(msaasamples) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msdepthtex);
4376     else glBindTexture(GL_TEXTURE_RECTANGLE, gdepthtex);
4377     glActiveTexture_(GL_TEXTURE0);
4378 
4379     float lightscale = 2.0f*ldrscale;
4380     GLOBALPARAMF(lightscale, 0.1f*lightscale, 0.1f*lightscale, 0.1f*lightscale, lightscale);
4381     GLOBALPARAM(sunlightdir, vec(0, -1, 2).normalize());
4382     GLOBALPARAMF(sunlightcolor, 0.6f*lightscale, 0.6f*lightscale, 0.6f*lightscale);
4383 
4384     SETSHADER(modelpreview);
4385 
4386     LOCALPARAMF(cutout, background ? 0 : 1);
4387 
4388     if(scissor) glEnable(GL_SCISSOR_TEST);
4389     screenquad(vieww, viewh);
4390     if(scissor) glDisable(GL_SCISSOR_TEST);
4391 
4392     GLERROR;
4393 
4394     glViewport(0, 0, hudw, hudh);
4395 }
4396 
shadesky()4397 void shadesky()
4398 {
4399     glBindFramebuffer_(GL_FRAMEBUFFER, msaasamples ? mshdrfbo : hdrfbo);
4400     glViewport(0, 0, vieww, viewh);
4401 
4402     drawskybox((hdrclear > 0 ? hdrclear-- : msaasamples) > 0);
4403 }
4404 
shadegbuffer()4405 void shadegbuffer()
4406 {
4407     GLERROR;
4408 
4409     timer *shcputimer = begintimer("deferred shading", false);
4410     timer *shtimer = begintimer("deferred shading");
4411 
4412     shadesky();
4413 
4414     if(msaasamples)
4415     {
4416         if((ghasstencil && msaaedgedetect) || (!hasMSS && msaasamples==2)) loopi(2) renderlights(-1, -1, 1, 1, NULL, 0, i+1);
4417         else renderlights(-1, -1, 1, 1, NULL, 0, drawtex ? -1 : 3);
4418     }
4419     else renderlights();
4420     GLERROR;
4421 
4422     if(!drawtex) renderstains(STAINBUF_OPAQUE, false);
4423 
4424     endtimer(shtimer);
4425     endtimer(shcputimer);
4426 }
4427 
setuplights()4428 void setuplights()
4429 {
4430     GLERROR;
4431     setupgbuffer();
4432     if(bloomw < 0 || bloomh < 0) setupbloom(gw, gh);
4433     if(ao && (aow < 0 || aoh < 0)) setupao(gw, gh);
4434     if(!shadowatlasfbo) setupshadowatlas();
4435     if(useradiancehints() && !rhfbo) setupradiancehints();
4436     if(!deferredlightshader) loaddeferredlightshaders();
4437     if(drawtex == DRAWTEX_MINIMAP && !deferredminimapshader) deferredminimapshader = loaddeferredlightshader(msaasamples ? "mM" : "m");
4438     setupaa(gw, gh);
4439     GLERROR;
4440 }
4441 
debuglights()4442 bool debuglights()
4443 {
4444     if(debugshadowatlas) viewshadowatlas();
4445     else if(debugao) viewao();
4446     else if(debugdepth) viewdepth();
4447     else if(debugstencil) viewstencil();
4448     else if(debugrefract) viewrefract();
4449     else if(debuglightscissor) viewlightscissor();
4450     else if(debugrsm) viewrsm();
4451     else if(debugrh) viewrh();
4452     else if(!debugaa()) return false;
4453     return true;
4454 }
4455 
cleanuplights()4456 void cleanuplights()
4457 {
4458     cleanupgbuffer();
4459     cleanupbloom();
4460     cleanupao();
4461     cleanupshadowatlas();
4462     cleanupradiancehints();
4463     cleanuplightsphere();
4464     cleanupaa();
4465 }
4466 
4467