LCOV - code coverage report
Current view: top level - engine/render - renderlights.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 0.9 % 2182 19
Test Date: 2026-01-07 07:46:09 Functions: 5.9 % 152 9

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

Generated by: LCOV version 2.0-1