LCOV - code coverage report
Current view: top level - engine/render - radiancehints.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 1.3 % 602 8
Test Date: 2025-10-13 07:05:02 Functions: 4.2 % 48 2

            Line data    Source code
       1              : /*
       2              :  * radiancehints.cpp: radiance hints global illumination
       3              :  *
       4              :  * Libprimis supports global illumination via the Radiance Hints algorithm. This
       5              :  * global illumination is rendered using a reflective shadow map mapped from the
       6              :  * sun's location.
       7              :  *
       8              :  * For performance reasons, the radiance hints algorithm is only run on the global
       9              :  * sunlight. Running radiance hints on point lights would require complex, expensive
      10              :  * shadowmapping similar to standard lighting, and not perform particularly well.
      11              :  *
      12              :  * By only allowing the sunlight to be lit, Libprimis' global illumination only
      13              :  * has to worry about a single RSM (reflective shadow map) rather than 6 sided cube
      14              :  * maps (as point lights would require).
      15              :  */
      16              : #include "../libprimis-headers/cube.h"
      17              : #include "../../shared/geomexts.h"
      18              : #include "../../shared/glemu.h"
      19              : #include "../../shared/glexts.h"
      20              : 
      21              : #include "csm.h"
      22              : #include "hdr.h"
      23              : #include "octarender.h"
      24              : #include "radiancehints.h"
      25              : #include "rendergl.h"
      26              : #include "renderlights.h"
      27              : #include "rendermodel.h"
      28              : #include "rendertimers.h"
      29              : #include "renderva.h"
      30              : #include "renderwindow.h"
      31              : #include "shader.h"
      32              : #include "shaderparam.h"
      33              : #include "texture.h"
      34              : 
      35              : #include "interface/control.h"
      36              : 
      37              : #include "world/light.h"
      38              : #include "world/world.h"
      39              : 
      40              : std::array<GLuint, 8> rhtex = { 0, 0, 0, 0, 0, 0, 0, 0 };
      41              : GLuint rhfbo = 0;
      42              : constexpr int rhmaxgrid = 64; //subdivision count for radiance hints
      43              : 
      44              : 
      45              : //radiance hints (global illumination) vars
      46            0 : VARF(rhsplits, 1, 2, rhmaxsplits, { cleardeferredlightshaders(); cleanupradiancehints(); }); //`r`adiance `h`ints `splits`: number of radiance hints subdivisions
      47              : 
      48              : //distance at which to render global illumination in cubits
      49            0 : VARFR(gidist, 0, 384, 1024,
      50              : {
      51              :     clearradiancehintscache();
      52              :     cleardeferredlightshaders();
      53              :     if(!gidist)
      54              :     {
      55              :         cleanupradiancehints();
      56              :     }
      57              : });
      58              : 
      59            0 : FVARFR(giscale, 0, 1.5f, 1e3f,
      60              : {
      61              :     cleardeferredlightshaders();
      62              :     if(!giscale)
      63              :     {
      64              :         cleanupradiancehints();
      65              :     }
      66              : }); //`g`lobal `i`llumination `scale`
      67              : 
      68              : FVARR(giaoscale, 0, 3, 1e3f);                                                   //`g`lobal `i`llumination `a`mbient `o`cclusion `scale`: scale of ambient occlusion (corner darkening) on globally illuminated surfaces
      69            0 : VARFP(gi, 0, 1, 1, { cleardeferredlightshaders(); cleanupradiancehints(); });   //`g`lobal `i`llumination toggle: 0 disables global illumination
      70              : 
      71              : //debugrsm/rh used extern in renderlights
      72              : VAR(debugrsm, 0, 0, 2);                                                         //displays the `r`adiance hints `s`hadow `m`ap in the bottom right of the screen; 1 for view from sun pos, 2 for view from sun pos, normal map
      73              : VAR(debugrh, -1, 0, rhmaxsplits*(rhmaxgrid + 2));
      74              : VAR(rsmcull, 0, 1, 1);                                                          //`r`eflective `s`hadow `m`ap `cull`ing
      75              : 
      76              : reflectiveshadowmap rsm;
      77              : radiancehints rh;
      78              : Shader *rsmworldshader = nullptr;
      79              : 
      80              : namespace //internal functionality
      81              : {
      82            0 :     VARF(rhrect, 0, 0, 1, cleanupradiancehints());
      83            0 :     VARF(rhborder, 0, 1, 1, cleanupradiancehints());
      84            0 :     VARF(rsmsize, 64, 512, 2048, cleanupradiancehints());                           //`r`eflective `s`hadow `m`ap `size`: resolution (squared) of global illumination
      85            0 :     VARF(rhnearplane, 1, 1, 16, clearradiancehintscache());                         //`r`adiance `h`ints `near plane`: distance in gridpower 0 cubes before global illumination gets rendered
      86            0 :     VARF(rhfarplane, 64, 1024, 16384, clearradiancehintscache());                   //`r`adiance `h`ints `far plane`: distance in gridpower 0 cubes whereafter global illumination no longer gets calculated
      87            0 :     FVARF(rsmpradiustweak, 1e-3f, 1, 1e3f, clearradiancehintscache());              //`r`eflective `s`hadow `m`ap `p`robe `radius tweak`
      88            0 :     FVARF(rhpradiustweak, 1e-3f, 1, 1e3f, clearradiancehintscache());               //`r`adiance `h`ints `p`robe `radius tweak`
      89            0 :     FVARF(rsmdepthrange, 0, 1024, 1e6f, clearradiancehintscache());                 //`r`eflective `s`hadow `m`ap `depth range`
      90            0 :     FVARF(rsmdepthmargin, 0, 0.1f, 1e3f, clearradiancehintscache());                //`r`eflective `s`hadow `m`ap `depth margin`
      91            0 :     VARFP(rhprec, 0, 0, 1, cleanupradiancehints());                                 //`r`adiance `h`ints `prec`ision: toggles between rgba16 or rgba8 map for radiance hints
      92            0 :     VARFP(rsmprec, 0, 0, 3, cleanupradiancehints());                                //`r`eflective `s`hadow `m`ap `prec`ision: toggles the rsm bit depth between rgb8 (0,1) , r11g11b10 (rgb 32bit) (2), or rgb16 (3)
      93            0 :     VARFP(rsmdepthprec, 0, 0, 2, cleanupradiancehints());                           //`r`eflective `s`hadow `m`ap `depth` `prec`ision: toggles the rsm depth map (buffer) between 16b, 24b, or 32b
      94              :     FVAR(rhnudge, 0, 0.5f, 4);                                                      //`r`adiance `h`ints `nudge`: (minor) factor for rsmsplits offset
      95            0 :     FVARF(rhworldbias, 0, 0.5f, 10, clearradiancehintscache());                     //`r`adiance `h`ints `worldbias`: (minor) factor for radiance hints nudge
      96            0 :     FVARF(rhsplitweight, 0.20f, 0.6f, 0.95f, clearradiancehintscache());            //`r`adiance `h`ints `split weight`
      97            0 :     VARF(rhgrid, 3, 27, rhmaxgrid, cleanupradiancehints());                         //`r`adiance `h`ints `grid`: subdivisions for the radiance hints to calculate
      98            0 :     FVARF(rsmspread, 0, 0.35f, 1, clearradiancehintscache());                       //smoothness of `r`adiance hints `s`hadow `m`ap: higher is more blurred
      99              :     VAR(rhclipgrid, 0, 1, 1);                                                       //`r`adiance `h`ints `clip` `grid`: determines whether the radiance hints clips to grid
     100            0 :     VARF(rhcache, 0, 1, 1, cleanupradiancehints());                                 //`r`adiance `h`ints `cache`: determines whether to create cache textures and use the cache shader
     101            0 :     VARF(rhforce, 0, 0, 1, cleanupradiancehints());
     102            0 :     VARFP(rhtaps, 0, 20, 32, cleanupradiancehints());                               //`r`adiance `h`ints `taps`: number of sample points for global illumination
     103              :     VAR(rhdyntex, 0, 0, 1);                                                         //`r`adiance `h`ints `dyn`amic `tex`tures
     104              :     VAR(rhdynmm, 0, 0, 1);                                                          //`r`adiance `h`ints `dyn`amic `m`ap `m`odels
     105              : 
     106              :     uint rhclearmasks[2][rhmaxsplits][(rhmaxgrid+2+31)/32];
     107              :     //reflective shadow map buffers, rendered from sun location
     108              :     GLuint rsmdepthtex = 0,
     109              :            rsmcolortex = 0,
     110              :            rsmnormaltex = 0,
     111              :            rsmfbo = 0;
     112              :     std::array<GLuint, 4> rhrb = {0, 0, 0, 0};
     113              : 
     114              :     Shader *radiancehintsshader = nullptr;
     115              : 
     116            0 :     Shader *loadradiancehintsshader()
     117              :     {
     118            0 :         std::string name = std::string("radiancehints").append(std::to_string(rhtaps));
     119            0 :         return generateshader(name, "radiancehintsshader %d", rhtaps);
     120            0 :     }
     121              : 
     122            0 :     void loadrhshaders()
     123              :     {
     124            0 :         if(rhborder)
     125              :         {
     126            0 :             useshaderbyname("radiancehintsborder");
     127              :         }
     128            0 :         if(rhcache)
     129              :         {
     130            0 :             useshaderbyname("radiancehintscached");
     131              :         }
     132            0 :         useshaderbyname("radiancehintsdisable");
     133            0 :         radiancehintsshader = loadradiancehintsshader();
     134            0 :         rsmworldshader = useshaderbyname("rsmworld");
     135            0 :         useshaderbyname("rsmsky");
     136            0 :     }
     137              : 
     138            0 :     void clearrhshaders()
     139              :     {
     140            0 :         radiancehintsshader = nullptr;
     141            0 :         rsmworldshader = nullptr;
     142            0 :     }
     143              : 
     144              :     //defines a rectangle with corners at x1...y2
     145            0 :     void rhquad(float x1, float y1, float x2, float y2, float tx1, float ty1, float tx2, float ty2, float tz)
     146              :     {
     147            0 :         gle::begin(GL_TRIANGLE_STRIP);
     148            0 :         gle::attribf(x2, y1); gle::attribf(tx2, ty1, tz);
     149            0 :         gle::attribf(x1, y1); gle::attribf(tx1, ty1, tz);
     150            0 :         gle::attribf(x2, y2); gle::attribf(tx2, ty2, tz);
     151            0 :         gle::attribf(x1, y2); gle::attribf(tx1, ty2, tz);
     152            0 :         gle::end();
     153            0 :     }
     154              : 
     155            0 :     void rhquad(float dx1, float dy1, float dx2, float dy2, float dtx1, float dty1, float dtx2, float dty2, float dtz,
     156              :                 float px1, float py1, float px2, float py2, float ptx1, float pty1, float ptx2, float pty2, float ptz)
     157              :     {
     158            0 :         gle::begin(GL_TRIANGLE_STRIP);
     159            0 :         gle::attribf(dx2, dy1); gle::attribf(dtx2, dty1, dtz);
     160            0 :         gle::attribf(px2, py1); gle::attribf(ptx2, pty1, ptz);
     161              : 
     162            0 :         gle::attribf(dx1, dy1); gle::attribf(dtx1, dty1, dtz);
     163            0 :         gle::attribf(px1, py1); gle::attribf(ptx1, pty1, ptz);
     164              : 
     165            0 :         gle::attribf(dx1, dy2); gle::attribf(dtx1, dty2, dtz);
     166            0 :         gle::attribf(px1, py2); gle::attribf(ptx1, pty2, ptz);
     167              : 
     168            0 :         gle::attribf(dx2, dy2); gle::attribf(dtx2, dty2, dtz);
     169            0 :         gle::attribf(px2, py2); gle::attribf(ptx2, pty2, ptz);
     170              : 
     171            0 :         gle::attribf(dx2, dy1); gle::attribf(dtx2, dty1, dtz);
     172            0 :         gle::attribf(px2, py1); gle::attribf(ptx2, pty1, ptz);
     173            0 :         gle::end();
     174            0 :     }
     175              : }
     176              : 
     177              : //externally relevant functionality
     178              : 
     179            0 : void viewrsm()
     180              : {
     181            0 :     int w = std::min(hudw(), hudh())/2,
     182            0 :         h = (w*hudh())/hudw(),
     183            0 :         x = hudw()-w,
     184            0 :         y = hudh()-h;
     185            0 :     SETSHADER(hudrect);
     186            0 :     gle::colorf(1, 1, 1);
     187            0 :     glBindTexture(GL_TEXTURE_RECTANGLE, debugrsm == 2 ? rsmnormaltex : rsmcolortex);
     188            0 :     debugquad(x, y, w, h, 0, 0, rsmsize, rsmsize);
     189            0 : }
     190              : 
     191            0 : void setupradiancehints()
     192              : {
     193            0 :     GLenum rhformat = rhprec >= 1 ? GL_RGBA16F : GL_RGBA8;
     194            0 :     for(int i = 0; i < (!rhrect && rhcache ? 8 : 4); ++i)
     195              :     {
     196            0 :         if(!rhtex[i])
     197              :         {
     198            0 :             glGenTextures(1, &rhtex[i]);
     199              :         }
     200            0 :         create3dtexture(rhtex[i], rhgrid+2*rhborder, rhgrid+2*rhborder, (rhgrid+2*rhborder)*rhsplits, nullptr, 7, 1, rhformat);
     201            0 :         if(rhborder)
     202              :         {
     203            0 :             glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
     204            0 :             glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
     205            0 :             glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
     206            0 :             GLfloat border[4] = { 0.5f, 0.5f, 0.5f, 0 };
     207            0 :             glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, border);
     208              :         }
     209              :     }
     210            0 :     if(!rhfbo)
     211              :     {
     212            0 :         glGenFramebuffers(1, &rhfbo);
     213              :     }
     214            0 :     glBindFramebuffer(GL_FRAMEBUFFER, rhfbo);
     215              : 
     216            0 :     if(rhrect)
     217              :     {
     218            0 :         for(size_t i = 0; i < rhrb.size(); ++i)
     219              :         {
     220            0 :             if(!rhrb[i])
     221              :             {
     222            0 :                 glGenRenderbuffers(1, &rhrb[i]);
     223              :             }
     224            0 :             glBindRenderbuffer(GL_RENDERBUFFER, rhrb[i]);
     225            0 :             glRenderbufferStorage(GL_RENDERBUFFER, rhformat, (rhgrid + 2*rhborder)*(rhgrid + 2*rhborder), (rhgrid + 2*rhborder)*rhsplits);
     226            0 :             glBindRenderbuffer(GL_RENDERBUFFER, 0);
     227            0 :             glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_RENDERBUFFER, rhrb[i]);
     228              :         }
     229              :     }
     230              :     else
     231              :     {
     232            0 :         for(int i = 0; i < 4; ++i)
     233              :         {
     234            0 :             glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_3D, rhtex[i], 0, 0);
     235              :         }
     236              :     }
     237              : 
     238              :     static const std::array<GLenum, 4> drawbufs =
     239              :     {
     240              :         GL_COLOR_ATTACHMENT0,
     241              :         GL_COLOR_ATTACHMENT1,
     242              :         GL_COLOR_ATTACHMENT2,
     243              :         GL_COLOR_ATTACHMENT3
     244              :     };
     245            0 :     glDrawBuffers(4, drawbufs.data());
     246              : 
     247            0 :     if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
     248              :     {
     249            0 :         fatal("failed allocating radiance hints buffer!");
     250              :     }
     251              : 
     252            0 :     if(!rsmdepthtex)
     253              :     {
     254            0 :         glGenTextures(1, &rsmdepthtex);
     255              :     }
     256            0 :     if(!rsmcolortex)
     257              :     {
     258            0 :         glGenTextures(1, &rsmcolortex);
     259              :     }
     260            0 :     if(!rsmnormaltex)
     261              :     {
     262            0 :         glGenTextures(1, &rsmnormaltex);
     263              :     }
     264            0 :     if(!rsmfbo)
     265              :     {
     266            0 :         glGenFramebuffers(1, &rsmfbo);
     267              :     }
     268              : 
     269            0 :     glBindFramebuffer(GL_FRAMEBUFFER, rsmfbo);
     270            0 :     GLenum rsmformat = gethdrformat(rsmprec, GL_RGBA8);
     271              : 
     272            0 :     createtexture(rsmdepthtex, rsmsize, rsmsize, nullptr, 3, 0, rsmdepthprec > 1 ? GL_DEPTH_COMPONENT32 : (rsmdepthprec ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16), GL_TEXTURE_RECTANGLE);
     273            0 :     createtexture(rsmcolortex, rsmsize, rsmsize, nullptr, 3, 0, rsmformat, GL_TEXTURE_RECTANGLE);
     274            0 :     createtexture(rsmnormaltex, rsmsize, rsmsize, nullptr, 3, 0, rsmformat, GL_TEXTURE_RECTANGLE);
     275              : 
     276            0 :     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_RECTANGLE, rsmdepthtex, 0);
     277            0 :     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, rsmcolortex, 0);
     278            0 :     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_RECTANGLE, rsmnormaltex, 0);
     279              : 
     280            0 :     glDrawBuffers(2, drawbufs.data());
     281              : 
     282            0 :     if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
     283              :     {
     284            0 :         fatal("failed allocating RSM buffer!");
     285              :     }
     286            0 :     glBindFramebuffer(GL_FRAMEBUFFER, 0);
     287            0 :     loadrhshaders();
     288            0 :     clearradiancehintscache();
     289            0 : }
     290              : 
     291            0 : void cleanupradiancehints()
     292              : {
     293            0 :     clearradiancehintscache();
     294              : 
     295            0 :     for(GLuint &i : rhtex)
     296              :     {
     297            0 :         if(i)
     298              :         {
     299            0 :             glDeleteTextures(1, &i);
     300            0 :             i = 0;
     301              :         }
     302              :     }
     303            0 :     for(GLuint &i : rhrb)
     304              :     {
     305            0 :         if(i)
     306              :         {
     307            0 :             glDeleteRenderbuffers(1, &i);
     308            0 :             i = 0;
     309              :         }
     310              :     }
     311            0 :     if(rhfbo)
     312              :     {
     313            0 :         glDeleteFramebuffers(1, &rhfbo);
     314            0 :         rhfbo = 0;
     315              :     }
     316            0 :     if(rsmdepthtex)
     317              :     {
     318            0 :         glDeleteTextures(1, &rsmdepthtex);
     319            0 :         rsmdepthtex = 0;
     320              :     }
     321            0 :     if(rsmcolortex)
     322              :     {
     323            0 :         glDeleteTextures(1, &rsmcolortex);
     324            0 :         rsmcolortex = 0;
     325              :     }
     326            0 :     if(rsmnormaltex)
     327              :     {
     328            0 :         glDeleteTextures(1, &rsmnormaltex);
     329            0 :         rsmnormaltex = 0;
     330              :     }
     331            0 :     if(rsmfbo)
     332              :     {
     333            0 :         glDeleteFramebuffers(1, &rsmfbo);
     334            0 :         rsmfbo = 0;
     335              :     }
     336              : 
     337            0 :     clearrhshaders();
     338            0 : }
     339              : 
     340            0 : void viewrh()
     341              : {
     342            0 :     int w = std::min(hudw(), hudh())/2,
     343            0 :         h = (w*hudh())/hudw(),
     344            0 :         x = hudw()-w,
     345            0 :         y = hudh()-h;
     346            0 :     gle::colorf(1, 1, 1);
     347            0 :     if(debugrh < 0 && rhrect)
     348              :     {
     349            0 :         SETSHADER(hudrect);
     350            0 :         glBindTexture(GL_TEXTURE_RECTANGLE, rhtex[5]);
     351            0 :         float tw = (rhgrid+2*rhborder)*(rhgrid+2*rhborder),
     352            0 :               th = (rhgrid+2*rhborder)*rhsplits;
     353            0 :         gle::defvertex(2);
     354            0 :         gle::deftexcoord0(2);
     355            0 :         gle::begin(GL_TRIANGLE_STRIP);
     356            0 :         gle::attribf(x,   y);   gle::attribf(0,  0);
     357            0 :         gle::attribf(x+w, y);   gle::attribf(tw, 0);
     358            0 :         gle::attribf(x,   y+h); gle::attribf(0,  th);
     359            0 :         gle::attribf(x+w, y+h); gle::attribf(tw, th);
     360            0 :         gle::end();
     361            0 :     }
     362              :     else
     363              :     {
     364            0 :         SETSHADER(hud3d);
     365            0 :         glBindTexture(GL_TEXTURE_3D, rhtex[1]);
     366            0 :         float z = (std::max(debugrh, 1)-1+0.5f)/static_cast<float>((rhgrid+2*rhborder)*rhsplits);
     367            0 :         gle::defvertex(2);
     368            0 :         gle::deftexcoord0(3);
     369            0 :         gle::begin(GL_TRIANGLE_STRIP);
     370            0 :         gle::attribf(x,   y);   gle::attribf(0, 0, z);
     371            0 :         gle::attribf(x+w, y);   gle::attribf(1, 0, z);
     372            0 :         gle::attribf(x,   y+h); gle::attribf(0, 1, z);
     373            0 :         gle::attribf(x+w, y+h); gle::attribf(1, 1, z);
     374            0 :         gle::end();
     375              :     }
     376            0 : }
     377              : 
     378            1 : void clearradiancehintscache()
     379              : {
     380            1 :     rh.clearcache();
     381            1 :     std::memset(rhclearmasks, 0, sizeof(rhclearmasks));
     382            1 : }
     383              : 
     384            0 : bool useradiancehints()
     385              : {
     386            0 :     return !sunlight.iszero() && csm.getcsmproperty(cascadedshadowmap::ShadowMap) && gi && giscale && gidist;
     387              : }
     388              : 
     389              : //============================= radiance hints object ==========================//
     390              : 
     391            0 : void radiancehints::updatesplitdist()
     392              : {
     393            0 :     const float lambda = rhsplitweight,
     394            0 :                 nd = rhnearplane,
     395            0 :                 fd = rhfarplane,
     396            0 :                 ratio = fd/nd;
     397            0 :     splits[0].nearplane = nd;
     398            0 :     for(int i = 1; i < rhsplits; ++i)
     399              :     {
     400            0 :         const float si = i / static_cast<float>(rhsplits);
     401            0 :         splits[i].nearplane = lambda*(nd*std::pow(ratio, si)) + (1-lambda)*(nd + (fd - nd)*si);
     402            0 :         splits[i-1].farplane = splits[i].nearplane * 1.005f;
     403              :     }
     404            0 :     splits[rhsplits-1].farplane = fd;
     405            0 : }
     406              : 
     407            0 : void radiancehints::setup()
     408              : {
     409            0 :     updatesplitdist();
     410              : 
     411            0 :     for(int i = 0; i < rhsplits; ++i)
     412              :     {
     413            0 :         SplitInfo &split = splits[i];
     414              : 
     415            0 :         vec c;
     416            0 :         const float radius = calcfrustumboundsphere(split.nearplane, split.farplane, camera1->o, camdir(), c);
     417              : 
     418              :         // compute the projected bounding box of the sphere
     419            0 :         const float pradius = std::ceil(radius * rhpradiustweak),
     420            0 :                     step = (2*pradius) / rhgrid;
     421            0 :         vec offset = vec(c).sub(pradius).div(step);
     422            0 :         offset.x = std::floor(offset.x);
     423            0 :         offset.y = std::floor(offset.y);
     424            0 :         offset.z = std::floor(offset.z);
     425            0 :         split.cached = split.bounds == pradius ? split.center : vec(-1e16f, -1e16f, -1e16f);
     426            0 :         split.center = vec(offset).mul(step).add(pradius);
     427            0 :         split.bounds = pradius;
     428              : 
     429              :         // modify mvp with a scale and offset
     430              :         // now compute the update model view matrix for this split
     431            0 :         split.scale = vec(1/(step*(rhgrid+2*rhborder)), 1/(step*(rhgrid+2*rhborder)), 1/(step*(rhgrid+2*rhborder)*rhsplits));
     432            0 :         split.offset = vec(-(offset.x-rhborder)/(rhgrid+2*rhborder), -(offset.y-rhborder)/(rhgrid+2*rhborder), (i - (offset.z-rhborder)/(rhgrid+2*rhborder))/static_cast<float>(rhsplits));
     433              :     }
     434            0 : }
     435              : 
     436            0 : void radiancehints::bindparams() const
     437              : {
     438            0 :     float step = 2*splits[0].bounds/rhgrid;
     439            0 :     GLOBALPARAMF(rhnudge, rhnudge*step);
     440            0 :     static GlobalShaderParam rhtc("rhtc");
     441            0 :     vec4<float> *rhtcv = rhtc.reserve<vec4<float>>();
     442            0 :     for(int i = 0; i < rhsplits; ++i)
     443              :     {
     444            0 :         const SplitInfo &split = splits[i];
     445            0 :         rhtcv[i] = vec4<float>(vec(split.center).mul(-split.scale.x), split.scale.x);//split.bounds*(1 + rhborder*2*0.5f/rhgrid));
     446              :     }
     447            0 :     GLOBALPARAMF(rhbounds, 0.5f*(rhgrid + rhborder)/static_cast<float>(rhgrid + 2*rhborder));
     448            0 : }
     449              : 
     450            1 : void radiancehints::clearcache()
     451              : {
     452            5 :     for(SplitInfo &i : splits)
     453              :     {
     454            4 :         i.clearcache();
     455              :     }
     456            1 : }
     457              : 
     458            0 : bool radiancehints::allcached() const
     459              : {
     460            0 :     for(const SplitInfo &i : splits)
     461              :     {
     462            0 :         if(i.cached != i.center)
     463              :         {
     464            0 :             return false;
     465              :         }
     466              :     }
     467            0 :     return true;
     468              : }
     469              : 
     470            0 : void radiancehints::rotatedynlimits()
     471              : {
     472            0 :     prevdynmin = dynmin;
     473            0 :     prevdynmax = dynmax;
     474            0 : }
     475              : 
     476            0 : bool radiancehints::checkprevbounds()
     477              : {
     478            0 :     return prevdynmin.z < rh.prevdynmax.z;
     479              : }
     480              : 
     481            0 : static void bindslice(int sx, int sy, int sw, int sh, int i, int j)
     482              : {
     483            0 :     if(rhrect)
     484              :     {
     485            0 :         glViewport(sx, sy, sw, sh);
     486            0 :         glScissor(sx, sy, sw, sh);
     487              :     }
     488              :     else
     489              :     {
     490            0 :         glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_3D, rhtex[0], 0, i*sh + j);
     491            0 :         glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_3D, rhtex[1], 0, i*sh + j);
     492            0 :         glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_3D, rhtex[2], 0, i*sh + j);
     493            0 :         glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_3D, rhtex[3], 0, i*sh + j);
     494              :     }
     495            0 : }
     496              : 
     497            0 : void radiancehints::renderslices()
     498              : {
     499            0 :     int sw = rhgrid+2*rhborder,
     500            0 :         sh = rhgrid+2*rhborder;
     501            0 :     glBindFramebuffer(GL_FRAMEBUFFER, rhfbo);
     502            0 :     if(!rhrect)
     503              :     {
     504            0 :         glViewport(0, 0, sw, sh);
     505            0 :         if(rhcache)
     506              :         {
     507            0 :             for(size_t i = 0; i < rhtex.size()/2; ++i)
     508              :             {
     509            0 :                 std::swap(rhtex[i], rhtex[i+rhtex.size()/2]);
     510              :             }
     511              :             uint clearmasks[rhmaxsplits][(rhmaxgrid+2+31)/32];
     512            0 :             std::memcpy(clearmasks, rhclearmasks[0], sizeof(clearmasks));
     513            0 :             std::memcpy(rhclearmasks[0], rhclearmasks[1], sizeof(clearmasks));
     514            0 :             std::memcpy(rhclearmasks[1], clearmasks, sizeof(clearmasks));
     515              :         }
     516              :     }
     517              : 
     518            0 :     GLOBALPARAMF(rhatten, 1.0f/(gidist*gidist));
     519            0 :     GLOBALPARAMF(rsmspread, gidist*rsmspread*rsm.scale.x, gidist*rsmspread*rsm.scale.y);
     520            0 :     GLOBALPARAMF(rhaothreshold, splits[0].bounds/rhgrid);
     521            0 :     GLOBALPARAMF(rhaoatten, 1.0f/(gidist*rsmspread));
     522            0 :     GLOBALPARAMF(rhaoheight, gidist*rsmspread);
     523              : 
     524            0 :     matrix4 rsmtcmatrix;
     525            0 :     rsmtcmatrix.identity();
     526            0 :     rsmtcmatrix.settranslation(rsm.offset);
     527            0 :     rsmtcmatrix.setscale(rsm.scale);
     528            0 :     rsmtcmatrix.mul(rsm.model);
     529            0 :     GLOBALPARAM(rsmtcmatrix, rsmtcmatrix);
     530              : 
     531            0 :     matrix4 rsmworldmatrix;
     532            0 :     rsmworldmatrix.invert(rsmtcmatrix);
     533            0 :     GLOBALPARAM(rsmworldmatrix, rsmworldmatrix);
     534              : 
     535            0 :     glBindTexture(GL_TEXTURE_RECTANGLE, rsmdepthtex);
     536            0 :     glActiveTexture(GL_TEXTURE1);
     537            0 :     glBindTexture(GL_TEXTURE_RECTANGLE, rsmcolortex);
     538            0 :     glActiveTexture(GL_TEXTURE2);
     539            0 :     glBindTexture(GL_TEXTURE_RECTANGLE, rsmnormaltex);
     540            0 :     if(rhborder)
     541              :     {
     542            0 :         for(size_t i = 0; i < rhtex.size()/2; ++i)
     543              :         {
     544            0 :             glActiveTexture(GL_TEXTURE3 + i);
     545            0 :             glBindTexture(GL_TEXTURE_3D, rhtex[i]);
     546              :         }
     547              :     }
     548            0 :     if(rhcache)
     549              :     {
     550            0 :         for(size_t i = 0; i < rhtex.size()/2; ++i)
     551              :         {
     552            0 :             glActiveTexture(GL_TEXTURE7 + i);
     553            0 :             glBindTexture(GL_TEXTURE_3D, rhtex[rhrect ? i :  rhtex.size()/2+i]);
     554              :         }
     555              :     }
     556            0 :     glActiveTexture(GL_TEXTURE0);
     557            0 :     glClearColor(0.5f, 0.5f, 0.5f, 0);
     558            0 :     if(rhrect)
     559              :     {
     560            0 :         glEnable(GL_SCISSOR_TEST);
     561              :     }
     562            0 :     gle::defvertex(2);
     563            0 :     gle::deftexcoord0(3);
     564              : 
     565            0 :     bool prevcached = true;
     566            0 :     int cx = -1,
     567            0 :         cy = -1;
     568            0 :     for(int i = rhsplits; --i >= 0;) //reverse iterate through rhsplits
     569              :     {
     570            0 :         SplitInfo &split = splits[i];
     571            0 :         float cellradius = split.bounds/rhgrid,
     572            0 :               step       = 2*cellradius,
     573            0 :               nudge      = rhnudge*2*splits[0].bounds/rhgrid + rhworldbias*step;
     574            0 :         vec cmin, cmax,
     575            0 :             dmin( 1e16f,  1e16f,  1e16f),
     576            0 :             dmax(-1e16f, -1e16f, -1e16f),
     577            0 :             bmin( 1e16f,  1e16f,  1e16f),
     578            0 :             bmax(-1e16f, -1e16f, -1e16f);
     579            0 :         for(int k = 0; k < 3; ++k)
     580              :         {
     581            0 :             cmin[k] = std::floor((worldmin[k] - nudge - (split.center[k] - split.bounds))/step)*step + split.center[k] - split.bounds;
     582            0 :             cmax[k] = std::ceil((worldmax[k] + nudge - (split.center[k] - split.bounds))/step)*step + split.center[k] - split.bounds;
     583              :         }
     584            0 :         if(prevdynmin.z < prevdynmax.z)
     585              :         {
     586            0 :             for(int k = 0; k < 3; ++k)
     587              :             {
     588            0 :                 dmin[k] = std::min(dmin[k], static_cast<float>(std::floor((prevdynmin[k] - gidist - cellradius - (split.center[k] - split.bounds))/step)*step + split.center[k] - split.bounds));
     589            0 :                 dmax[k] = std::max(dmax[k], static_cast<float>(std::ceil((prevdynmax[k] + gidist + cellradius - (split.center[k] - split.bounds))/step)*step + split.center[k] - split.bounds));
     590              :             }
     591              :         }
     592            0 :         if(dynmin.z < dynmax.z)
     593              :         {
     594            0 :             for(int k = 0; k < 3; ++k)
     595              :             {
     596            0 :                 dmin[k] = std::min(dmin[k], static_cast<float>(std::floor((dynmin[k] - gidist - cellradius - (split.center[k] - split.bounds))/step)*step + split.center[k] - split.bounds));
     597            0 :                 dmax[k] = std::max(dmax[k], static_cast<float>(std::ceil((dynmax[k] + gidist + cellradius - (split.center[k] - split.bounds))/step)*step + split.center[k] - split.bounds));
     598              :             }
     599              :         }
     600            0 :         if((rhrect || !rhcache || hasCI) && split.cached == split.center && (!rhborder || prevcached) && !rhforce &&
     601            0 :            (dmin.x > split.center.x + split.bounds || dmax.x < split.center.x - split.bounds ||
     602            0 :             dmin.y > split.center.y + split.bounds || dmax.y < split.center.y - split.bounds ||
     603            0 :             dmin.z > split.center.z + split.bounds || dmax.z < split.center.z - split.bounds))
     604              :         {
     605            0 :             if(rhrect || !rhcache || split.copied)
     606              :             {
     607            0 :                 continue;
     608              :             }
     609            0 :             split.copied = true;
     610            0 :             for(size_t k = 0; k <  rhtex.size()/2; ++k)
     611              :             {
     612            0 :                 glCopyImageSubData_(rhtex[rhtex.size()/2+k], GL_TEXTURE_3D, 0, 0, 0, i*sh, rhtex[k], GL_TEXTURE_3D, 0, 0, 0, i*sh, sw, sh, sh);
     613              :             }
     614            0 :             continue;
     615            0 :         }
     616              : 
     617            0 :         prevcached = false;
     618            0 :         split.copied = false;
     619              : 
     620            0 :         GLOBALPARAM(rhcenter, split.center);
     621            0 :         GLOBALPARAMF(rhbounds, split.bounds);
     622            0 :         GLOBALPARAMF(rhspread, cellradius);
     623              : 
     624            0 :         if(rhborder && i + 1 < rhsplits)
     625              :         {
     626            0 :             GLOBALPARAMF(bordercenter, 0.5f, 0.5f, static_cast<float>(i+1 + 0.5f)/rhsplits);
     627            0 :             GLOBALPARAMF(borderrange, 0.5f - 0.5f/(rhgrid+2), 0.5f - 0.5f/(rhgrid+2), (0.5f - 0.5f/(rhgrid+2))/rhsplits);
     628            0 :             GLOBALPARAMF(borderscale, rhgrid+2, rhgrid+2, (rhgrid+2)*rhsplits);
     629              : 
     630            0 :             SplitInfo &next = splits[i+1];
     631            0 :             for(int k = 0; k < 3; ++k)
     632              :             {
     633            0 :                 bmin[k] = std::floor((std::max(static_cast<float>(worldmin[k] - nudge), next.center[k] - next.bounds) - (split.center[k] - split.bounds))/step)*step + split.center[k] - split.bounds;
     634            0 :                 bmax[k] = std::ceil((std::min(static_cast<float>(worldmax[k] + nudge), next.center[k] + next.bounds) - (split.center[k] - split.bounds))/step)*step + split.center[k] - split.bounds;
     635              :             }
     636              :         }
     637              : 
     638              :         std::array<uint, (rhmaxgrid+2+31)/32> clearmasks;
     639            0 :         std::memset(clearmasks.data(), 0xFF, clearmasks.size()*sizeof(uint));
     640              : 
     641            0 :         int sy = rhrect ? i*sh : 0;
     642            0 :         for(int j = sh; --j >= 0;) //note reverse iteration
     643              :         {
     644            0 :             int sx = rhrect ? j*sw : 0;
     645              : 
     646            0 :             float x1  = split.center.x - split.bounds,
     647            0 :                   x2  = split.center.x + split.bounds,
     648            0 :                   y1  = split.center.y - split.bounds,
     649            0 :                   y2  = split.center.y + split.bounds,
     650            0 :                   z   = split.center.z - split.bounds + (j-rhborder+0.5f)*step,
     651            0 :                   vx1 = -1 + rhborder*2.0f/(rhgrid+2),
     652            0 :                   vx2 = 1 - rhborder*2.0f/(rhgrid+2),
     653            0 :                   vy1 = -1 + rhborder*2.0f/(rhgrid+2),
     654            0 :                   vy2 = 1 - rhborder*2.0f/(rhgrid+2),
     655            0 :                   tx1 = x1,
     656            0 :                   tx2 = x2,
     657            0 :                   ty1 = y1,
     658            0 :                   ty2 = y2;
     659            0 :             bool clipped = false;
     660              : 
     661            0 :             if(rhborder && i + 1 < rhsplits)
     662              :             {
     663            0 :                 const SplitInfo &next = splits[i+1];
     664            0 :                 float bx1  = x1-step,
     665            0 :                       bx2  = x2+step,
     666            0 :                       by1  = y1-step,
     667            0 :                       by2  = y2+step,
     668            0 :                       bz   = z,
     669            0 :                       bvx1 = -1,
     670            0 :                       bvx2 = 1,
     671            0 :                       bvy1 = -1,
     672            0 :                       bvy2 = 1,
     673            0 :                       btx1 = bx1,
     674            0 :                       btx2 = bx2,
     675            0 :                       bty1 = by1,
     676            0 :                       bty2 = by2;
     677              : 
     678            0 :                 if(rhclipgrid)
     679              :                 {
     680            0 :                     if(bz < bmin.z || bz > bmax.z)
     681              :                     {
     682            0 :                         goto noborder;
     683              :                     }
     684            0 :                     if(bx1 < bmin.x || bx2 > bmax.x || by1 < bmin.y || by2 > bmax.y)
     685              :                     {
     686            0 :                         btx1 = std::max(bx1, bmin.x);
     687            0 :                         btx2 = std::min(bx2, bmax.x);
     688            0 :                         bty1 = std::max(by1, bmin.y);
     689            0 :                         bty2 = std::min(by2, bmax.y);
     690            0 :                         if(btx1 > tx2 || bty1 > bty2)
     691              :                         {
     692            0 :                             goto noborder;
     693              :                         }
     694            0 :                         bvx1 += 2*(btx1 - bx1)/(bx2 - bx1);
     695            0 :                         bvx2 += 2*(btx2 - bx2)/(bx2 - bx1);
     696            0 :                         bvy1 += 2*(bty1 - by1)/(by2 - by1);
     697            0 :                         bvy2 += 2*(bty2 - by2)/(by2 - by1);
     698            0 :                         clipped = true;
     699              :                     }
     700              :                 }
     701            0 :                 btx1 = btx1*next.scale.x + next.offset.x;
     702            0 :                 btx2 = btx2*next.scale.x + next.offset.x;
     703            0 :                 bty1 = bty1*next.scale.y + next.offset.y;
     704            0 :                 bty2 = bty2*next.scale.y + next.offset.y;
     705            0 :                 bz = bz*next.scale.z + next.offset.z;
     706            0 :                 bindslice(sx, sy, sw, sh, i, j);
     707            0 :                 if(clipped)
     708              :                 {
     709            0 :                     glClear(GL_COLOR_BUFFER_BIT);
     710              :                 }
     711            0 :                 SETSHADER(radiancehintsborder);
     712            0 :                 rhquad(bvx1, bvy1, bvx2, bvy2, btx1, bty1, btx2, bty2, bz);
     713            0 :                 clearmasks[j/32] &= ~(1 << (j%32));
     714              :             }
     715              : 
     716            0 :         noborder:
     717            0 :             if(j < rhborder || j >= rhgrid + rhborder)
     718              :             {
     719            0 :             skipped:
     720            0 :                 if(clearmasks[j/32] & (1 << (j%32)) && (!rhrect || cx < 0) && !(rhclearmasks[0][i][j/32] & (1 << (j%32))))
     721              :                 {
     722            0 :                     bindslice(sx, sy, sw, sh, i, j);
     723            0 :                     glClear(GL_COLOR_BUFFER_BIT);
     724            0 :                     cx = sx;
     725            0 :                     cy = sy;
     726              :                 }
     727            0 :                 continue;
     728              :             }
     729              : 
     730            0 :             if(rhclipgrid)
     731              :             {
     732            0 :                 if(z < cmin.z || z > cmax.z)
     733              :                 {
     734            0 :                     goto skipped;
     735              :                 }
     736            0 :                 if(x1 < cmin.x || x2 > cmax.x || y1 < cmin.y || y2 > cmax.y)
     737              :                 {
     738            0 :                     tx1 = std::max(x1, cmin.x);
     739            0 :                     tx2 = std::min(x2, cmax.x);
     740            0 :                     ty1 = std::max(y1, cmin.y);
     741            0 :                     ty2 = std::min(y2, cmax.y);
     742            0 :                     if(tx1 > tx2 || ty1 > ty2)
     743              :                     {
     744            0 :                         goto skipped;
     745              :                     }
     746            0 :                     vx1 += 2*rhgrid/static_cast<float>(sw)*(tx1 - x1)/(x2 - x1);
     747            0 :                     vx2 += 2*rhgrid/static_cast<float>(sw)*(tx2 - x2)/(x2 - x1);
     748            0 :                     vy1 += 2*rhgrid/static_cast<float>(sh)*(ty1 - y1)/(y2 - y1);
     749            0 :                     vy2 += 2*rhgrid/static_cast<float>(sh)*(ty2 - y2)/(y2 - y1);
     750            0 :                     clipped = true;
     751              :                 }
     752              :             }
     753            0 :             if(clearmasks[j/32] & (1 << (j%32)))
     754              :             {
     755            0 :                 bindslice(sx, sy, sw, sh, i, j);
     756            0 :                 if(clipped || (rhborder && i + 1 >= rhsplits))
     757              :                 {
     758            0 :                     glClear(GL_COLOR_BUFFER_BIT);
     759              :                 }
     760            0 :                 clearmasks[j/32] &= ~(1 << (j%32));
     761              :             }
     762              : 
     763            0 :             if(rhcache && z > split.cached.z - split.bounds && z < split.cached.z + split.bounds)
     764              :             {
     765            0 :                 float px1 = std::max(tx1, split.cached.x - split.bounds),
     766            0 :                       px2 = std::min(tx2, split.cached.x + split.bounds),
     767            0 :                       py1 = std::max(ty1, split.cached.y - split.bounds),
     768            0 :                       py2 = std::min(ty2, split.cached.y + split.bounds);
     769            0 :                 if(px1 < px2 && py1 < py2)
     770              :                 {
     771            0 :                     float pvx1 = -1 + rhborder*2.0f/(rhgrid+2) + 2*rhgrid/static_cast<float>(sw)*(px1 - x1)/(x2 - x1),
     772            0 :                           pvx2 =  1 - rhborder*2.0f/(rhgrid+2) + 2*rhgrid/static_cast<float>(sw)*(px2 - x2)/(x2 - x1),
     773            0 :                           pvy1 = -1 + rhborder*2.0f/(rhgrid+2) + 2*rhgrid/static_cast<float>(sh)*(py1 - y1)/(y2 - y1),
     774            0 :                           pvy2 =  1 - rhborder*2.0f/(rhgrid+2) + 2*rhgrid/static_cast<float>(sh)*(py2 - y2)/(y2 - y1),
     775            0 :                           ptx1 = (px1 + split.center.x - split.cached.x)*split.scale.x + split.offset.x,
     776            0 :                           ptx2 = (px2 + split.center.x - split.cached.x)*split.scale.x + split.offset.x,
     777            0 :                           pty1 = (py1 + split.center.y - split.cached.y)*split.scale.y + split.offset.y,
     778            0 :                           pty2 = (py2 + split.center.y - split.cached.y)*split.scale.y + split.offset.y,
     779            0 :                           pz = (z + split.center.z - split.cached.z)*split.scale.z + split.offset.z;
     780            0 :                     if(px1 != tx1 || px2 != tx2 || py1 != ty1 || py2 != ty2)
     781              :                     {
     782            0 :                         radiancehintsshader->set();
     783            0 :                         rhquad(pvx1, pvy1, pvx2, pvy2, px1, py1, px2, py2, z,
     784              :                                 vx1,  vy1,  vx2,  vy2, tx1, ty1, tx2, ty2, z);
     785              :                     }
     786            0 :                     if(z > dmin.z && z < dmax.z)
     787              :                     {
     788            0 :                         float dx1 = std::max(px1, dmin.x),
     789            0 :                               dx2 = std::min(px2, dmax.x),
     790            0 :                               dy1 = std::max(py1, dmin.y),
     791            0 :                               dy2 = std::min(py2, dmax.y);
     792            0 :                         if(dx1 < dx2 && dy1 < dy2)
     793              :                         {
     794            0 :                             float dvx1 = -1 + rhborder*2.0f/(rhgrid+2) + 2*rhgrid/static_cast<float>(sw)*(dx1 - x1)/(x2 - x1),
     795            0 :                                   dvx2 =  1 - rhborder*2.0f/(rhgrid+2) + 2*rhgrid/static_cast<float>(sw)*(dx2 - x2)/(x2 - x1),
     796            0 :                                   dvy1 = -1 + rhborder*2.0f/(rhgrid+2) + 2*rhgrid/static_cast<float>(sh)*(dy1 - y1)/(y2 - y1),
     797            0 :                                   dvy2 =  1 - rhborder*2.0f/(rhgrid+2) + 2*rhgrid/static_cast<float>(sh)*(dy2 - y2)/(y2 - y1),
     798            0 :                                   dtx1 = (dx1 + split.center.x - split.cached.x)*split.scale.x + split.offset.x,
     799            0 :                                   dtx2 = (dx2 + split.center.x - split.cached.x)*split.scale.x + split.offset.x,
     800            0 :                                   dty1 = (dy1 + split.center.y - split.cached.y)*split.scale.y + split.offset.y,
     801            0 :                                   dty2 = (dy2 + split.center.y - split.cached.y)*split.scale.y + split.offset.y,
     802            0 :                                   dz = (z + split.center.z - split.cached.z)*split.scale.z + split.offset.z;
     803            0 :                             if(dx1 != px1 || dx2 != px2 || dy1 != py1 || dy2 != py2)
     804              :                             {
     805            0 :                                 SETSHADER(radiancehintscached);
     806            0 :                                 rhquad(dvx1, dvy1, dvx2, dvy2, dtx1, dty1, dtx2, dty2, dz,
     807              :                                        pvx1, pvy1, pvx2, pvy2, ptx1, pty1, ptx2, pty2, pz);
     808              :                             }
     809            0 :                             radiancehintsshader->set();
     810            0 :                             rhquad(dvx1, dvy1, dvx2, dvy2, dx1, dy1, dx2, dy2, z);
     811            0 :                             goto maskslice;
     812              :                         }
     813              :                     }
     814            0 :                     SETSHADER(radiancehintscached);
     815            0 :                     rhquad(pvx1, pvy1, pvx2, pvy2, ptx1, pty1, ptx2, pty2, pz);
     816            0 :                     goto maskslice;
     817              :                 }
     818              :             }
     819            0 :             radiancehintsshader->set();
     820            0 :             rhquad(vx1, vy1, vx2, vy2, tx1, ty1, tx2, ty2, z);
     821            0 :         maskslice:
     822            0 :             if(i)
     823              :             {
     824            0 :                 continue;
     825              :             }
     826            0 :             if(gle::attribbuf.empty())
     827              :             {
     828            0 :                 continue;
     829              :             }
     830            0 :             SETSHADER(radiancehintsdisable);
     831            0 :             if(rhborder)
     832              :             {
     833            0 :                 glScissor(sx + rhborder, sy + rhborder, sw - 2*rhborder, sh - 2*rhborder);
     834            0 :                 if(!rhrect)
     835              :                 {
     836            0 :                     glEnable(GL_SCISSOR_TEST);
     837              :                 }
     838              :             }
     839            0 :             gle::defvertex(2);
     840            0 :             gle::begin(GL_TRIANGLES);
     841            0 :             gle::end();
     842            0 :             if(rhborder && !rhrect)
     843              :             {
     844            0 :                 glDisable(GL_SCISSOR_TEST);
     845              :             }
     846            0 :             gle::defvertex(2);
     847            0 :             gle::deftexcoord0(3);
     848              :         }
     849            0 :         if(rhrect)
     850              :         {
     851            0 :             for(int k = 0; k < 4; ++k)
     852              :             {
     853            0 :                 glReadBuffer(GL_COLOR_ATTACHMENT0+k);
     854            0 :                 glBindTexture(GL_TEXTURE_3D, rhtex[k]);
     855            0 :                 for(int j = 0; j < sh; ++j)
     856              :                 {
     857            0 :                     if(clearmasks[j/32] & (1 << (j%32)))
     858              :                     {
     859            0 :                         if(!(rhclearmasks[0][i][j/32] & (1 << (j%32))))
     860              :                         {
     861            0 :                             glCopyTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, sy+j, cx, cy, sw, sh);
     862              :                         }
     863            0 :                         continue;
     864              :                     }
     865            0 :                     glCopyTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, sy+j, j*sw, sy, sw, sh);
     866              :                 }
     867              :             }
     868              :         }
     869            0 :         std::memcpy(rhclearmasks[0][i], clearmasks.data(), clearmasks.size()*sizeof(uint));
     870              :     }
     871            0 :     if(rhrect)
     872              :     {
     873            0 :         glDisable(GL_SCISSOR_TEST);
     874              :     }
     875            0 : }
     876              : 
     877            0 : void GBuffer::renderradiancehints() const
     878              : {
     879            0 :     if(rhinoq && !inoq && shouldworkinoq())
     880              :     {
     881            0 :         return;
     882              :     }
     883            0 :     if(!useradiancehints())
     884              :     {
     885            0 :         return;
     886              :     }
     887            0 :     timer *rhcputimer = begintimer("radiance hints", false),
     888            0 :           *rhtimer = begintimer("radiance hints"); //implicit true
     889              : 
     890            0 :     rh.setup();
     891            0 :     rsm.setup();
     892              : 
     893            0 :     shadowmapping = ShadowMap_Reflect;
     894            0 :     shadowside = 0;
     895            0 :     shadoworigin = vec(0, 0, 0);
     896            0 :     shadowdir = rsm.lightview;
     897            0 :     shadowbias = rsm.lightview.project_bb(worldmin, worldmax);
     898            0 :     shadowradius = std::fabs(rsm.lightview.project_bb(worldmax, worldmin));
     899              : 
     900            0 :     findshadowvas();
     901            0 :     findshadowmms();
     902              : 
     903            0 :     batching::shadowmaskbatchedmodels(false);
     904            0 :     batchshadowmapmodels();
     905              : 
     906            0 :     rh.rotatedynlimits();
     907            0 :     rh.dynmin = vec(1e16f, 1e16f, 1e16f);
     908            0 :     rh.dynmax = vec(-1e16f, -1e16f, -1e16f);
     909            0 :     if(rhdyntex)
     910              :     {
     911            0 :         dynamicshadowvabounds(1<<shadowside, rh.dynmin, rh.dynmax);
     912              :     }
     913            0 :     if(rhdynmm)
     914              :     {
     915            0 :         batching::batcheddynamicmodelbounds(1<<shadowside, rh.dynmin, rh.dynmax);
     916              :     }
     917            0 :     if(rhforce || rh.checkprevbounds() || rh.dynmin.z < rh.dynmax.z || !rh.allcached())
     918              :     {
     919            0 :         if(inoq)
     920              :         {
     921            0 :             glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
     922            0 :             glDepthMask(GL_TRUE);
     923              :         }
     924            0 :         glBindFramebuffer(GL_FRAMEBUFFER, rsmfbo);
     925            0 :         shadowmatrix.mul(rsm.proj, rsm.model);
     926            0 :         GLOBALPARAM(rsmmatrix, shadowmatrix);
     927            0 :         GLOBALPARAMF(rsmdir, -rsm.lightview.x, -rsm.lightview.y, -rsm.lightview.z);
     928              : 
     929            0 :         glViewport(0, 0, rsmsize, rsmsize);
     930            0 :         glClearColor(0, 0, 0, 0);
     931            0 :         glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
     932              : 
     933            0 :         renderrsmgeom(rhdyntex!=0);
     934            0 :         batching::rendershadowmodelbatches(rhdynmm!=0);
     935            0 :         rh.renderslices();
     936            0 :         if(inoq)
     937              :         {
     938            0 :             glBindFramebuffer(GL_FRAMEBUFFER, msaasamples ? msfbo : gfbo);
     939            0 :             glViewport(0, 0, vieww, viewh);
     940              : 
     941            0 :             glFlush();
     942              :         }
     943              :     }
     944              : 
     945            0 :     batching::clearbatchedmapmodels();
     946            0 :     shadowmapping = 0;
     947            0 :     endtimer(rhtimer);
     948            0 :     endtimer(rhcputimer);
     949              : }
     950              : 
     951              : // ============================ reflective shadow map =========================//
     952              : //the reflective shadow map caches the terrain albedo for use by the radiance
     953              : //hints algorithm: it needs to know how bright the surfaces the sun is shining on
     954            0 : void reflectiveshadowmap::setup()
     955              : {
     956            0 :     getmodelmatrix();
     957            0 :     getprojmatrix();
     958            0 :     gencullplanes();
     959            0 : }
     960              : 
     961              : // sets the reflectiveshadowmap's model to the global viewmatrix, then points it
     962              : // to be oriented like the sun
     963            0 : void reflectiveshadowmap::getmodelmatrix()
     964              : {
     965            0 :     model = viewmatrix;                             //copy global view matrix
     966            0 :     model.rotate_around_x(sunlightpitch/RAD);       //orient camera in same yaw as sunlight
     967            0 :     model.rotate_around_z((180-sunlightyaw)/RAD);   //orient camera in same pitch as sunlight
     968            0 : }
     969              : 
     970            0 : void reflectiveshadowmap::getprojmatrix()
     971              : {
     972            0 :     lightview = vec(sunlightdir).neg();
     973              :     // find z extent
     974            0 :     float minz = lightview.project_bb(worldmin, worldmax),
     975            0 :           maxz = lightview.project_bb(worldmax, worldmin),
     976            0 :           zmargin = std::max((maxz - minz)*rsmdepthmargin, 0.5f*(rsmdepthrange - (maxz - minz)));
     977            0 :     minz -= zmargin;
     978            0 :     maxz += zmargin;
     979            0 :     vec c;
     980            0 :     const float radius = calcfrustumboundsphere(rhnearplane, rhfarplane, camera1->o, camdir(), c);
     981              :     // compute the projected bounding box of the sphere
     982            0 :     vec tc;
     983            0 :     model.transform(c, tc);
     984            0 :     const float pradius = std::ceil((radius + gidist) * rsmpradiustweak),
     985            0 :                 step = (2*pradius) / rsmsize;
     986            0 :     vec2 tcoff = vec2(tc).sub(pradius).div(step);
     987            0 :     tcoff.x = std::floor(tcoff.x);
     988            0 :     tcoff.y = std::floor(tcoff.y);
     989            0 :     center = vec(vec2(tcoff).mul(step).add(pradius), -0.5f*(minz + maxz));
     990            0 :     bounds = vec(pradius, pradius, 0.5f*(maxz - minz));
     991              : 
     992            0 :     scale = vec(1/step, 1/step, -1/(maxz - minz));
     993            0 :     offset = vec(-tcoff.x, -tcoff.y, -minz/(maxz - minz));
     994              : 
     995            0 :     proj.identity();
     996            0 :     proj.settranslation(2*offset.x/rsmsize - 1, 2*offset.y/rsmsize - 1, 2*offset.z - 1);
     997            0 :     proj.setscale(2*scale.x/rsmsize, 2*scale.y/rsmsize, 2*scale.z);
     998            0 : }
     999              : 
    1000              : //sets the culling plane objects' location within the reflectiveshadowmap object
    1001            0 : void reflectiveshadowmap::gencullplanes()
    1002              : {
    1003            0 :     matrix4 mvp;
    1004            0 :     mvp.mul(proj, model);
    1005            0 :     vec4<float> px = mvp.rowx(),
    1006            0 :                 py = mvp.rowy(),
    1007            0 :                 pw = mvp.roww();
    1008            0 :     cull[0] = plane(vec4<float>(pw).add(px)).normalize(); // left plane
    1009            0 :     cull[1] = plane(vec4<float>(pw).sub(px)).normalize(); // right plane
    1010            0 :     cull[2] = plane(vec4<float>(pw).add(py)).normalize(); // bottom plane
    1011            0 :     cull[3] = plane(vec4<float>(pw).sub(py)).normalize(); // top plane
    1012            0 : }
    1013              : 
    1014              : //=========================== end reflective shadow map =======================//
        

Generated by: LCOV version 2.0-1