LCOV - code coverage report
Current view: top level - engine/render - renderlights.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 19 2174 0.9 %
Date: 2024-11-22 05:07:59 Functions: 9 150 6.0 %

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

Generated by: LCOV version 1.14