LCOV - code coverage report
Current view: top level - engine/render - radiancehints.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 1.3 % 599 8
Test Date: 2026-06-16 06:16:16 Functions: 4.3 % 47 2

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

Generated by: LCOV version 2.0-1