LCOV - code coverage report
Current view: top level - engine/render - renderva.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 14 1955 0.7 %
Date: 2025-01-07 07:51:37 Functions: 4 126 3.2 %

          Line data    Source code
       1             : // renderva.cpp: handles the occlusion and rendering of vertex arrays
       2             : 
       3             : #include "../libprimis-headers/cube.h"
       4             : #include "../../shared/geomexts.h"
       5             : #include "../../shared/glemu.h"
       6             : #include "../../shared/glexts.h"
       7             : 
       8             : #include <memory>
       9             : #include <optional>
      10             : 
      11             : #include "csm.h"
      12             : #include "grass.h"
      13             : #include "octarender.h"
      14             : #include "radiancehints.h"
      15             : #include "rendergl.h"
      16             : #include "renderlights.h"
      17             : #include "rendermodel.h"
      18             : #include "renderva.h"
      19             : #include "renderwindow.h"
      20             : #include "rendersky.h"
      21             : #include "shaderparam.h"
      22             : #include "shader.h"
      23             : #include "texture.h"
      24             : 
      25             : #include "interface/control.h"
      26             : 
      27             : #include "world/entities.h"
      28             : #include "world/light.h"
      29             : #include "world/octaedit.h"
      30             : #include "world/octaworld.h"
      31             : #include "world/raycube.h"
      32             : #include "world/bih.h"
      33             : #include "world/world.h"
      34             : 
      35             : 
      36             : #include "model/model.h"
      37             : 
      38             : VAR(oqfrags, 0, 8, 64); //occlusion query fragments
      39           0 : CVARP(outlinecolor, 0); //color of edit mode outlines
      40             : 
      41             : float shadowradius = 0,
      42             :       shadowbias = 0;
      43             : size_t shadowside = 0;
      44             : int shadowspot = 0;
      45             : vec shadoworigin(0, 0, 0),
      46             :     shadowdir(0, 0, 0);
      47             : 
      48             : vtxarray *visibleva = nullptr;
      49             : vfc view;
      50             : 
      51             : int deferquery = 0;
      52             : 
      53             : struct shadowmesh
      54             : {
      55             :     vec origin;
      56             :     float radius;
      57             :     vec spotloc;
      58             :     int spotangle;
      59             :     int type;
      60             :     std::array<int, 6> draws;
      61             : };
      62             : 
      63             : Occluder occlusionengine;
      64             : 
      65             : /* internally relevant functionality */
      66             : ///////////////////////////////////////
      67             : 
      68             : namespace
      69             : {
      70           0 :     void drawtris(GLsizei numindices, const GLvoid *indices, GLuint minvert, GLuint maxvert)
      71             :     {
      72           0 :         glDrawRangeElements(GL_TRIANGLES, minvert, maxvert, numindices, GL_UNSIGNED_SHORT, indices);
      73           0 :         glde++;
      74           0 :     }
      75             : 
      76           0 :     void drawvatris(const vtxarray &va, GLsizei numindices, int offset)
      77             :     {
      78           0 :         drawtris(numindices, (ushort *)0 + va.eoffset + offset, va.minvert, va.maxvert);
      79           0 :     }
      80             : 
      81           0 :     void drawvaskytris(const vtxarray &va)
      82             :     {
      83           0 :         drawtris(va.sky, (ushort *)0 + va.skyoffset, va.minvert, va.maxvert);
      84           0 :     }
      85             : 
      86             :     ///////// view frustrum culling ///////////////////////
      87             : 
      88           0 :     float vadist(const vtxarray &va, const vec &p)
      89             :     {
      90           0 :         return p.dist_to_bb(va.bbmin, va.bbmax);
      91             :     }
      92             : 
      93             :     constexpr int vasortsize = 64;
      94             : 
      95           0 :     void addvisibleva(vtxarray *va, std::array<vtxarray *, vasortsize> &vasort)
      96             :     {
      97           0 :         float dist = vadist(*va, camera1->o);
      98           0 :         va->distance = static_cast<int>(dist); /*cv.dist(camera1->o) - va->size*SQRT3/2*/
      99             : 
     100           0 :         int hash = std::clamp(static_cast<int>(dist*vasortsize/rootworld.mapsize()), 0, vasortsize-1);
     101           0 :         vtxarray **prev = &vasort[hash],
     102           0 :                   *cur = vasort[hash];
     103             : 
     104           0 :         while(cur && va->distance >= cur->distance)
     105             :         {
     106           0 :             prev = &cur->next;
     107           0 :             cur = cur->next;
     108             :         }
     109             : 
     110           0 :         va->next = cur;
     111           0 :         *prev = va;
     112           0 :     }
     113             : 
     114           0 :     void sortvisiblevas(const std::array<vtxarray *, vasortsize> &vasort)
     115             :     {
     116           0 :         visibleva = nullptr;
     117           0 :         vtxarray **last = &visibleva;
     118           0 :         for(vtxarray *i : vasort)
     119             :         {
     120           0 :             if(i)
     121             :             {
     122           0 :                 vtxarray *va = i;
     123           0 :                 *last = va;
     124           0 :                 while(va->next)
     125             :                 {
     126           0 :                     va = va->next;
     127             :                 }
     128           0 :                 last = &va->next;
     129             :             }
     130             :         }
     131           0 :     }
     132             : 
     133             :     template<bool fullvis, bool resetocclude>
     134           0 :     void findvisiblevas(std::vector<vtxarray *> &vas, std::array<vtxarray *, vasortsize> &vasort)
     135             :     {
     136           0 :         for(uint i = 0; i < vas.size(); i++)
     137             :         {
     138           0 :             vtxarray &v = *vas[i];
     139           0 :             int prevvfc = v.curvfc;
     140           0 :             v.curvfc = fullvis ? ViewFrustumCull_FullyVisible : view.isvisiblecube(v.o, v.size);
     141           0 :             if(v.curvfc != ViewFrustumCull_NotVisible)
     142             :             {
     143           0 :                 bool resetchildren = prevvfc >= ViewFrustumCull_NotVisible || resetocclude;
     144           0 :                 if(resetchildren)
     145             :                 {
     146           0 :                     v.occluded = !v.texs ? Occlude_Geom : Occlude_Nothing;
     147           0 :                     v.query = nullptr;
     148             :                 }
     149           0 :                 addvisibleva(&v, vasort);
     150           0 :                 if(v.children.size())
     151             :                 {
     152           0 :                     if(fullvis || v.curvfc == ViewFrustumCull_FullyVisible)
     153             :                     {
     154           0 :                         if(resetchildren)
     155             :                         {
     156           0 :                             findvisiblevas<true, true>(v.children, vasort);
     157             :                         }
     158             :                         else
     159             :                         {
     160           0 :                             findvisiblevas<true, false>(v.children, vasort);
     161             :                         }
     162             :                     }
     163           0 :                     else if(resetchildren)
     164             :                     {
     165           0 :                         findvisiblevas<false, true>(v.children, vasort);
     166             :                     }
     167             :                     else
     168             :                     {
     169           0 :                         findvisiblevas<false, false>(v.children, vasort);
     170             :                     }
     171             :                 }
     172             :             }
     173             :         }
     174           0 :     }
     175             : 
     176           0 :     void findvisiblevas()
     177             :     {
     178             :         std::array<vtxarray *, vasortsize> vasort;
     179           0 :         vasort.fill(nullptr);
     180           0 :         findvisiblevas<false, false>(varoot, vasort);
     181           0 :         sortvisiblevas(vasort);
     182           0 :     }
     183             : 
     184             :     ///////// occlusion queries /////////////
     185             : 
     186           0 :     VARF(oqany, 0, 0, 2, occlusionengine.clearqueries()); //occlusion query settings: 0: GL_SAMPLES_PASSED, 1: GL_ANY_SAMPLES_PASSED, 2: GL_ANY_SAMPLES_PASSED_CONSERVATIVE
     187             :     VAR(oqwait, 0, 1, 1);
     188             : 
     189           0 :     GLenum querytarget()
     190             :     {
     191           0 :         return oqany ? (oqany > 1 && hasES3 ? GL_ANY_SAMPLES_PASSED_CONSERVATIVE : GL_ANY_SAMPLES_PASSED) : GL_SAMPLES_PASSED;
     192             :     }
     193             : 
     194             :     GLuint bbvbo = 0,
     195             :            bbebo = 0;
     196             : 
     197           0 :     void setupbb()
     198             :     {
     199           0 :         if(!bbvbo)
     200             :         {
     201           0 :             glGenBuffers(1, &bbvbo);
     202           0 :             gle::bindvbo(bbvbo);
     203           0 :             vec verts[8];
     204           0 :             for(int i = 0; i < 8; ++i)
     205             :             {
     206           0 :                 verts[i] = vec(i&1, (i>>1)&1, (i>>2)&1);
     207             :             }
     208           0 :             glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
     209           0 :             gle::clearvbo();
     210             :         }
     211           0 :         if(!bbebo)
     212             :         {
     213           0 :             glGenBuffers(1, &bbebo);
     214           0 :             gle::bindebo(bbebo);
     215             :             GLushort tris[3*2*6];
     216             :             //======================================== GENFACEVERT GENFACEORIENT
     217             :             #define GENFACEORIENT(orient, v0, v1, v2, v3) do { \
     218             :                 int offset = orient*3*2; \
     219             :                 tris[offset + 0] = v0; \
     220             :                 tris[offset + 1] = v1; \
     221             :                 tris[offset + 2] = v2; \
     222             :                 tris[offset + 3] = v0; \
     223             :                 tris[offset + 4] = v2; \
     224             :                 tris[offset + 5] = v3; \
     225             :             } while(0);
     226             :             #define GENFACEVERT(orient, vert, ox,oy,oz, rx,ry,rz) (ox | oy | oz)
     227           0 :             GENFACEVERTS(0, 1, 0, 2, 0, 4, , , , , , )
     228             :             #undef GENFACEORIENT
     229             :             #undef GENFACEVERT
     230             :             //==================================================================
     231           0 :             glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(tris), tris, GL_STATIC_DRAW);
     232           0 :             gle::clearebo();
     233             :         }
     234           0 :     }
     235             : 
     236           0 :     void cleanupbb()
     237             :     {
     238           0 :         if(bbvbo)
     239             :         {
     240           0 :             glDeleteBuffers(1, &bbvbo);
     241           0 :             bbvbo = 0;
     242             :         }
     243           0 :         if(bbebo)
     244             :         {
     245           0 :             glDeleteBuffers(1, &bbebo);
     246           0 :             bbebo = 0;
     247             :         }
     248           0 :     }
     249             : 
     250             :     octaentities *visiblemms,
     251             :                 **lastvisiblemms;
     252             : 
     253           0 :     void findvisiblemms(const std::vector<extentity *> &ents, bool doquery)
     254             :     {
     255           0 :         visiblemms = nullptr;
     256           0 :         lastvisiblemms = &visiblemms;
     257           0 :         for(vtxarray *va = visibleva; va; va = va->next)
     258             :         {
     259           0 :             if(va->occluded < Occlude_BB && va->curvfc < ViewFrustumCull_Fogged)
     260             :             {
     261           0 :                 for(octaentities *oe : va->mapmodels)
     262             :                 {
     263           0 :                     if(view.isfoggedcube(oe->o, oe->size))
     264             :                     {
     265           0 :                         continue;
     266             :                     }
     267           0 :                     bool occluded = doquery && oe->query && oe->query->owner == oe && occlusionengine.checkquery(oe->query);
     268           0 :                     if(occluded)
     269             :                     {
     270           0 :                         oe->distance = -1;
     271           0 :                         oe->next = nullptr;
     272           0 :                         *lastvisiblemms = oe;
     273           0 :                         lastvisiblemms = &oe->next;
     274             :                     }
     275             :                     else
     276             :                     {
     277           0 :                         int visible = 0;
     278           0 :                         for(const int &i : oe->mapmodels)
     279             :                         {
     280           0 :                             extentity &e = *ents[i];
     281           0 :                             if(e.flags&EntFlag_NoVis)
     282             :                             {
     283           0 :                                 continue;
     284             :                             }
     285           0 :                             e.flags |= EntFlag_Render;
     286           0 :                             ++visible;
     287             :                         }
     288           0 :                         if(!visible)
     289             :                         {
     290           0 :                             continue;
     291             :                         }
     292           0 :                         oe->distance = static_cast<int>(camera1->o.dist_to_bb(oe->o, ivec(oe->o).add(oe->size)));
     293             : 
     294           0 :                         octaentities **prev = &visiblemms, *cur = visiblemms;
     295           0 :                         while(cur && cur->distance >= 0 && oe->distance > cur->distance)
     296             :                         {
     297           0 :                             prev = &cur->next;
     298           0 :                             cur = cur->next;
     299             :                         }
     300             : 
     301           0 :                         if(*prev == nullptr)
     302             :                         {
     303           0 :                             lastvisiblemms = &oe->next;
     304             :                         }
     305           0 :                         oe->next = *prev;
     306           0 :                         *prev = oe;
     307             :                     }
     308             :                 }
     309             :             }
     310             :         }
     311           0 :     }
     312             : 
     313             :     VAR(oqmm, 0, 4, 8); //`o`cclusion `q`uery `m`ap `m`odel
     314             : 
     315           0 :     void rendermapmodel(const extentity &e)
     316             :     {
     317           0 :         int anim = Anim_Mapmodel | Anim_Loop, basetime = 0;
     318           0 :         rendermapmodel(e.attr1, anim, e.o, e.attr2, e.attr3, e.attr4, Model_CullVFC | Model_CullDist, basetime, e.attr5 > 0 ? e.attr5/100.0f : 1.0f);
     319           0 :     }
     320             : 
     321           0 :     bool bbinsideva(const ivec &bo, const ivec &br, const vtxarray &va)
     322             :     {
     323           0 :         return bo.x >= va.bbmin.x && bo.y >= va.bbmin.y && bo.z >= va.bbmin.z &&
     324           0 :             br.x <= va.bbmax.x && br.y <= va.bbmax.y && br.z <= va.bbmax.z;
     325             :     }
     326             : 
     327           0 :     bool bboccluded(const ivec &bo, const ivec &br, const std::array<cube, 8> &c, const ivec &o, int size)
     328             :     {
     329           0 :         LOOP_OCTA_BOX(o, size, bo, br)
     330             :         {
     331           0 :             ivec co(i, o, size);
     332           0 :             if(c[i].ext && c[i].ext->va)
     333             :             {
     334           0 :                 vtxarray *va = c[i].ext->va;
     335           0 :                 if(va->curvfc >= ViewFrustumCull_Fogged || (va->occluded >= Occlude_BB && bbinsideva(bo, br, *va)))
     336             :                 {
     337           0 :                     continue;
     338             :                 }
     339             :             }
     340           0 :             if(c[i].children && bboccluded(bo, br, *(c[i].children), co, size>>1))
     341             :             {
     342           0 :                 continue;
     343             :             }
     344           0 :             return false;
     345             :         }
     346           0 :         return true;
     347             :     }
     348             : 
     349             :     VAR(dtoutline, 0, 1, 1); //`d`epth `t`est `outline`s
     350             : 
     351           0 :     int calcbbsidemask(const ivec &bbmin, const ivec &bbmax, const vec &lightpos, float lightradius, float bias)
     352             :     {
     353           0 :         vec pmin = vec(bbmin).sub(lightpos).div(lightradius),
     354           0 :             pmax = vec(bbmax).sub(lightpos).div(lightradius);
     355           0 :         int mask = 0x3F;
     356           0 :         float dp1 = pmax.x + pmax.y,
     357           0 :               dn1 = pmax.x - pmin.y,
     358           0 :               ap1 = std::fabs(dp1),
     359           0 :               an1 = std::fabs(dn1),
     360           0 :               dp2 = pmin.x + pmin.y,
     361           0 :               dn2 = pmin.x - pmax.y,
     362           0 :               ap2 = std::fabs(dp2),
     363           0 :               an2 = std::fabs(dn2);
     364           0 :         if(ap1 > bias*an1 && ap2 > bias*an2)
     365             :         {
     366           0 :             mask &= (3<<4)
     367           0 :                 | (dp1 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2))
     368           0 :                 | (dp2 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2));
     369             :         }
     370           0 :         if(an1 > bias*ap1 && an2 > bias*ap2)
     371             :         {
     372           0 :             mask &= (3<<4)
     373           0 :                 | (dn1 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2))
     374           0 :                 | (dn2 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2));
     375             :         }
     376           0 :         dp1 = pmax.y + pmax.z,
     377           0 :         dn1 = pmax.y - pmin.z,
     378           0 :         ap1 = std::fabs(dp1),
     379           0 :         an1 = std::fabs(dn1),
     380           0 :         dp2 = pmin.y + pmin.z,
     381           0 :         dn2 = pmin.y - pmax.z,
     382           0 :         ap2 = std::fabs(dp2),
     383           0 :         an2 = std::fabs(dn2);
     384           0 :         if(ap1 > bias*an1 && ap2 > bias*an2)
     385             :         {
     386           0 :             mask &= (3<<0)
     387           0 :                 | (dp1 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4))
     388           0 :                 | (dp2 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4));
     389             :         }
     390           0 :         if(an1 > bias*ap1 && an2 > bias*ap2)
     391             :         {
     392           0 :             mask &= (3<<0)
     393           0 :                 | (dn1 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4))
     394           0 :                 | (dn2 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4));
     395             :         }
     396           0 :         dp1 = pmax.z + pmax.x,
     397           0 :         dn1 = pmax.z - pmin.x,
     398           0 :         ap1 = std::fabs(dp1),
     399           0 :         an1 = std::fabs(dn1),
     400           0 :         dp2 = pmin.z + pmin.x,
     401           0 :         dn2 = pmin.z - pmax.x,
     402           0 :         ap2 = std::fabs(dp2),
     403           0 :         an2 = std::fabs(dn2);
     404           0 :         if(ap1 > bias*an1 && ap2 > bias*an2)
     405             :         {
     406           0 :             mask &= (3<<2)
     407           0 :                 | (dp1 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0))
     408           0 :                 | (dp2 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0));
     409             :         }
     410           0 :         if(an1 > bias*ap1 && an2 > bias*ap2)
     411             :         {
     412           0 :             mask &= (3<<2)
     413           0 :                 | (dn1 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0))
     414           0 :                 | (dn2 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0));
     415             :         }
     416           0 :         return mask;
     417             :     }
     418             : 
     419             :     VAR(smbbcull, 0, 1, 1);
     420             :     VAR(smdistcull, 0, 1, 1);
     421             :     VAR(smnodraw, 0, 0, 1);
     422             : 
     423             :     vtxarray *shadowva = nullptr;
     424             : 
     425           0 :     void addshadowva(vtxarray * const va, float dist, std::array<vtxarray *, vasortsize> &vasort)
     426             :     {
     427           0 :         va->rdistance = static_cast<int>(dist);
     428             : 
     429           0 :         int hash = std::clamp(static_cast<int>(dist*vasortsize/shadowradius), 0, vasortsize-1);
     430           0 :         vtxarray **prev = &vasort[hash],
     431           0 :                    *cur = vasort[hash];
     432             : 
     433           0 :         while(cur && va->rdistance > cur->rdistance)
     434             :         {
     435           0 :             prev = &cur->rnext;
     436           0 :             cur = cur->rnext;
     437             :         }
     438             : 
     439           0 :         va->rnext = cur;
     440           0 :         *prev = va;
     441           0 :     }
     442             : 
     443           0 :     void sortshadowvas(std::array<vtxarray *, vasortsize> &vasort)
     444             :     {
     445           0 :         shadowva = nullptr;
     446           0 :         vtxarray **last = &shadowva;
     447           0 :         for(vtxarray *i : vasort)
     448             :         {
     449           0 :             if(i)
     450             :             {
     451           0 :                 vtxarray *va = i;
     452           0 :                 *last = va;
     453           0 :                 while(va->rnext)
     454             :                 {
     455           0 :                     va = va->rnext;
     456             :                 }
     457           0 :                 last = &va->rnext;
     458             :             }
     459             :         }
     460           0 :     }
     461             : 
     462           0 :     void findcsmshadowvas(std::vector<vtxarray *> &vas, std::array<vtxarray *, vasortsize> &vasort)
     463             :     {
     464           0 :         for(vtxarray * const &v : vas)
     465             :         {
     466           0 :             ivec bbmin, bbmax;
     467           0 :             if(v->children.size() || v->mapmodels.size())
     468             :             {
     469           0 :                 bbmin = v->bbmin;
     470           0 :                 bbmax = v->bbmax;
     471             :             }
     472             :             else
     473             :             {
     474           0 :                 bbmin = v->geommin;
     475           0 :                 bbmax = v->geommax;
     476             :             }
     477           0 :             v->shadowmask = csm.calcbbcsmsplits(bbmin, bbmax);
     478           0 :             if(v->shadowmask)
     479             :             {
     480           0 :                 float dist = shadowdir.project_bb(bbmin, bbmax) - shadowbias;
     481           0 :                 addshadowva(v, dist, vasort);
     482           0 :                 if(v->children.size())
     483             :                 {
     484           0 :                     findcsmshadowvas(v->children, vasort);
     485             :                 }
     486             :             }
     487             :         }
     488           0 :     }
     489             : 
     490           0 :     void findrsmshadowvas(std::vector<vtxarray *> &vas, std::array<vtxarray *, vasortsize> &vasort)
     491             :     {
     492           0 :         for(vtxarray *v :vas)
     493             :         {
     494           0 :             ivec bbmin, bbmax;
     495           0 :             if(v->children.size() || v->mapmodels.size())
     496             :             {
     497           0 :                 bbmin = v->bbmin;
     498           0 :                 bbmax = v->bbmax;
     499             :             }
     500             :             else
     501             :             {
     502           0 :                 bbmin = v->geommin;
     503           0 :                 bbmax = v->geommax;
     504             :             }
     505           0 :             v->shadowmask = calcbbrsmsplits(bbmin, bbmax);
     506           0 :             if(v->shadowmask)
     507             :             {
     508           0 :                 float dist = shadowdir.project_bb(bbmin, bbmax) - shadowbias;
     509           0 :                 addshadowva(v, dist, vasort);
     510           0 :                 if(v->children.size())
     511             :                 {
     512           0 :                     findrsmshadowvas(v->children, vasort);
     513             :                 }
     514             :             }
     515             :         }
     516           0 :     }
     517             : 
     518           0 :     void findspotshadowvas(std::vector<vtxarray *> &vas, std::array<vtxarray *, vasortsize> &vasort)
     519             :     {
     520           0 :         for(vtxarray *v : vas)
     521             :         {
     522           0 :             float dist = vadist(*v, shadoworigin);
     523           0 :             if(dist < shadowradius || !smdistcull)
     524             :             {
     525           0 :                 v->shadowmask = !smbbcull || (v->children.size() || v->mapmodels.size() ?
     526           0 :                                     bbinsidespot(shadoworigin, shadowdir, shadowspot, v->bbmin, v->bbmax) :
     527           0 :                                     bbinsidespot(shadoworigin, shadowdir, shadowspot, v->geommin, v->geommax)) ? 1 : 0;
     528           0 :                 addshadowva(v, dist, vasort);
     529           0 :                 if(v->children.size())
     530             :                 {
     531           0 :                     findspotshadowvas(v->children, vasort);
     532             :                 }
     533             :             }
     534             :         }
     535           0 :     }
     536             : 
     537             :     octaentities *shadowmms = nullptr;
     538             : 
     539             :     struct geombatch
     540             :     {
     541             :         const elementset &es;
     542             :         VSlot &vslot;
     543             :         int offset;
     544             :         const vtxarray * const va;
     545             :         int next, batch;
     546             : 
     547           0 :         geombatch(const elementset &es, int offset, const vtxarray *va)
     548           0 :           : es(es), vslot(lookupvslot(es.texture)), offset(offset), va(va),
     549           0 :             next(-1), batch(-1)
     550           0 :         {}
     551             : 
     552             :         void renderbatch() const;
     553             : 
     554           0 :         int compare(const geombatch &b) const
     555             :         {
     556           0 :             if(va->vbuf < b.va->vbuf)
     557             :             {
     558           0 :                 return -1;
     559             :             }
     560           0 :             if(va->vbuf > b.va->vbuf)
     561             :             {
     562           0 :                 return 1;
     563             :             }
     564           0 :             if(es.attrs.layer&BlendLayer_Bottom)
     565             :             {
     566           0 :                 if(!(b.es.attrs.layer&BlendLayer_Bottom))
     567             :                 {
     568           0 :                     return 1;
     569             :                 }
     570           0 :                 int x1 = va->o.x&~0xFFF,
     571           0 :                     x2 = b.va->o.x&~0xFFF;
     572           0 :                 if(x1 < x2)
     573             :                 {
     574           0 :                     return -1;
     575             :                 }
     576           0 :                 if(x1 > x2)
     577             :                 {
     578           0 :                     return 1;
     579             :                 }
     580           0 :                 int y1 = va->o.y&~0xFFF,
     581           0 :                     y2 = b.va->o.y&~0xFFF;
     582           0 :                 if(y1 < y2)
     583             :                 {
     584           0 :                     return -1;
     585             :                 }
     586           0 :                 if(y1 > y2)
     587             :                 {
     588           0 :                     return 1;
     589             :                 }
     590             :             }
     591           0 :             else if(b.es.attrs.layer&BlendLayer_Bottom)
     592             :             {
     593           0 :                 return -1;
     594             :             }
     595           0 :             if(vslot.slot->shader < b.vslot.slot->shader)
     596             :             {
     597           0 :                 return -1;
     598             :             }
     599           0 :             if(vslot.slot->shader > b.vslot.slot->shader)
     600             :             {
     601           0 :                 return 1;
     602             :             }
     603           0 :             if(es.texture < b.es.texture)
     604             :             {
     605           0 :                 return -1;
     606             :             }
     607           0 :             if(es.texture > b.es.texture)
     608             :             {
     609           0 :                 return 1;
     610             :             }
     611           0 :             if(vslot.slot->params.size() < b.vslot.slot->params.size())
     612             :             {
     613           0 :                 return -1;
     614             :             }
     615           0 :             if(vslot.slot->params.size() > b.vslot.slot->params.size())
     616             :             {
     617           0 :                 return 1;
     618             :             }
     619           0 :             if(es.attrs.orient < b.es.attrs.orient)
     620             :             {
     621           0 :                 return -1;
     622             :             }
     623           0 :             if(es.attrs.orient > b.es.attrs.orient)
     624             :             {
     625           0 :                 return 1;
     626             :             }
     627           0 :             return 0;
     628             :         }
     629             :     };
     630             : 
     631             :     class renderstate
     632             :     {
     633             :         public:
     634             :             bool colormask, depthmask;
     635             :             int alphaing;
     636             :             GLuint vbuf;
     637             :             bool vattribs, vquery;
     638             :             int globals;
     639             : 
     640             :             void disablevquery();
     641             :             void disablevbuf();
     642             :             void enablevquery();
     643             :             void cleanupgeom();
     644             :             void enablevattribs(bool all = true);
     645             :             void disablevattribs(bool all = true);
     646             :             void renderbatches(int pass);
     647             :             void renderzpass(const vtxarray &va);
     648             :             void invalidatetexgenorient();
     649             :             void invalidatealphascale();
     650             :             void cleartexgenmillis();
     651             : 
     652           0 :             renderstate() : colormask(true), depthmask(true), alphaing(0), vbuf(0), vattribs(false),
     653           0 :                             vquery(false), globals(-1), alphascale(0), texgenorient(-1),
     654           0 :                             texgenmillis(lastmillis), tmu(-1), colorscale(1, 1, 1),
     655           0 :                             vslot(nullptr), texgenslot(nullptr), texgenvslot(nullptr),
     656           0 :                             texgenscroll(0, 0), refractscale(0), refractcolor(1, 1, 1)
     657             :             {
     658           0 :                 for(int k = 0; k < 7; ++k)
     659             :                 {
     660           0 :                     textures[k] = 0;
     661             :                 }
     662           0 :             }
     663             :         private:
     664             : 
     665             :             float alphascale;
     666             :             int texgenorient, texgenmillis;
     667             :             int tmu;
     668             :             GLuint textures[7];
     669             :             vec colorscale;
     670             :             const VSlot *vslot;
     671             :             const Slot *texgenslot;
     672             :             const VSlot *texgenvslot;
     673             :             vec2 texgenscroll;
     674             :             float refractscale;
     675             :             vec refractcolor;
     676             : 
     677             :             void changetexgen(int orient, Slot &slot, VSlot &vslot);
     678             :             void changebatchtmus();
     679             :             void changeslottmus(int pass, Slot &newslot, VSlot &newvslot);
     680             :             void bindslottex(int type, const Texture *tex, GLenum target = GL_TEXTURE_2D);
     681             :             void changeshader(int pass, const geombatch &b);
     682             : 
     683             :     };
     684             : 
     685           0 :     void renderstate::invalidatetexgenorient()
     686             :     {
     687           0 :         texgenorient = -1;
     688           0 :     }
     689             : 
     690           0 :     void renderstate::invalidatealphascale()
     691             :     {
     692           0 :         alphascale = -1;
     693           0 :     }
     694             : 
     695           0 :     void renderstate::cleartexgenmillis()
     696             :     {
     697           0 :         texgenmillis = 0;
     698           0 :     }
     699             : 
     700           0 :     void renderstate::disablevbuf()
     701             :     {
     702           0 :         gle::clearvbo();
     703           0 :         gle::clearebo();
     704           0 :         vbuf = 0;
     705           0 :     }
     706             : 
     707           0 :     void renderstate::enablevquery()
     708             :     {
     709           0 :         if(colormask)
     710             :         {
     711           0 :             colormask = false;
     712           0 :             glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
     713             :         }
     714           0 :         if(depthmask)
     715             :         {
     716           0 :             depthmask = false;
     717           0 :             glDepthMask(GL_FALSE);
     718             :         }
     719           0 :         startbb(false);
     720           0 :         vquery = true;
     721           0 :     }
     722             : 
     723           0 :     void renderstate::disablevquery()
     724             :     {
     725           0 :         endbb(false);
     726           0 :         vquery = false;
     727           0 :     }
     728             : 
     729           0 :     void renderquery(renderstate &cur, const occludequery &query, const vtxarray &va, bool full = true)
     730             :     {
     731           0 :         if(!cur.vquery)
     732             :         {
     733           0 :             cur.enablevquery();
     734             :         }
     735           0 :         query.startquery();
     736           0 :         if(full)
     737             :         {
     738           0 :             drawbb(ivec(va.bbmin).sub(1), ivec(va.bbmax).sub(va.bbmin).add(2));
     739             :         }
     740             :         else
     741             :         {
     742           0 :             drawbb(va.geommin, ivec(va.geommax).sub(va.geommin));
     743             :         }
     744           0 :         occlusionengine.endquery();
     745           0 :     }
     746             : 
     747             :     enum RenderPass
     748             :     {
     749             :         RenderPass_GBuffer = 0,
     750             :         RenderPass_Z,
     751             :         RenderPass_Caustics,
     752             :         RenderPass_GBufferBlend,
     753             :         RenderPass_ReflectiveShadowMap,
     754             :         RenderPass_ReflectiveShadowMapBlend
     755             :     };
     756             : 
     757             :     std::vector<geombatch> geombatches;
     758             :     int firstbatch = -1,
     759             :         numbatches = 0;
     760             : 
     761           0 :     void mergetexs(const renderstate &cur, const vtxarray &va, elementset *texs = nullptr, int offset = 0)
     762             :     {
     763           0 :         int numtexs = 0;
     764           0 :         if(!texs)
     765             :         {
     766           0 :             texs = va.texelems;
     767           0 :             numtexs = va.texs;
     768           0 :             if(cur.alphaing)
     769             :             {
     770           0 :                 texs += va.texs;
     771           0 :                 offset += 3*(va.tris);
     772           0 :                 numtexs = va.alphaback;
     773           0 :                 if(cur.alphaing > 1)
     774             :                 {
     775           0 :                     numtexs += va.alphafront + va.refract;
     776             :                 }
     777             :             }
     778             :         }
     779             : 
     780           0 :         if(firstbatch < 0)
     781             :         {
     782           0 :             firstbatch = geombatches.size();
     783           0 :             numbatches = numtexs;
     784           0 :             for(int i = 0; i < numtexs-1; ++i)
     785             :             {
     786           0 :                 geombatches.emplace_back(texs[i], offset, &va);
     787           0 :                 geombatches.back().next = i+1;
     788           0 :                 offset += texs[i].length;
     789             :             }
     790           0 :             geombatches.emplace_back(texs[numtexs-1], offset, &va);
     791           0 :             return;
     792             :         }
     793             : 
     794           0 :         int prevbatch = -1,
     795           0 :             curbatch = firstbatch,
     796           0 :             curtex = 0;
     797             :         do
     798             :         {
     799           0 :             geombatches.emplace_back(texs[curtex], offset, &va);
     800           0 :             geombatch &b = geombatches.back();
     801           0 :             offset += texs[curtex].length;
     802           0 :             int dir = -1;
     803           0 :             while(curbatch >= 0)
     804             :             {
     805           0 :                 dir = b.compare(geombatches[curbatch]);
     806           0 :                 if(dir <= 0)
     807             :                 {
     808           0 :                     break;
     809             :                 }
     810           0 :                 prevbatch = curbatch;
     811           0 :                 curbatch = geombatches[curbatch].next;
     812             :             }
     813           0 :             if(!dir)
     814             :             {
     815           0 :                 int last = curbatch, next;
     816             :                 for(;;)
     817             :                 {
     818           0 :                     next = geombatches[last].batch;
     819           0 :                     if(next < 0)
     820             :                     {
     821           0 :                         break;
     822             :                     }
     823           0 :                     last = next;
     824             :                 }
     825           0 :                 if(last==curbatch)
     826             :                 {
     827           0 :                     b.batch = curbatch;
     828           0 :                     b.next = geombatches[curbatch].next;
     829           0 :                     if(prevbatch < 0)
     830             :                     {
     831           0 :                         firstbatch = geombatches.size()-1;
     832             :                     }
     833             :                     else
     834             :                     {
     835           0 :                         geombatches[prevbatch].next = geombatches.size()-1;
     836             :                     }
     837           0 :                     curbatch = geombatches.size()-1;
     838             :                 }
     839             :                 else
     840             :                 {
     841           0 :                     b.batch = next;
     842           0 :                     geombatches[last].batch = geombatches.size()-1;
     843             :                 }
     844             :             }
     845             :             else
     846             :             {
     847           0 :                 numbatches++;
     848           0 :                 b.next = curbatch;
     849           0 :                 if(prevbatch < 0)
     850             :                 {
     851           0 :                     firstbatch = geombatches.size()-1;
     852             :                 }
     853             :                 else
     854             :                 {
     855           0 :                     geombatches[prevbatch].next = geombatches.size()-1;
     856             :                 }
     857           0 :                 prevbatch = geombatches.size()-1;
     858             :             }
     859           0 :         } while(++curtex < numtexs);
     860             :     }
     861             : 
     862           0 :     void renderstate::enablevattribs(bool all)
     863             :     {
     864           0 :         gle::enablevertex();
     865           0 :         if(all)
     866             :         {
     867           0 :             gle::enabletexcoord0();
     868           0 :             gle::enablenormal();
     869           0 :             gle::enabletangent();
     870             :         }
     871           0 :         vattribs = true;
     872           0 :     }
     873             : 
     874           0 :     void renderstate::disablevattribs(bool all)
     875             :     {
     876           0 :         gle::disablevertex();
     877           0 :         if(all)
     878             :         {
     879           0 :             gle::disabletexcoord0();
     880           0 :             gle::disablenormal();
     881           0 :             gle::disabletangent();
     882             :         }
     883           0 :         vattribs = false;
     884           0 :     }
     885             : 
     886           0 :     void changevbuf(renderstate &cur, int pass, const vtxarray &va)
     887             :     {
     888           0 :         gle::bindvbo(va.vbuf);
     889           0 :         gle::bindebo(va.ebuf);
     890           0 :         cur.vbuf = va.vbuf;
     891             : 
     892           0 :         vertex *vdata = nullptr;
     893           0 :         gle::vertexpointer(sizeof(vertex), vdata->pos.data());
     894           0 :         gle::vertexpointer(sizeof(vertex), vdata->pos.data());
     895             : 
     896           0 :         if(pass==RenderPass_GBuffer || pass==RenderPass_ReflectiveShadowMap)
     897             :         {
     898           0 :             gle::normalpointer(sizeof(vertex), vdata->norm.data(), GL_BYTE);
     899           0 :             gle::texcoord0pointer(sizeof(vertex), vdata->tc.data());
     900           0 :             gle::tangentpointer(sizeof(vertex), vdata->tangent.data(), GL_BYTE);
     901             :         }
     902           0 :     }
     903             : 
     904           0 :     void renderstate::changebatchtmus()
     905             :     {
     906           0 :         if(tmu != 0)
     907             :         {
     908           0 :             tmu = 0;
     909           0 :             glActiveTexture(GL_TEXTURE0);
     910             :         }
     911           0 :     }
     912             : 
     913           0 :     void renderstate::bindslottex(int type, const Texture *tex, GLenum target)
     914             :     {
     915           0 :         if(textures[type] != tex->id)
     916             :         {
     917           0 :             if(tmu != type)
     918             :             {
     919           0 :                 tmu = type;
     920           0 :                 glActiveTexture(GL_TEXTURE0 + type);
     921             :             }
     922           0 :             glBindTexture(target, textures[type] = tex->id);
     923             :         }
     924           0 :     }
     925             : 
     926           0 :     void renderstate::changeslottmus(int pass, Slot &newslot, VSlot &newvslot)
     927             :     {
     928           0 :         Texture *diffuse = newslot.sts.empty() ? notexture : newslot.sts[0].t;
     929           0 :         if(pass==RenderPass_GBuffer || pass==RenderPass_ReflectiveShadowMap)
     930             :         {
     931           0 :             bindslottex(Tex_Diffuse, diffuse);
     932             : 
     933           0 :             if(pass == RenderPass_GBuffer)
     934             :             {
     935           0 :                 if(msaasamples)
     936             :                 {
     937           0 :                     GLOBALPARAMF(hashid, newvslot.index);
     938             :                 }
     939           0 :                 if(newslot.shader->type & Shader_Triplanar)
     940             :                 {
     941           0 :                     float scale = defaulttexscale/newvslot.scale;
     942           0 :                     GLOBALPARAMF(texgenscale, scale/diffuse->xs, scale/diffuse->ys);
     943             :                 }
     944             :             }
     945             :         }
     946             : 
     947           0 :         if(alphaing)
     948             :         {
     949           0 :             float alpha = alphaing > 1 ? newvslot.alphafront : newvslot.alphaback;
     950           0 :             if(alphascale != alpha)
     951             :             {
     952           0 :                 alphascale = alpha;
     953           0 :                 refractscale = 0;
     954           0 :                 goto changecolorparams; //also run next if statement
     955             :             }
     956           0 :             if(colorscale != newvslot.colorscale)
     957             :             {
     958           0 :             changecolorparams:
     959           0 :                 colorscale = newvslot.colorscale;
     960           0 :                 GLOBALPARAMF(colorparams,
     961             :                              alpha*newvslot.colorscale.x,
     962             :                              alpha*newvslot.colorscale.y,
     963             :                              alpha*newvslot.colorscale.z,
     964             :                              alpha);
     965             :             }
     966           0 :             if(alphaing > 1 && newvslot.refractscale > 0 &&
     967           0 :                   (refractscale != newvslot.refractscale || refractcolor != newvslot.refractcolor))
     968             :             {
     969           0 :                 refractscale = newvslot.refractscale;
     970           0 :                 refractcolor = newvslot.refractcolor;
     971           0 :                 float refractscale = 0.5f/ldrscale*(1-alpha);
     972           0 :                 GLOBALPARAMF(refractparams,
     973             :                              newvslot.refractcolor.x*refractscale,
     974             :                              newvslot.refractcolor.y*refractscale,
     975             :                              newvslot.refractcolor.z*refractscale,
     976             :                              newvslot.refractscale*viewh);
     977             :             }
     978             :         }
     979           0 :         else if(colorscale != newvslot.colorscale)
     980             :         {
     981           0 :             colorscale = newvslot.colorscale;
     982           0 :             GLOBALPARAMF(colorparams, newvslot.colorscale.x, newvslot.colorscale.y, newvslot.colorscale.z, 1);
     983             :         }
     984             : 
     985           0 :         for(const Slot::Tex &t : newslot.sts)
     986             :         {
     987           0 :             switch(t.type)
     988             :             {
     989           0 :                 case Tex_Normal:
     990             :                 case Tex_Glow:
     991             :                 {
     992           0 :                     bindslottex(t.type, t.t);
     993           0 :                     break;
     994             :                 }
     995             :             }
     996             :         }
     997           0 :         GLOBALPARAM(rotate, vec(newvslot.angle.y, newvslot.angle.z, diffuse->ratio()));
     998           0 :         if(tmu != 0)
     999             :         {
    1000           0 :             tmu = 0;
    1001           0 :             glActiveTexture(GL_TEXTURE0);
    1002             :         }
    1003           0 :         vslot = &newvslot;
    1004           0 :     }
    1005             : 
    1006           0 :     void renderstate::changetexgen(int orient, Slot &slot, VSlot &vslot)
    1007             :     {
    1008           0 :         if(texgenslot != &slot || texgenvslot != &vslot)
    1009             :         {
    1010           0 :             const Texture *curtex = !texgenslot || texgenslot->sts.empty() ? notexture : texgenslot->sts[0].t;
    1011           0 :             const Texture *tex = slot.sts.empty() ? notexture : slot.sts[0].t;
    1012           0 :             if(!texgenvslot || slot.sts.empty() ||
    1013           0 :                 (curtex->xs != tex->xs || curtex->ys != tex->ys ||
    1014           0 :                  texgenvslot->rotation != vslot.rotation || texgenvslot->scale != vslot.scale ||
    1015           0 :                  texgenvslot->offset != vslot.offset || texgenvslot->scroll != vslot.scroll) ||
    1016           0 :                  texgenvslot->angle != vslot.angle)
    1017             :             {
    1018           0 :                 const texrotation &r = texrotations[vslot.rotation];
    1019           0 :                 float xs = r.flipx ? -tex->xs : tex->xs,
    1020           0 :                       ys = r.flipy ? -tex->ys : tex->ys;
    1021           0 :                 vec2 scroll(vslot.scroll);
    1022           0 :                 if(r.swapxy)
    1023             :                 {
    1024           0 :                     std::swap(scroll.x, scroll.y);
    1025             :                 }
    1026           0 :                 scroll.x *= texgenmillis*tex->xs/xs;
    1027           0 :                 scroll.y *= texgenmillis*tex->ys/ys;
    1028           0 :                 if(texgenscroll != scroll)
    1029             :                 {
    1030           0 :                     texgenscroll = scroll;
    1031           0 :                     texgenorient = -1;
    1032             :                 }
    1033             :             }
    1034           0 :             texgenslot = &slot;
    1035           0 :             texgenvslot = &vslot;
    1036             :         }
    1037             : 
    1038           0 :         if(texgenorient == orient)
    1039             :         {
    1040           0 :             return;
    1041             :         }
    1042           0 :         GLOBALPARAM(texgenscroll, texgenscroll);
    1043             : 
    1044           0 :         texgenorient = orient;
    1045             :     }
    1046             : 
    1047           0 :     void renderstate::changeshader(int pass, const geombatch &b)
    1048             :     {
    1049           0 :         VSlot &vslot = b.vslot;
    1050           0 :         Slot &slot = *vslot.slot;
    1051           0 :         if(pass == RenderPass_ReflectiveShadowMap)
    1052             :         {
    1053           0 :             if(b.es.attrs.layer&BlendLayer_Bottom)
    1054             :             {
    1055           0 :                 rsmworldshader->setvariant(0, 0, slot, vslot);
    1056             :             }
    1057             :             else
    1058             :             {
    1059           0 :                 rsmworldshader->set(slot, vslot);
    1060             :             }
    1061             :         }
    1062           0 :         else if(alphaing)
    1063             :         {
    1064           0 :             slot.shader->setvariant(alphaing > 1 && vslot.refractscale > 0 ? 1 : 0, 1, slot, vslot);
    1065             :         }
    1066           0 :         else if(b.es.attrs.layer&BlendLayer_Bottom)
    1067             :         {
    1068           0 :             slot.shader->setvariant(0, 0, slot, vslot);
    1069             :         }
    1070             :         else
    1071             :         {
    1072           0 :             slot.shader->set(slot, vslot);
    1073             :         }
    1074           0 :         globals = GlobalShaderParamState::nextversion;
    1075           0 :     }
    1076             : 
    1077             :     template<class T>
    1078           0 :     void updateshader(T &cur)
    1079             :     {
    1080           0 :         if(cur.globals != GlobalShaderParamState::nextversion)
    1081             :         {
    1082           0 :             if(Shader::lastshader)
    1083             :             {
    1084           0 :                 Shader::lastshader->flushparams();
    1085             :             }
    1086           0 :             cur.globals = GlobalShaderParamState::nextversion;
    1087             :         }
    1088           0 :     }
    1089             : 
    1090           0 :     void geombatch::renderbatch() const
    1091             :     {
    1092           0 :         gbatches++;
    1093           0 :         for(const geombatch *curbatch = this;; curbatch = &geombatches[curbatch->batch])
    1094             :         {
    1095           0 :             ushort len = curbatch->es.length;
    1096           0 :             if(len)
    1097             :             {
    1098           0 :                 drawtris(len, (ushort *)0 + curbatch->va->eoffset + curbatch->offset, curbatch->es.minvert, curbatch->es.maxvert);
    1099           0 :                 vtris += len/3;
    1100             :             }
    1101           0 :             if(curbatch->batch < 0)
    1102             :             {
    1103           0 :                 break;
    1104             :             }
    1105           0 :         }
    1106           0 :     }
    1107             : 
    1108           0 :     void resetbatches()
    1109             :     {
    1110           0 :         geombatches.clear();
    1111           0 :         firstbatch = -1;
    1112           0 :         numbatches = 0;
    1113           0 :     }
    1114             : 
    1115           0 :     void renderstate::renderbatches(int pass)
    1116             :     {
    1117           0 :         vslot = nullptr;
    1118           0 :         int curbatch = firstbatch;
    1119           0 :         if(curbatch >= 0)
    1120             :         {
    1121           0 :             if(!depthmask)
    1122             :             {
    1123           0 :                 depthmask = true;
    1124           0 :                 glDepthMask(GL_TRUE);
    1125             :             }
    1126           0 :             if(!colormask)
    1127             :             {
    1128           0 :                 colormask = true;
    1129           0 :                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    1130             :             }
    1131           0 :             if(!vattribs)
    1132             :             {
    1133           0 :                 if(vquery)
    1134             :                 {
    1135           0 :                     disablevquery();
    1136             :                 }
    1137           0 :                 enablevattribs();
    1138             :             }
    1139             :         }
    1140           0 :         while(curbatch >= 0)
    1141             :         {
    1142           0 :             const geombatch &b = geombatches[curbatch];
    1143           0 :             curbatch = b.next;
    1144             : 
    1145           0 :             if(vbuf != b.va->vbuf)
    1146             :             {
    1147           0 :                 changevbuf(*this, pass, *b.va);
    1148             :             }
    1149           0 :             if(pass == RenderPass_GBuffer || pass == RenderPass_ReflectiveShadowMap)
    1150             :             {
    1151           0 :                 changebatchtmus();
    1152             :             }
    1153           0 :             if(vslot != &b.vslot)
    1154             :             {
    1155           0 :                 changeslottmus(pass, *b.vslot.slot, b.vslot);
    1156           0 :                 if(texgenorient != b.es.attrs.orient || (texgenorient < Orient_Any && texgenvslot != &b.vslot))
    1157             :                 {
    1158           0 :                     changetexgen(b.es.attrs.orient, *b.vslot.slot, b.vslot);
    1159             :                 }
    1160           0 :                 changeshader(pass, b);
    1161             :             }
    1162             :             else
    1163             :             {
    1164           0 :                 if(texgenorient != b.es.attrs.orient)
    1165             :                 {
    1166           0 :                     changetexgen(b.es.attrs.orient, *b.vslot.slot, b.vslot);
    1167             :                 }
    1168           0 :                 updateshader(*this);
    1169             :             }
    1170             : 
    1171           0 :             b.renderbatch();
    1172             :         }
    1173             : 
    1174           0 :         resetbatches();
    1175           0 :     }
    1176             : 
    1177           0 :     void renderstate::renderzpass(const vtxarray &va)
    1178             :     {
    1179           0 :         if(!vattribs)
    1180             :         {
    1181           0 :             if(vquery)
    1182             :             {
    1183           0 :                 disablevquery();
    1184             :             }
    1185           0 :             enablevattribs(false);
    1186             :         }
    1187           0 :         if(vbuf!=va.vbuf)
    1188             :         {
    1189           0 :             changevbuf(*this, RenderPass_Z, va);
    1190             :         }
    1191           0 :         if(!depthmask)
    1192             :         {
    1193           0 :             depthmask = true;
    1194           0 :             glDepthMask(GL_TRUE);
    1195             :         }
    1196           0 :         if(colormask)
    1197             :         {
    1198           0 :             colormask = false;
    1199           0 :             glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
    1200             :         }
    1201           0 :         int firsttex = 0,
    1202           0 :             numtris = va.tris,
    1203           0 :             offset = 0;
    1204           0 :         if(alphaing)
    1205             :         {
    1206           0 :             firsttex += va.texs;
    1207           0 :             offset += 3*(va.tris);
    1208           0 :             numtris = va.alphabacktris + va.alphafronttris + va.refracttris;
    1209           0 :             xtravertsva += 3*numtris;
    1210             :         }
    1211             :         else
    1212             :         {
    1213           0 :             xtravertsva += va.verts;
    1214             :         }
    1215           0 :         nocolorshader->set();
    1216           0 :         drawvatris(va, 3*numtris, offset);
    1217           0 :     }
    1218             : 
    1219             :     VAR(batchgeom, 0, 1, 1);
    1220             : 
    1221           0 :     void renderva(renderstate &cur, const vtxarray &va, int pass = RenderPass_GBuffer, bool doquery = false)
    1222             :     {
    1223           0 :         switch(pass)
    1224             :         {
    1225           0 :             case RenderPass_GBuffer:
    1226           0 :                 if(!cur.alphaing)
    1227             :                 {
    1228           0 :                     vverts += va.verts;
    1229             :                 }
    1230           0 :                 if(doquery && va.query)
    1231             :                 {
    1232           0 :                     if(geombatches.size())
    1233             :                     {
    1234           0 :                         cur.renderbatches(pass);
    1235             :                     }
    1236           0 :                     va.query->startquery();
    1237             :                 }
    1238           0 :                 mergetexs(cur, va);
    1239           0 :                 if(doquery)
    1240             :                 {
    1241           0 :                     if(va.query)
    1242             :                     {
    1243           0 :                         if(geombatches.size())
    1244             :                         {
    1245           0 :                             cur.renderbatches(pass);
    1246             :                         }
    1247           0 :                         occlusionengine.endquery();
    1248             :                     }
    1249             :                 }
    1250           0 :                 else if(!batchgeom && geombatches.size())
    1251             :                 {
    1252           0 :                     cur.renderbatches(pass);
    1253             :                 }
    1254           0 :                 break;
    1255             : 
    1256           0 :             case RenderPass_GBufferBlend:
    1257           0 :                 if(doquery && va.query)
    1258             :                 {
    1259           0 :                     if(geombatches.size())
    1260             :                     {
    1261           0 :                         cur.renderbatches(RenderPass_GBuffer);
    1262             :                     }
    1263           0 :                     va.query->startquery();
    1264             :                 }
    1265           0 :                 mergetexs(cur, va, &va.texelems[va.texs], 3*va.tris);
    1266           0 :                 if(doquery)
    1267             :                 {
    1268           0 :                     if(va.query)
    1269             :                     {
    1270           0 :                         if(geombatches.size())
    1271             :                         {
    1272           0 :                             cur.renderbatches(RenderPass_GBuffer);
    1273             :                         }
    1274           0 :                         occlusionengine.endquery();
    1275             :                     }
    1276             :                 }
    1277           0 :                 else if(!batchgeom && geombatches.size())
    1278             :                 {
    1279           0 :                     cur.renderbatches(RenderPass_GBuffer);
    1280             :                 }
    1281           0 :                 break;
    1282             : 
    1283           0 :             case RenderPass_Caustics:
    1284           0 :                 if(!cur.vattribs)
    1285             :                 {
    1286           0 :                     cur.enablevattribs(false);
    1287             :                 }
    1288           0 :                 if(cur.vbuf!=va.vbuf)
    1289             :                 {
    1290           0 :                     changevbuf(cur, pass, va);
    1291             :                 }
    1292           0 :                 drawvatris(va, 3*va.tris, 0);
    1293           0 :                 xtravertsva += va.verts;
    1294           0 :                 break;
    1295             : 
    1296           0 :             case RenderPass_Z:
    1297           0 :                 if(doquery && va.query)
    1298             :                 {
    1299           0 :                     va.query->startquery();
    1300             :                 }
    1301           0 :                 cur.renderzpass(va);
    1302           0 :                 if(doquery && va.query)
    1303             :                 {
    1304           0 :                     occlusionengine.endquery();
    1305             :                 }
    1306           0 :                 break;
    1307             : 
    1308           0 :             case RenderPass_ReflectiveShadowMap:
    1309           0 :                 mergetexs(cur, va);
    1310           0 :                 if(!batchgeom && geombatches.size())
    1311             :                 {
    1312           0 :                     cur.renderbatches(pass);
    1313             :                 }
    1314           0 :                 break;
    1315             : 
    1316           0 :             case RenderPass_ReflectiveShadowMapBlend:
    1317           0 :                 mergetexs(cur, va, &va.texelems[va.texs], 3*va.tris);
    1318           0 :                 if(!batchgeom && geombatches.size())
    1319             :                 {
    1320           0 :                     cur.renderbatches(RenderPass_ReflectiveShadowMap);
    1321             :                 }
    1322           0 :                 break;
    1323             :         }
    1324           0 :     }
    1325             : 
    1326           0 :     void setupgeom()
    1327             :     {
    1328           0 :         glActiveTexture(GL_TEXTURE0);
    1329           0 :         GLOBALPARAMF(colorparams, 1, 1, 1, 1);
    1330           0 :     }
    1331             : 
    1332           0 :     void renderstate::cleanupgeom()
    1333             :     {
    1334           0 :         if(vattribs)
    1335             :         {
    1336           0 :             disablevattribs();
    1337             :         }
    1338           0 :         if(vbuf)
    1339             :         {
    1340           0 :             disablevbuf();
    1341             :         }
    1342           0 :     }
    1343             : 
    1344             :     VAR(oqgeom, 0, 1, 1); //occlusion query geometry
    1345             : 
    1346             :     std::vector<const vtxarray *> alphavas;
    1347             : 
    1348           0 :     CVARP(explicitskycolor, 0x800080);
    1349             : 
    1350             :     struct decalbatch
    1351             :     {
    1352             :         const elementset &es;
    1353             :         DecalSlot &slot;
    1354             :         int offset;
    1355             :         const vtxarray &va;
    1356             :         int next, batch;
    1357             : 
    1358           0 :         decalbatch(const elementset &es, int offset, const vtxarray &va)
    1359           0 :           : es(es), slot(lookupdecalslot(es.texture)), offset(offset), va(va),
    1360           0 :             next(-1), batch(-1)
    1361           0 :         {}
    1362             : 
    1363             :         void renderdecalbatch();
    1364             : 
    1365           0 :         int compare(const decalbatch &b) const
    1366             :         {
    1367           0 :             if(va.vbuf < b.va.vbuf)
    1368             :             {
    1369           0 :                 return -1;
    1370             :             }
    1371           0 :             if(va.vbuf > b.va.vbuf)
    1372             :             {
    1373           0 :                 return 1;
    1374             :             }
    1375           0 :             if(slot.shader < b.slot.shader)
    1376             :             {
    1377           0 :                 return -1;
    1378             :             }
    1379           0 :             if(slot.shader > b.slot.shader)
    1380             :             {
    1381           0 :                 return 1;
    1382             :             }
    1383           0 :             if(es.texture < b.es.texture)
    1384             :             {
    1385           0 :                 return -1;
    1386             :             }
    1387           0 :             if(es.texture > b.es.texture)
    1388             :             {
    1389           0 :                 return 1;
    1390             :             }
    1391           0 :             if(slot.Slot::params.size() < b.slot.Slot::params.size())
    1392             :             {
    1393           0 :                 return -1;
    1394             :             }
    1395           0 :             if(slot.Slot::params.size() > b.slot.Slot::params.size())
    1396             :             {
    1397           0 :                 return 1;
    1398             :             }
    1399           0 :             if(es.reuse < b.es.reuse)
    1400             :             {
    1401           0 :                 return -1;
    1402             :             }
    1403           0 :             if(es.reuse > b.es.reuse)
    1404             :             {
    1405           0 :                 return 1;
    1406             :             }
    1407           0 :             return 0;
    1408             :         }
    1409             :     };
    1410             : 
    1411             :     std::vector<decalbatch> decalbatches;
    1412             : 
    1413             :     class decalrenderer
    1414             :     {
    1415             :         public:
    1416             :             GLuint vbuf;
    1417             :             int globals;
    1418             : 
    1419             :             void renderdecalbatches(int pass);
    1420             : 
    1421           0 :             decalrenderer() : vbuf(0), globals(-1), colorscale(1, 1, 1), tmu(-1), slot(nullptr)
    1422             :             {
    1423           0 :                 for(int i = 0; i < 7; ++i)
    1424             :                 {
    1425           0 :                     textures[i] = 0;
    1426             :                 }
    1427           0 :             }
    1428             :         private:
    1429             :             vec colorscale;
    1430             :             int tmu;
    1431             :             GLuint textures[7];
    1432             :             DecalSlot *slot;
    1433             : 
    1434             :             void changebatchtmus();
    1435             :             void bindslottex(int type, const Texture *tex, GLenum target = GL_TEXTURE_2D);
    1436             :             void changeslottmus(DecalSlot &slot);
    1437             :             void changeshader(int pass, const decalbatch &b);
    1438             :     };
    1439             : 
    1440           0 :     void mergedecals(const vtxarray &va)
    1441             :     {
    1442           0 :         elementset *texs = va.decalelems;
    1443           0 :         int numtexs = va.decaltexs,
    1444           0 :             offset  = 0;
    1445             : 
    1446           0 :         if(firstbatch < 0)
    1447             :         {
    1448           0 :             firstbatch = decalbatches.size();
    1449           0 :             numbatches = numtexs;
    1450           0 :             for(int i = 0; i < numtexs-1; ++i)
    1451             :             {
    1452           0 :                 decalbatches.emplace_back(decalbatch(texs[i], offset, va));
    1453           0 :                 decalbatches.back().next = i+1;
    1454           0 :                 offset += texs[i].length;
    1455             :             }
    1456           0 :             decalbatches.emplace_back(decalbatch(texs[numtexs-1], offset, va));
    1457           0 :             return;
    1458             :         }
    1459             : 
    1460           0 :         int prevbatch = -1,
    1461           0 :             curbatch = firstbatch,
    1462           0 :             curtex = 0;
    1463             :         do
    1464             :         {
    1465           0 :             decalbatch b = decalbatch(texs[curtex], offset, va);
    1466           0 :             offset += texs[curtex].length;
    1467           0 :             int dir = -1;
    1468           0 :             while(curbatch >= 0)
    1469             :             {
    1470           0 :                 dir = b.compare(decalbatches[curbatch]);
    1471           0 :                 if(dir <= 0)
    1472             :                 {
    1473           0 :                     break;
    1474             :                 }
    1475           0 :                 prevbatch = curbatch;
    1476           0 :                 curbatch = decalbatches[curbatch].next;
    1477             :             }
    1478           0 :             if(!dir)
    1479             :             {
    1480           0 :                 int last = curbatch, next;
    1481             :                 for(;;)
    1482             :                 {
    1483           0 :                     next = decalbatches[last].batch;
    1484           0 :                     if(next < 0)
    1485             :                     {
    1486           0 :                         break;
    1487             :                     }
    1488           0 :                     last = next;
    1489             :                 }
    1490           0 :                 if(last==curbatch)
    1491             :                 {
    1492           0 :                     b.batch = curbatch;
    1493           0 :                     b.next = decalbatches[curbatch].next;
    1494           0 :                     if(prevbatch < 0)
    1495             :                     {
    1496           0 :                         firstbatch = decalbatches.size()-1;
    1497             :                     }
    1498             :                     else
    1499             :                     {
    1500           0 :                         decalbatches[prevbatch].next = decalbatches.size()-1;
    1501             :                     }
    1502           0 :                     curbatch = decalbatches.size()-1;
    1503             :                 }
    1504             :                 else
    1505             :                 {
    1506           0 :                     b.batch = next;
    1507           0 :                     decalbatches[last].batch = decalbatches.size()-1;
    1508             :                 }
    1509             :             }
    1510             :             else
    1511             :             {
    1512           0 :                 numbatches++;
    1513           0 :                 b.next = curbatch;
    1514           0 :                 if(prevbatch < 0)
    1515             :                 {
    1516           0 :                     firstbatch = decalbatches.size()-1;
    1517             :                 }
    1518             :                 else
    1519             :                 {
    1520           0 :                     decalbatches[prevbatch].next = decalbatches.size()-1;
    1521             :                 }
    1522           0 :                 prevbatch = decalbatches.size()-1;
    1523             :             }
    1524           0 :             decalbatches.push_back(b);
    1525           0 :         } while(++curtex < numtexs);
    1526             :     }
    1527             : 
    1528           0 :     void resetdecalbatches()
    1529             :     {
    1530           0 :         decalbatches.clear();
    1531           0 :         firstbatch = -1;
    1532           0 :         numbatches = 0;
    1533           0 :     }
    1534             : 
    1535           0 :     void changevbuf(decalrenderer &cur, const vtxarray &va)
    1536             :     {
    1537           0 :         gle::bindvbo(va.vbuf);
    1538           0 :         gle::bindebo(va.decalbuf);
    1539           0 :         cur.vbuf = va.vbuf;
    1540           0 :         vertex *vdata = nullptr;
    1541             :         //note inane bikeshedding: use of offset from dereferenced null ptr (aka 0)
    1542           0 :         gle::vertexpointer(sizeof(vertex), vdata->pos.data());
    1543           0 :         gle::normalpointer(sizeof(vertex), vdata->norm.data(), GL_BYTE, 4);
    1544           0 :         gle::texcoord0pointer(sizeof(vertex), vdata->tc.data(), GL_FLOAT, 3);
    1545           0 :         gle::tangentpointer(sizeof(vertex), vdata->tangent.data(), GL_BYTE);
    1546           0 :     }
    1547             : 
    1548           0 :     void decalrenderer::changebatchtmus()
    1549             :     {
    1550           0 :         if(tmu != 0)
    1551             :         {
    1552           0 :             tmu = 0;
    1553           0 :             glActiveTexture(GL_TEXTURE0);
    1554             :         }
    1555           0 :     }
    1556             : 
    1557           0 :     void decalrenderer::bindslottex(int type, const Texture *tex, GLenum target)
    1558             :     {
    1559           0 :         if(textures[type] != tex->id)
    1560             :         {
    1561           0 :             if(tmu != type)
    1562             :             {
    1563           0 :                 tmu = type;
    1564           0 :                 glActiveTexture(GL_TEXTURE0 + type);
    1565             :             }
    1566           0 :             glBindTexture(target, textures[type] = tex->id);
    1567             :         }
    1568           0 :     }
    1569             : 
    1570           0 :     void decalrenderer::changeslottmus(DecalSlot &dslot)
    1571             :     {
    1572           0 :         const Texture *diffuse = dslot.sts.empty() ? notexture : dslot.sts[0].t;
    1573           0 :         bindslottex(Tex_Diffuse, diffuse);
    1574           0 :         for(const Slot::Tex &t : dslot.sts)
    1575             :         {
    1576           0 :             switch(t.type)
    1577             :             {
    1578           0 :                 case Tex_Normal:
    1579             :                 case Tex_Glow:
    1580             :                 {
    1581           0 :                     bindslottex(t.type, t.t);
    1582           0 :                     break;
    1583             :                 }
    1584           0 :                 case Tex_Spec:
    1585             :                 {
    1586           0 :                     if(t.combined < 0)
    1587             :                     {
    1588           0 :                         bindslottex(Tex_Glow, t.t);
    1589             :                     }
    1590           0 :                     break;
    1591             :                 }
    1592             :             }
    1593             :         }
    1594           0 :         if(tmu != 0)
    1595             :         {
    1596           0 :             tmu = 0;
    1597           0 :             glActiveTexture(GL_TEXTURE0);
    1598             :         }
    1599           0 :         if(colorscale != dslot.colorscale)
    1600             :         {
    1601           0 :             colorscale = dslot.colorscale;
    1602           0 :             GLOBALPARAMF(colorparams, dslot.colorscale.x, dslot.colorscale.y, dslot.colorscale.z, 1);
    1603             :         }
    1604           0 :         slot = &dslot;
    1605           0 :     }
    1606             : 
    1607           0 :     void decalrenderer::changeshader(int pass, const decalbatch &b)
    1608             :     {
    1609           0 :         DecalSlot &slot = b.slot;
    1610           0 :         if(b.es.reuse)
    1611             :         {
    1612           0 :             VSlot &reuse = lookupvslot(b.es.reuse);
    1613           0 :             if(pass)
    1614             :             {
    1615           0 :                 slot.shader->setvariant(0, 0, slot, reuse);
    1616             :             }
    1617             :             else
    1618             :             {
    1619           0 :                 slot.shader->set(slot, reuse);
    1620             :             }
    1621             :         }
    1622           0 :         else if(pass)
    1623             :         {
    1624           0 :             slot.shader->setvariant(0, 0, slot);
    1625             :         }
    1626             :         else
    1627             :         {
    1628           0 :             slot.shader->set(slot);
    1629             :         }
    1630           0 :         globals = GlobalShaderParamState::nextversion;
    1631           0 :     }
    1632             : 
    1633           0 :     void decalbatch::renderdecalbatch()
    1634             :     {
    1635           0 :         gbatches++;
    1636           0 :         for(decalbatch *curbatch = this;; curbatch = &decalbatches[curbatch->batch])
    1637             :         {
    1638           0 :             ushort len = curbatch->es.length;
    1639           0 :             if(len)
    1640             :             {
    1641           0 :                 drawtris(len, reinterpret_cast<ushort *>(curbatch->va.decaloffset) + curbatch->offset, curbatch->es.minvert, curbatch->es.maxvert);
    1642           0 :                 vtris += len/3;
    1643             :             }
    1644           0 :             if(curbatch->batch < 0)
    1645             :             {
    1646           0 :                 break;
    1647             :             }
    1648           0 :         }
    1649           0 :     }
    1650             : 
    1651           0 :     void decalrenderer::renderdecalbatches(int pass)
    1652             :     {
    1653           0 :         slot = nullptr;
    1654           0 :         int curbatch = firstbatch;
    1655           0 :         while(curbatch >= 0)
    1656             :         {
    1657           0 :             decalbatch &b = decalbatches[curbatch];
    1658           0 :             curbatch = b.next;
    1659             : 
    1660           0 :             if(pass && !b.slot.shader->numvariants(0))
    1661             :             {
    1662           0 :                 continue;
    1663             :             }
    1664           0 :             if(vbuf != b.va.vbuf)
    1665             :             {
    1666           0 :                 changevbuf(*this, b.va);
    1667             :             }
    1668           0 :             changebatchtmus();
    1669           0 :             if(slot != &b.slot)
    1670             :             {
    1671           0 :                 changeslottmus(b.slot);
    1672           0 :                 changeshader(pass, b);
    1673             :             }
    1674             :             else
    1675             :             {
    1676           0 :                 updateshader(*this);
    1677             :             }
    1678             : 
    1679           0 :             b.renderdecalbatch();
    1680             :         }
    1681             : 
    1682           0 :         resetdecalbatches();
    1683           0 :     }
    1684             : 
    1685           0 :     void setupdecals()
    1686             :     {
    1687           0 :         gle::enablevertex();
    1688           0 :         gle::enablenormal();
    1689           0 :         gle::enabletexcoord0();
    1690           0 :         gle::enabletangent();
    1691             : 
    1692           0 :         glDepthMask(GL_FALSE);
    1693           0 :         glEnable(GL_BLEND);
    1694           0 :         enablepolygonoffset(GL_POLYGON_OFFSET_FILL);
    1695             : 
    1696           0 :         GLOBALPARAMF(colorparams, 1, 1, 1, 1);
    1697           0 :     }
    1698             : 
    1699           0 :     void cleanupdecals()
    1700             :     {
    1701           0 :         disablepolygonoffset(GL_POLYGON_OFFSET_FILL);
    1702           0 :         glDisable(GL_BLEND);
    1703           0 :         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    1704           0 :         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    1705           0 :         glDepthMask(GL_TRUE);
    1706           0 :         maskgbuffer("cnd");
    1707             : 
    1708           0 :         gle::disablevertex();
    1709           0 :         gle::disablenormal();
    1710           0 :         gle::disabletexcoord0();
    1711           0 :         gle::disabletangent();
    1712             : 
    1713           0 :         gle::clearvbo();
    1714           0 :         gle::clearebo();
    1715           0 :     }
    1716             : 
    1717             :     VAR(batchdecals, 0, 1, 1);
    1718             : 
    1719             :     struct shadowdraw
    1720             :     {
    1721             :         GLuint ebuf, vbuf;
    1722             :         int offset, tris, next;
    1723             :         GLuint minvert, maxvert;
    1724             :     };
    1725             : 
    1726             :     struct shadowverts
    1727             :     {
    1728             :         static constexpr int tablesize = 1<<13;
    1729             :         std::array<int, tablesize> table;
    1730             :         std::vector<vec> verts;
    1731             :         std::vector<int> chain;
    1732             : 
    1733           1 :         shadowverts() { clear(); }
    1734             : 
    1735           1 :         void clear()
    1736             :         {
    1737           1 :             table.fill(-1);
    1738           1 :             chain.clear();
    1739           1 :             verts.clear();
    1740           1 :         }
    1741             : 
    1742           0 :         int add(const vec &v)
    1743             :         {
    1744             :             auto vechash = std::hash<vec>();
    1745           0 :             uint h = vechash(v)&(tablesize-1);
    1746           0 :             for(int i = table[h]; i>=0; i = chain[i])
    1747             :             {
    1748           0 :                 if(verts[i] == v)
    1749             :                 {
    1750           0 :                     return i;
    1751             :                 }
    1752             :             }
    1753           0 :             if(verts.size() >= USHRT_MAX)
    1754             :             {
    1755           0 :                 return -1;
    1756             :             }
    1757           0 :             verts.push_back(v);
    1758           0 :             chain.emplace_back(table[h]);
    1759           0 :             return table[h] = verts.size()-1;
    1760             :         }
    1761             :     } shadowverts;
    1762             :     std::array<std::vector<GLuint>, 6> shadowtris;
    1763             :     std::vector<GLuint> shadowvbos;
    1764             :     std::unordered_map<int, shadowmesh> shadowmeshes;
    1765             :     std::vector<shadowdraw> shadowdraws;
    1766             : 
    1767             :     struct shadowdrawinfo
    1768             :     {
    1769             :         int last;
    1770             :         GLuint minvert, maxvert;
    1771             : 
    1772           0 :         shadowdrawinfo() : last(-1)
    1773             :         {
    1774           0 :             reset();
    1775           0 :         }
    1776             : 
    1777           0 :         void reset()
    1778             :         {
    1779           0 :             minvert = USHRT_MAX;
    1780           0 :             maxvert = 0;
    1781           0 :         }
    1782             :     };
    1783             : 
    1784           0 :     void flushshadowmeshdraws(shadowmesh &m, int sides, std::array<shadowdrawinfo, 6> &draws)
    1785             :     {
    1786           0 :         int numindexes = 0;
    1787           0 :         for(int i = 0; i < sides; ++i)
    1788             :         {
    1789           0 :             numindexes += shadowtris[i].size();
    1790             :         }
    1791           0 :         if(!numindexes)
    1792             :         {
    1793           0 :             return;
    1794             :         }
    1795             : 
    1796           0 :         GLuint ebuf = 0,
    1797           0 :                vbuf = 0;
    1798           0 :         glGenBuffers(1, &ebuf);
    1799           0 :         glGenBuffers(1, &vbuf);
    1800           0 :         ushort *indexes = new ushort[numindexes];
    1801           0 :         int offset = 0;
    1802           0 :         for(int i = 0; i < sides; ++i)
    1803             :         {
    1804           0 :             if(shadowtris[i].size())
    1805             :             {
    1806           0 :                 if(draws[i].last < 0)
    1807             :                 {
    1808           0 :                     m.draws[i] = shadowdraws.size();
    1809             :                 }
    1810             :                 else
    1811             :                 {
    1812           0 :                     shadowdraws[draws[i].last].next = shadowdraws.size();
    1813             :                 }
    1814           0 :                 draws[i].last = shadowdraws.size();
    1815             : 
    1816             :                 shadowdraw d;
    1817           0 :                 d.ebuf = ebuf;
    1818           0 :                 d.vbuf = vbuf;
    1819           0 :                 d.offset = offset;
    1820           0 :                 d.tris = shadowtris[i].size()/3;
    1821           0 :                 d.minvert = draws[i].minvert;
    1822           0 :                 d.maxvert = draws[i].maxvert;
    1823           0 :                 d.next = -1;
    1824           0 :                 shadowdraws.push_back(d);
    1825             : 
    1826           0 :                 std::memcpy(indexes + offset, shadowtris[i].data(), shadowtris[i].size()*sizeof(ushort));
    1827           0 :                 offset += shadowtris[i].size();
    1828             : 
    1829           0 :                 shadowtris[i].clear();
    1830           0 :                 draws[i].reset();
    1831             :             }
    1832             :         }
    1833             : 
    1834           0 :         gle::bindebo(ebuf);
    1835           0 :         glBufferData(GL_ELEMENT_ARRAY_BUFFER, numindexes*sizeof(ushort), indexes, GL_STATIC_DRAW);
    1836           0 :         gle::clearebo();
    1837           0 :         delete[] indexes;
    1838             : 
    1839           0 :         gle::bindvbo(vbuf);
    1840           0 :         glBufferData(GL_ARRAY_BUFFER, shadowverts.verts.size()*sizeof(vec), shadowverts.verts.data(), GL_STATIC_DRAW);
    1841           0 :         gle::clearvbo();
    1842           0 :         shadowverts.clear();
    1843             : 
    1844           0 :         shadowvbos.push_back(ebuf);
    1845           0 :         shadowvbos.push_back(vbuf);
    1846             :     }
    1847             : 
    1848           0 :     int calctrisidemask(const vec &p1, const vec &p2, const vec &p3, float bias)
    1849             :     {
    1850             :         // p1, p2, p3 are in the cubemap's local coordinate system
    1851             :         // bias = border/(size - border)
    1852           0 :         int mask = 0x3F;
    1853           0 :         float dp1 = p1.x + p1.y,
    1854           0 :               dn1 = p1.x - p1.y,
    1855           0 :               ap1 = std::fabs(dp1),
    1856           0 :               an1 = std::fabs(dn1),
    1857           0 :               dp2 = p2.x + p2.y,
    1858           0 :               dn2 = p2.x - p2.y,
    1859           0 :               ap2 = std::fabs(dp2),
    1860           0 :               an2 = std::fabs(dn2),
    1861           0 :               dp3 = p3.x + p3.y,
    1862           0 :               dn3 = p3.x - p3.y,
    1863           0 :               ap3 = std::fabs(dp3),
    1864           0 :               an3 = std::fabs(dn3);
    1865           0 :         if(ap1 > bias*an1 && ap2 > bias*an2 && ap3 > bias*an3)
    1866             :         {
    1867           0 :             mask &=  (3<<4)
    1868           0 :                    | (dp1 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2))
    1869           0 :                    | (dp2 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2))
    1870           0 :                    | (dp3 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2));
    1871             :         }
    1872           0 :         if(an1 > bias*ap1 && an2 > bias*ap2 && an3 > bias*ap3)
    1873           0 :             mask &=  (3<<4)
    1874           0 :                    | (dn1 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2))
    1875           0 :                    | (dn2 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2))
    1876           0 :                    | (dn3 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2));
    1877           0 :         dp1 = p1.y + p1.z,
    1878           0 :         dn1 = p1.y - p1.z,
    1879           0 :         ap1 = std::fabs(dp1),
    1880           0 :         an1 = std::fabs(dn1),
    1881           0 :         dp2 = p2.y + p2.z,
    1882           0 :         dn2 = p2.y - p2.z,
    1883           0 :         ap2 = std::fabs(dp2),
    1884           0 :         an2 = std::fabs(dn2),
    1885           0 :         dp3 = p3.y + p3.z,
    1886           0 :         dn3 = p3.y - p3.z,
    1887           0 :         ap3 = std::fabs(dp3),
    1888           0 :         an3 = std::fabs(dn3);
    1889           0 :         if(ap1 > bias*an1 && ap2 > bias*an2 && ap3 > bias*an3)
    1890             :         {
    1891           0 :             mask &= (3<<0)
    1892           0 :                 | (dp1 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4))
    1893           0 :                 | (dp2 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4))
    1894           0 :                 | (dp3 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4));
    1895             :         }
    1896           0 :         if(an1 > bias*ap1 && an2 > bias*ap2 && an3 > bias*ap3)
    1897             :         {
    1898           0 :             mask &= (3<<0)
    1899           0 :                 | (dn1 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4))
    1900           0 :                 | (dn2 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4))
    1901           0 :                 | (dn3 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4));
    1902             :         }
    1903           0 :         dp1 = p1.z + p1.x,
    1904           0 :         dn1 = p1.z - p1.x,
    1905           0 :         ap1 = std::fabs(dp1),
    1906           0 :         an1 = std::fabs(dn1),
    1907           0 :         dp2 = p2.z + p2.x,
    1908           0 :         dn2 = p2.z - p2.x,
    1909           0 :         ap2 = std::fabs(dp2),
    1910           0 :         an2 = std::fabs(dn2),
    1911           0 :         dp3 = p3.z + p3.x,
    1912           0 :         dn3 = p3.z - p3.x,
    1913           0 :         ap3 = std::fabs(dp3),
    1914           0 :         an3 = std::fabs(dn3);
    1915           0 :         if(ap1 > bias*an1 && ap2 > bias*an2 && ap3 > bias*an3)
    1916             :         {
    1917           0 :             mask &= (3<<2)
    1918           0 :                 | (dp1 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0))
    1919           0 :                 | (dp2 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0))
    1920           0 :                 | (dp3 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0));
    1921             :         }
    1922           0 :         if(an1 > bias*ap1 && an2 > bias*ap2 && an3 > bias*ap3)
    1923             :         {
    1924           0 :             mask &= (3<<2)
    1925           0 :                 | (dn1 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0))
    1926           0 :                 | (dn2 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0))
    1927           0 :                 | (dn3 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0));
    1928             :         }
    1929           0 :         return mask;
    1930             :     }
    1931             : 
    1932           0 :     void addshadowmeshtri(shadowmesh &m, int sides, std::array<shadowdrawinfo, 6> &draws, const vec &v0, const vec &v1, const vec &v2)
    1933             :     {
    1934           0 :         vec l0 = vec(v0).sub(shadoworigin);
    1935           0 :         float side = l0.scalartriple(vec(v1).sub(v0), vec(v2).sub(v0));
    1936           0 :         if(smcullside ? side > 0 : side < 0)
    1937             :         {
    1938           0 :             return;
    1939             :         }
    1940           0 :         vec l1 = vec(v1).sub(shadoworigin),
    1941           0 :             l2 = vec(v2).sub(shadoworigin);
    1942           0 :         if(l0.squaredlen() > shadowradius*shadowradius && l1.squaredlen() > shadowradius*shadowradius && l2.squaredlen() > shadowradius*shadowradius)
    1943             :         {
    1944           0 :             return;
    1945             :         }
    1946           0 :         int sidemask = 0;
    1947           0 :         switch(m.type)
    1948             :         {
    1949           0 :             case ShadowMap_Spot:
    1950             :             {
    1951           0 :                 sidemask = bbinsidespot(shadoworigin, shadowdir, shadowspot, ivec(vec(v0).min(v1).min(v2)), ivec(vec(v0).max(v1).max(v2).add(1))) ? 1 : 0;
    1952           0 :                 break;
    1953             :             }
    1954           0 :             case ShadowMap_CubeMap:
    1955             :             {
    1956           0 :                 sidemask = calctrisidemask(l0.div(shadowradius), l1.div(shadowradius), l2.div(shadowradius), shadowbias);
    1957           0 :                 break;
    1958             :             }
    1959             :         }
    1960           0 :         if(!sidemask)
    1961             :         {
    1962           0 :             return;
    1963             :         }
    1964           0 :         if(shadowverts.verts.size() + 3 >= USHRT_MAX)
    1965             :         {
    1966           0 :             flushshadowmeshdraws(m, sides, draws);
    1967             :         }
    1968           0 :         int i0 = shadowverts.add(v0),
    1969           0 :             i1 = shadowverts.add(v1),
    1970           0 :             i2 = shadowverts.add(v2);
    1971           0 :         GLuint minvert = std::min(i0, std::min(i1, i2)),
    1972           0 :                maxvert = std::max(i0, std::max(i1, i2));
    1973           0 :         for(int k = 0; k < sides; ++k)
    1974             :         {
    1975           0 :             if(sidemask&(1<<k))
    1976             :             {
    1977           0 :                 shadowdrawinfo &d = draws[k];
    1978           0 :                 d.minvert = std::min(d.minvert, minvert);
    1979           0 :                 d.maxvert = std::max(d.maxvert, maxvert);
    1980           0 :                 shadowtris[k].push_back(i0);
    1981           0 :                 shadowtris[k].push_back(i1);
    1982           0 :                 shadowtris[k].push_back(i2);
    1983             :             }
    1984             :         }
    1985             :     }
    1986             : 
    1987           0 :     void genshadowmeshtris(shadowmesh &m, int sides, std::array<shadowdrawinfo, 6> &draws, ushort *edata, int numtris, vertex *vdata)
    1988             :     {
    1989           0 :         for(int j = 0; j < 3*numtris; j += 3)
    1990             :         {
    1991           0 :             addshadowmeshtri(m, sides, draws, vdata[edata[j]].pos, vdata[edata[j+1]].pos, vdata[edata[j+2]].pos);
    1992             :         }
    1993           0 :     }
    1994             : 
    1995           0 :     void genshadowmeshmapmodels(shadowmesh &m, int sides, std::array<shadowdrawinfo, 6> &draws)
    1996             :     {
    1997           0 :         const std::vector<extentity *> &ents = entities::getents();
    1998           0 :         for(octaentities *oe = shadowmms; oe; oe = oe->rnext)
    1999             :         {
    2000           0 :             for(uint k = 0; k < oe->mapmodels.size(); k++)
    2001             :             {
    2002           0 :                 extentity &e = *ents[oe->mapmodels[k]];
    2003           0 :                 if(e.flags&(EntFlag_NoVis|EntFlag_NoShadow))
    2004             :                 {
    2005           0 :                     continue;
    2006             :                 }
    2007           0 :                 e.flags |= EntFlag_Render;
    2008             :             }
    2009             :         }
    2010           0 :         std::vector<triangle> tris;
    2011           0 :         for(octaentities *oe = shadowmms; oe; oe = oe->rnext)
    2012             :         {
    2013           0 :             for(const int &j : oe->mapmodels)
    2014             :             {
    2015           0 :                 extentity &e = *ents[j];
    2016           0 :                 if(!(e.flags&EntFlag_Render))
    2017             :                 {
    2018           0 :                     continue;
    2019             :                 }
    2020           0 :                 e.flags &= ~EntFlag_Render;
    2021           0 :                 model *mm = loadmapmodel(e.attr1);
    2022           0 :                 if(!mm || !mm->shadow || mm->animated() || (mm->alphashadow && mm->alphatested()))
    2023             :                 {
    2024           0 :                     continue;
    2025             :                 }
    2026           0 :                 matrix4x3 orient;
    2027           0 :                 orient.identity();
    2028           0 :                 if(e.attr2)
    2029             :                 {
    2030           0 :                     orient.rotate_around_z(sincosmod360(e.attr2));
    2031             :                 }
    2032           0 :                 if(e.attr3)
    2033             :                 {
    2034           0 :                     orient.rotate_around_x(sincosmod360(e.attr3));
    2035             :                 }
    2036           0 :                 if(e.attr4)
    2037             :                 {
    2038           0 :                     orient.rotate_around_y(sincosmod360(-e.attr4));
    2039             :                 }
    2040           0 :                 if(e.attr5 > 0)
    2041             :                 {
    2042           0 :                     orient.scale(e.attr5/100.0f);
    2043             :                 }
    2044           0 :                 orient.settranslation(e.o);
    2045           0 :                 tris.clear();
    2046           0 :                 mm->genshadowmesh(tris, orient);
    2047             : 
    2048           0 :                 for(uint i = 0; i < tris.size(); i++)
    2049             :                 {
    2050           0 :                     triangle &t = tris[i];
    2051           0 :                     addshadowmeshtri(m, sides, draws, t.a, t.b, t.c);
    2052             :                 }
    2053             : 
    2054           0 :                 e.flags |= EntFlag_ShadowMesh;
    2055             :             }
    2056             :         }
    2057           0 :     }
    2058             : 
    2059           0 :     void genshadowmesh(int idx, const extentity &e)
    2060             :     {
    2061           0 :         shadowmesh m;
    2062           0 :         m.type = calcshadowinfo(e, m.origin, m.radius, m.spotloc, m.spotangle, shadowbias);
    2063           0 :         if(!m.type)
    2064             :         {
    2065           0 :             return;
    2066             :         }
    2067           0 :         m.draws.fill(-1);
    2068             : 
    2069           0 :         shadowmapping = m.type;
    2070           0 :         shadoworigin = m.origin;
    2071           0 :         shadowradius = m.radius;
    2072           0 :         shadowdir = m.type == ShadowMap_Spot ? vec(m.spotloc).sub(m.origin).normalize() : vec(0, 0, 0);
    2073           0 :         shadowspot = m.spotangle;
    2074             : 
    2075           0 :         findshadowvas();
    2076           0 :         findshadowmms();
    2077             : 
    2078           0 :         int sides = m.type == ShadowMap_Spot ? 1 : 6;
    2079           0 :         std::array<shadowdrawinfo, 6> draws;
    2080           0 :         for(vtxarray *va = shadowva; va; va = va->rnext)
    2081             :         {
    2082           0 :             if(va->shadowmask)
    2083             :             {
    2084           0 :                 if(va->tris)
    2085             :                 {
    2086           0 :                     genshadowmeshtris(m, sides, draws, va->edata + va->eoffset, va->tris, va->vdata);
    2087             :                 }
    2088           0 :                 if(skyshadow && va->sky)
    2089             :                 {
    2090           0 :                     genshadowmeshtris(m, sides, draws, va->skydata + va->skyoffset, va->sky/3, va->vdata);
    2091             :                 }
    2092             :             }
    2093             :         }
    2094           0 :         if(shadowmms)
    2095             :         {
    2096           0 :             genshadowmeshmapmodels(m, sides, draws);
    2097             :         }
    2098           0 :         flushshadowmeshdraws(m, sides, draws);
    2099             : 
    2100           0 :         shadowmeshes[idx] = m;
    2101             : 
    2102           0 :         shadowmapping = 0;
    2103             :     }
    2104             : 
    2105           0 :     VARF(smmesh, 0, 1, 1, { if(!smmesh) clearshadowmeshes(); });
    2106             : }
    2107             : 
    2108             : /* externally relevant functionality */
    2109             : ///////////////////////////////////////
    2110             : 
    2111             : // vfc - view frustum culling
    2112             : 
    2113           0 : int vfc::isfoggedcube(const ivec &o, int size) const
    2114             : {
    2115           0 :     for(int i = 0; i < 4; ++i)
    2116             :     {
    2117           0 :         if(o.dist(vfcP[i]) < -vfcDfar[i]*size)
    2118             :         {
    2119           0 :             return true;
    2120             :         }
    2121             :     }
    2122           0 :     float dist = o.dist(vfcP[4]);
    2123           0 :     return dist < -vfcDfar[4]*size || dist > vfcDfog - vfcDnear[4]*size;
    2124             : }
    2125             : 
    2126           0 : int vfc::isvisiblecube(const ivec &o, int size) const
    2127             : {
    2128           0 :     int v = ViewFrustumCull_FullyVisible;
    2129             :     float dist;
    2130             : 
    2131           0 :     for(int i = 0; i < 5; ++i)
    2132             :     {
    2133           0 :         dist = o.dist(vfcP[i]);
    2134           0 :         if(dist < -vfcDfar[i]*size)
    2135             :         {
    2136           0 :             return ViewFrustumCull_NotVisible;
    2137             :         }
    2138           0 :         if(dist < -vfcDnear[i]*size)
    2139             :         {
    2140           0 :             v = ViewFrustumCull_PartlyVisible;
    2141             :         }
    2142             :     }
    2143             : 
    2144           0 :     dist -= vfcDfog;
    2145           0 :     if(dist > -vfcDnear[4]*size)
    2146             :     {
    2147           0 :         return ViewFrustumCull_Fogged;
    2148             :     }
    2149           0 :     if(dist > -vfcDfar[4]*size)
    2150             :     {
    2151           0 :         v = ViewFrustumCull_PartlyVisible;
    2152             :     }
    2153             : 
    2154           0 :     return v;
    2155             : }
    2156             : 
    2157           0 : void vfc::calcvfcD()
    2158             : {
    2159           0 :     for(int i = 0; i < 5; ++i)
    2160             :     {
    2161           0 :         plane &p = vfcP[i];
    2162           0 :         vfcDnear[i] = vfcDfar[i] = 0;
    2163           0 :         for(int k = 0; k < 3; ++k)
    2164             :         {
    2165           0 :             if(p[k] > 0)
    2166             :             {
    2167           0 :                 vfcDfar[i] += p[k];
    2168             :             }
    2169             :             else
    2170             :             {
    2171           0 :                 vfcDnear[i] += p[k];
    2172             :             }
    2173             :         }
    2174             :     }
    2175           0 : }
    2176             : 
    2177           0 : void vfc::visiblecubes(bool cull)
    2178             : {
    2179           0 :     if(cull)
    2180             :     {
    2181           0 :         setvfcP();
    2182           0 :         findvisiblevas();
    2183             :     }
    2184             :     else
    2185             :     {
    2186           0 :         for(int i = 0; i < 5; ++i)
    2187             :         {
    2188           0 :             vfcP[i].x = vfcP[i].y = vfcP[i].z = vfcP[i].offset = 0;
    2189             :         };
    2190           0 :         vfcDfog = farplane;
    2191           0 :         vfcDnear.fill(0);
    2192           0 :         vfcDfar.fill(0);
    2193           0 :         visibleva = nullptr;
    2194           0 :         for(uint i = 0; i < valist.size(); i++)
    2195             :         {
    2196           0 :             vtxarray *va = valist[i];
    2197           0 :             va->distance = 0;
    2198           0 :             va->curvfc = ViewFrustumCull_FullyVisible;
    2199           0 :             va->occluded = !va->texs ? Occlude_Geom : Occlude_Nothing;
    2200           0 :             va->query = nullptr;
    2201           0 :             va->next = visibleva;
    2202           0 :             visibleva = va;
    2203             :         }
    2204             :     }
    2205           0 : }
    2206             : 
    2207           0 : bool vfc::isfoggedsphere(float rad, const vec &cv) const
    2208             : {
    2209           0 :     for(int i = 0; i < 4; ++i)
    2210             :     {
    2211           0 :         if(vfcP[i].dist(cv) < -rad)
    2212             :         {
    2213           0 :             return true;
    2214             :         }
    2215             :     }
    2216           0 :     float dist = vfcP[4].dist(cv);
    2217           0 :     return dist < -rad || dist > vfcDfog + rad; // true if abs(dist) is large
    2218             : }
    2219             : 
    2220           0 : int vfc::isvisiblesphere(float rad, const vec &cv) const
    2221             : {
    2222           0 :     int v = ViewFrustumCull_FullyVisible;
    2223             :     float dist;
    2224             : 
    2225           0 :     for(int i = 0; i < 5; ++i)
    2226             :     {
    2227           0 :         dist = vfcP[i].dist(cv);
    2228           0 :         if(dist < -rad)
    2229             :         {
    2230           0 :             return ViewFrustumCull_NotVisible;
    2231             :         }
    2232           0 :         if(dist < rad)
    2233             :         {
    2234           0 :             v = ViewFrustumCull_PartlyVisible;
    2235             :         }
    2236             :     }
    2237             : 
    2238           0 :     dist -= vfcDfog;
    2239           0 :     if(dist > rad)
    2240             :     {
    2241           0 :         return ViewFrustumCull_Fogged;  //ViewFrustumCull_NotVisible;    // culling when fog is closer than size of world results in HOM
    2242             :     }
    2243           0 :     if(dist > -rad)
    2244             :     {
    2245           0 :         v = ViewFrustumCull_PartlyVisible;
    2246             :     }
    2247           0 :     return v;
    2248             : }
    2249             : 
    2250           0 : int vfc::isvisiblebb(const ivec &bo, const ivec &br) const
    2251             : {
    2252           0 :     int v = ViewFrustumCull_FullyVisible;
    2253             :     float dnear, dfar;
    2254             : 
    2255           0 :     for(int i = 0; i < 5; ++i)
    2256             :     {
    2257           0 :         const plane &p = vfcP[i];
    2258           0 :         dnear = dfar = bo.dist(p);
    2259           0 :         if(p.x > 0)
    2260             :         {
    2261           0 :             dfar += p.x*br.x;
    2262             :         }
    2263             :         else
    2264             :         {
    2265           0 :             dnear += p.x*br.x;
    2266             :         }
    2267           0 :         if(p.y > 0)
    2268             :         {
    2269           0 :             dfar += p.y*br.y;
    2270             :         }
    2271             :         else
    2272             :         {
    2273           0 :             dnear += p.y*br.y;
    2274             :         }
    2275           0 :         if(p.z > 0)
    2276             :         {
    2277           0 :             dfar += p.z*br.z;
    2278             :         }
    2279             :         else
    2280             :         {
    2281           0 :             dnear += p.z*br.z;
    2282             :         }
    2283           0 :         if(dfar < 0)
    2284             :         {
    2285           0 :             return ViewFrustumCull_NotVisible;
    2286             :         }
    2287           0 :         if(dnear < 0)
    2288             :         {
    2289           0 :             v = ViewFrustumCull_PartlyVisible;
    2290             :         }
    2291             :     }
    2292             : 
    2293           0 :     if(dnear > vfcDfog)
    2294             :     {
    2295           0 :         return ViewFrustumCull_Fogged;
    2296             :     }
    2297           0 :     if(dfar > vfcDfog)
    2298             :     {
    2299           0 :         v = ViewFrustumCull_PartlyVisible;
    2300             :     }
    2301           0 :     return v;
    2302             : }
    2303             : 
    2304           0 : bool cubeworld::bboccluded(const ivec &bo, const ivec &br) const
    2305             : {
    2306           0 :     int diff = (bo.x^br.x) | (bo.y^br.y) | (bo.z^br.z);
    2307           0 :     if(diff&~((1<<worldscale)-1))
    2308             :     {
    2309           0 :         return false;
    2310             :     }
    2311           0 :     int scale = worldscale-1;
    2312           0 :     if(diff&(1<<scale))
    2313             :     {
    2314           0 :         return ::bboccluded(bo, br, *worldroot, ivec(0, 0, 0), 1<<scale);
    2315             :     }
    2316           0 :     const cube *c = &(*worldroot)[OCTA_STEP(bo.x, bo.y, bo.z, scale)];
    2317           0 :     if(c->ext && c->ext->va)
    2318             :     {
    2319           0 :         vtxarray *va = c->ext->va;
    2320           0 :         if(va->curvfc >= ViewFrustumCull_Fogged || (va->occluded >= Occlude_BB && bbinsideva(bo, br, *va)))
    2321             :         {
    2322           0 :             return true;
    2323             :         }
    2324             :     }
    2325           0 :     scale--;
    2326           0 :     while(c->children && !(diff&(1<<scale)))
    2327             :     {
    2328           0 :         c = &(*c->children)[OCTA_STEP(bo.x, bo.y, bo.z, scale)];
    2329           0 :         if(c->ext && c->ext->va)
    2330             :         {
    2331           0 :             vtxarray *va = c->ext->va;
    2332           0 :             if(va->curvfc >= ViewFrustumCull_Fogged || (va->occluded >= Occlude_BB && bbinsideva(bo, br, *va)))
    2333             :             {
    2334           0 :                 return true;
    2335             :             }
    2336             :         }
    2337           0 :         scale--;
    2338             :     }
    2339           0 :     if(c->children)
    2340             :     {
    2341           0 :         return ::bboccluded(bo, br, *(c->children), ivec(bo).mask(~((2<<scale)-1)), 1<<scale);
    2342             :     }
    2343           0 :     return false;
    2344             : }
    2345             : 
    2346           0 : void startbb(bool mask)
    2347             : {
    2348           0 :     setupbb();
    2349           0 :     gle::bindvbo(bbvbo);
    2350           0 :     gle::bindebo(bbebo);
    2351           0 :     gle::vertexpointer(sizeof(vec), (const vec *)0);
    2352           0 :     gle::enablevertex();
    2353           0 :     SETSHADER(bbquery,);
    2354           0 :     if(mask)
    2355             :     {
    2356           0 :         glDepthMask(GL_FALSE);
    2357           0 :         glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
    2358             :     }
    2359           0 : }
    2360             : 
    2361           0 : void endbb(bool mask)
    2362             : {
    2363           0 :     gle::disablevertex();
    2364           0 :     gle::clearvbo();
    2365           0 :     gle::clearebo();
    2366           0 :     if(mask)
    2367             :     {
    2368           0 :         glDepthMask(GL_TRUE);
    2369           0 :         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    2370             :     }
    2371           0 : }
    2372             : 
    2373           0 : void drawbb(const ivec &bo, const ivec &br)
    2374             : {
    2375           0 :     LOCALPARAMF(bborigin, bo.x, bo.y, bo.z);
    2376           0 :     LOCALPARAMF(bbsize, br.x, br.y, br.z);
    2377           0 :     glDrawRangeElements(GL_TRIANGLES, 0, 8-1, 3*2*6, GL_UNSIGNED_SHORT, (ushort *)0);
    2378           0 :     xtraverts += 8;
    2379           0 : }
    2380             : 
    2381           0 : void vfc::setvfcP(const vec &bbmin, const vec &bbmax)
    2382             : {
    2383           0 :     vec4<float> px = camprojmatrix.rowx(),
    2384           0 :          py = camprojmatrix.rowy(),
    2385           0 :          pz = camprojmatrix.rowz(),
    2386           0 :          pw = camprojmatrix.roww();
    2387           0 :     vfcP[0] = plane(vec4<float>(pw).mul(-bbmin.x).add(px)).normalize(); // left plane
    2388           0 :     vfcP[1] = plane(vec4<float>(pw).mul(bbmax.x).sub(px)).normalize(); // right plane
    2389           0 :     vfcP[2] = plane(vec4<float>(pw).mul(-bbmin.y).add(py)).normalize(); // bottom plane
    2390           0 :     vfcP[3] = plane(vec4<float>(pw).mul(bbmax.y).sub(py)).normalize(); // top plane
    2391           0 :     vfcP[4] = plane(vec4<float>(pw).add(pz)).normalize(); // near/far planes
    2392             : 
    2393           0 :     vfcDfog = std::min(calcfogcull(), static_cast<float>(farplane));
    2394           0 :     view.calcvfcD();
    2395           0 : }
    2396             : 
    2397             : //oq
    2398             : 
    2399           0 : void Occluder::clearqueries()
    2400             : {
    2401           0 :     for(queryframe &i : queryframes)
    2402             :     {
    2403           0 :         i.cleanup();
    2404             :     }
    2405           0 : }
    2406             : 
    2407           0 : void Occluder::flipqueries()
    2408             : {
    2409           0 :     flipquery = (flipquery + 1) % maxqueryframes;
    2410           0 :     queryframes[flipquery].flip();
    2411           0 : }
    2412             : 
    2413           0 : void Occluder::endquery()
    2414             : {
    2415           0 :     glEndQuery(querytarget());
    2416           0 : }
    2417             : 
    2418           0 : bool Occluder::checkquery(occludequery *query, bool nowait)
    2419             : {
    2420           0 :     if(query->fragments < 0)
    2421             :     {
    2422           0 :         if(nowait || !oqwait)
    2423             :         {
    2424             :             GLint avail;
    2425           0 :             glGetQueryObjectiv(query->id, GL_QUERY_RESULT_AVAILABLE, &avail);
    2426           0 :             if(!avail)
    2427             :             {
    2428           0 :                 return false;
    2429             :             }
    2430             :         }
    2431             : 
    2432             :         GLuint fragments;
    2433           0 :         glGetQueryObjectuiv(query->id, GL_QUERY_RESULT, &fragments);
    2434           0 :         query->fragments = querytarget() == GL_SAMPLES_PASSED || !fragments ? static_cast<int>(fragments) : oqfrags;
    2435             :     }
    2436           0 :     return query->fragments < oqfrags;
    2437             : }
    2438             : 
    2439           0 : void Occluder::resetqueries()
    2440             : {
    2441           0 :     for(queryframe &i : queryframes)
    2442             :     {
    2443           0 :         i.reset();
    2444             :     }
    2445           0 : }
    2446             : 
    2447           1 : int Occluder::getnumqueries() const
    2448             : {
    2449           1 :     return queryframes[flipquery].cur;
    2450             : }
    2451             : 
    2452           0 : void Occluder::queryframe::flip()
    2453             : {
    2454           0 :     for(int i = 0; i < cur; ++i)
    2455             :     {
    2456           0 :         queries[i].owner = nullptr;
    2457             :     }
    2458           0 :     for(; defer > 0 && max < maxquery; defer--)
    2459             :     {
    2460           0 :         queries[max].owner = nullptr;
    2461           0 :         queries[max].fragments = -1;
    2462           0 :         glGenQueries(1, &queries[max++].id);
    2463             :     }
    2464           0 :     cur = defer = 0;
    2465           0 : }
    2466             : 
    2467           0 : occludequery *Occluder::queryframe::newquery(const void *owner)
    2468             : {
    2469           0 :     if(cur >= max)
    2470             :     {
    2471           0 :         if(max >= maxquery)
    2472             :         {
    2473           0 :             return nullptr;
    2474             :         }
    2475           0 :         if(deferquery)
    2476             :         {
    2477           0 :             if(max + defer < maxquery)
    2478             :             {
    2479           0 :                 defer++;
    2480             :             }
    2481           0 :             return nullptr;
    2482             :         }
    2483           0 :         glGenQueries(1, &queries[max++].id);
    2484             :     }
    2485           0 :     occludequery *query = &queries[cur++];
    2486           0 :     query->owner = owner;
    2487           0 :     query->fragments = -1;
    2488           0 :     return query;
    2489             : }
    2490             : 
    2491           0 : void Occluder::queryframe::reset()
    2492             : {
    2493           0 :     for(int i = 0; i < max; ++i)
    2494             :     {
    2495           0 :         queries[i].owner = nullptr;
    2496             :     }
    2497           0 : }
    2498             : 
    2499           0 : void Occluder::queryframe::cleanup()
    2500             : {
    2501           0 :     for(int i = 0; i < max; ++i)
    2502             :     {
    2503           0 :         glDeleteQueries(1, &queries[i].id);
    2504           0 :         queries[i].owner = nullptr;
    2505             :     }
    2506           0 :     cur = max = defer = 0;
    2507           0 : }
    2508             : 
    2509           0 : void occludequery::startquery() const
    2510             : {
    2511           0 :     glBeginQuery(querytarget(), this->id);
    2512           0 : }
    2513             : 
    2514           0 : void rendermapmodels()
    2515             : {
    2516             :     static int skipoq = 0;
    2517           0 :     bool doquery = !drawtex && oqfrags && oqmm;
    2518           0 :     const std::vector<extentity *> &ents = entities::getents();
    2519           0 :     findvisiblemms(ents, doquery);
    2520             : 
    2521           0 :     for(octaentities *oe = visiblemms; oe; oe = oe->next)
    2522             :     {
    2523           0 :         if(oe->distance>=0)
    2524             :         {
    2525           0 :             bool rendered = false;
    2526           0 :             for(uint i = 0; i < oe->mapmodels.size(); i++)
    2527             :             {
    2528           0 :                 extentity &e = *ents[oe->mapmodels[i]];
    2529           0 :                 if(!(e.flags&EntFlag_Render))
    2530             :                 {
    2531           0 :                     continue;
    2532             :                 }
    2533           0 :                 if(!rendered)
    2534             :                 {
    2535           0 :                     rendered = true;
    2536           0 :                     oe->query = doquery && oe->distance>0 && !(++skipoq%oqmm) ? occlusionengine.newquery(oe) : nullptr;
    2537           0 :                     if(oe->query)
    2538             :                     {
    2539           0 :                         occlusionengine.setupmodelquery(oe->query);
    2540             :                     }
    2541             :                 }
    2542           0 :                 rendermapmodel(e);
    2543           0 :                 e.flags &= ~EntFlag_Render;
    2544             :             }
    2545           0 :             if(rendered && oe->query)
    2546             :             {
    2547           0 :                 occlusionengine.endmodelquery();
    2548             :             }
    2549             :         }
    2550             :     }
    2551           0 :     rendermapmodelbatches();
    2552           0 :     clearbatchedmapmodels();
    2553             : 
    2554           0 :     bool queried = false;
    2555           0 :     for(octaentities *oe = visiblemms; oe; oe = oe->next)
    2556             :     {
    2557           0 :         if(oe->distance<0)
    2558             :         {
    2559           0 :             oe->query = doquery && !camera1->o.insidebb(oe->bbmin, oe->bbmax, 1) ? occlusionengine.newquery(oe) : nullptr;
    2560           0 :             if(!oe->query)
    2561             :             {
    2562           0 :                 continue;
    2563             :             }
    2564           0 :             if(!queried)
    2565             :             {
    2566           0 :                 startbb();
    2567           0 :                 queried = true;
    2568             :             }
    2569           0 :             oe->query->startquery();
    2570           0 :             drawbb(oe->bbmin, ivec(oe->bbmax).sub(oe->bbmin));
    2571           0 :             occlusionengine.endquery();
    2572             :         }
    2573             :     }
    2574           0 :     if(queried)
    2575             :     {
    2576           0 :         endbb();
    2577             :     }
    2578           0 : }
    2579             : 
    2580           0 : void renderoutline()
    2581             : {
    2582           0 :     ldrnotextureshader->set();
    2583             : 
    2584           0 :     gle::enablevertex();
    2585             : 
    2586           0 :     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    2587           0 :     gle::color(outlinecolor);
    2588             : 
    2589           0 :     enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
    2590             : 
    2591           0 :     if(!dtoutline)
    2592             :     {
    2593           0 :         glDisable(GL_DEPTH_TEST);
    2594             :     }
    2595           0 :     vtxarray *prev = nullptr;
    2596           0 :     for(vtxarray *va = visibleva; va; va = va->next)
    2597             :     {
    2598           0 :         if(va->occluded < Occlude_BB)
    2599             :         {
    2600           0 :             if((!va->texs || va->occluded >= Occlude_Geom) && !va->alphaback && !va->alphafront && !va->refracttris)
    2601             :             {
    2602           0 :                 continue;
    2603             :             }
    2604           0 :             if(!prev || va->vbuf != prev->vbuf)
    2605             :             {
    2606           0 :                 gle::bindvbo(va->vbuf);
    2607           0 :                 gle::bindebo(va->ebuf);
    2608           0 :                 const vertex *ptr = 0;
    2609           0 :                 gle::vertexpointer(sizeof(vertex), ptr->pos.data());
    2610             :             }
    2611           0 :             if(va->texs && va->occluded < Occlude_Geom)
    2612             :             {
    2613           0 :                 drawvatris(*va, 3*va->tris, 0);
    2614           0 :                 xtravertsva += va->verts;
    2615             :             }
    2616           0 :             if(va->alphaback || va->alphafront || va->refract)
    2617             :             {
    2618           0 :                 drawvatris(*va, 3*(va->alphabacktris + va->alphafronttris + va->refracttris), 3*(va->tris));
    2619           0 :                 xtravertsva += 3*(va->alphabacktris + va->alphafronttris + va->refracttris);
    2620             :             }
    2621           0 :             prev = va;
    2622             :         }
    2623             :     }
    2624           0 :     if(!dtoutline)
    2625             :     {
    2626           0 :         glEnable(GL_DEPTH_TEST);
    2627             :     }
    2628           0 :     disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
    2629           0 :     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    2630           0 :     gle::clearvbo();
    2631           0 :     gle::clearebo();
    2632           0 :     gle::disablevertex();
    2633           0 : }
    2634             : 
    2635           0 : bool renderexplicitsky(bool outline)
    2636             : {
    2637           0 :     vtxarray *prev = nullptr;
    2638           0 :     for(vtxarray *va = visibleva; va; va = va->next)
    2639             :     {
    2640           0 :         if(va->sky && va->occluded < Occlude_BB &&
    2641           0 :             ((va->skymax.x >= 0 && view.isvisiblebb(va->skymin, ivec(va->skymax).sub(va->skymin)) != ViewFrustumCull_NotVisible) ||
    2642           0 :             !insideworld(camera1->o)))
    2643             :         {
    2644           0 :             if(!prev || va->vbuf != prev->vbuf)
    2645             :             {
    2646           0 :                 if(!prev)
    2647             :                 {
    2648           0 :                     gle::enablevertex();
    2649           0 :                     if(outline)
    2650             :                     {
    2651           0 :                         ldrnotextureshader->set();
    2652           0 :                         gle::color(explicitskycolor);
    2653           0 :                         glDepthMask(GL_FALSE);
    2654           0 :                         enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
    2655           0 :                         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    2656             :                     }
    2657           0 :                     else if(editmode)
    2658             :                     {
    2659           0 :                         maskgbuffer("d");
    2660           0 :                         SETSHADER(depth,);
    2661             :                     }
    2662             :                     else
    2663             :                     {
    2664           0 :                         nocolorshader->set();
    2665           0 :                         glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
    2666             :                     }
    2667             :                 }
    2668           0 :                 gle::bindvbo(va->vbuf);
    2669           0 :                 gle::bindebo(va->skybuf);
    2670           0 :                 const vertex *ptr = 0;
    2671           0 :                 gle::vertexpointer(sizeof(vertex), ptr->pos.data());
    2672             :             }
    2673           0 :             drawvaskytris(*va);
    2674           0 :             xtraverts += va->sky;
    2675           0 :             prev = va;
    2676             :         }
    2677             :     }
    2678           0 :     if(!prev)
    2679             :     {
    2680           0 :         return false;
    2681             :     }
    2682           0 :     if(outline)
    2683             :     {
    2684           0 :         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    2685           0 :         disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
    2686           0 :         glDepthMask(GL_TRUE);
    2687             :     }
    2688           0 :     else if(editmode)
    2689             :     {
    2690           0 :         maskgbuffer("cnd");
    2691             :     }
    2692             :     else
    2693             :     {
    2694           0 :         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    2695             :     }
    2696           0 :     gle::disablevertex();
    2697           0 :     gle::clearvbo();
    2698           0 :     gle::clearebo();
    2699           0 :     return true;
    2700             : }
    2701             : 
    2702           0 : void cubeworld::cleanupva()
    2703             : {
    2704           0 :     clearvas(*worldroot);
    2705           0 :     occlusionengine.clearqueries();
    2706           0 :     cleanupbb();
    2707           0 :     cleanupgrass();
    2708           0 : }
    2709             : 
    2710           0 : GBuffer::AlphaInfo GBuffer::findalphavas()
    2711             : {
    2712           0 :     alphavas.clear();
    2713             :     AlphaInfo a;
    2714           0 :     a.alphafrontsx1 = a.alphafrontsy1 = a.alphabacksx1 = a.alphabacksy1 = a.alpharefractsx1 = a.alpharefractsy1 = 1;
    2715           0 :     a.alphafrontsx2 = a.alphafrontsy2 = a.alphabacksx2 = a.alphabacksy2 = a.alpharefractsx2 = a.alpharefractsy2 = -1;
    2716           0 :     int alphabackvas = 0,
    2717           0 :         alpharefractvas = 0;
    2718           0 :     std::memset(alphatiles, 0, sizeof(alphatiles));
    2719           0 :     for(vtxarray *va = visibleva; va; va = va->next)
    2720             :     {
    2721           0 :         if(va->alphabacktris || va->alphafronttris || va->refracttris)
    2722             :         {
    2723           0 :             if(va->occluded >= Occlude_BB)
    2724             :             {
    2725           0 :                 continue;
    2726             :             }
    2727           0 :             if(va->curvfc==ViewFrustumCull_Fogged)
    2728             :             {
    2729           0 :                 continue;
    2730             :             }
    2731           0 :             float sx1 = -1,
    2732           0 :                   sx2 =  1,
    2733           0 :                   sy1 = -1,
    2734           0 :                   sy2 =  1;
    2735           0 :             if(!calcbbscissor(va->alphamin, va->alphamax, sx1, sy1, sx2, sy2))
    2736             :             {
    2737           0 :                 continue;
    2738             :             }
    2739           0 :             alphavas.push_back(va);
    2740           0 :             masktiles(alphatiles, sx1, sy1, sx2, sy2);
    2741           0 :             a.alphafrontsx1 = std::min(a.alphafrontsx1, sx1);
    2742           0 :             a.alphafrontsy1 = std::min(a.alphafrontsy1, sy1);
    2743           0 :             a.alphafrontsx2 = std::max(a.alphafrontsx2, sx2);
    2744           0 :             a.alphafrontsy2 = std::max(a.alphafrontsy2, sy2);
    2745           0 :             if(va->alphabacktris)
    2746             :             {
    2747           0 :                 alphabackvas++;
    2748           0 :                 a.alphabacksx1 = std::min(a.alphabacksx1, sx1);
    2749           0 :                 a.alphabacksy1 = std::min(a.alphabacksy1, sy1);
    2750           0 :                 a.alphabacksx2 = std::max(a.alphabacksx2, sx2);
    2751           0 :                 a.alphabacksy2 = std::max(a.alphabacksy2, sy2);
    2752             :             }
    2753           0 :             if(va->refracttris)
    2754             :             {
    2755           0 :                 if(!calcbbscissor(va->refractmin, va->refractmax, sx1, sy1, sx2, sy2))
    2756             :                 {
    2757           0 :                     continue;
    2758             :                 }
    2759           0 :                 alpharefractvas++;
    2760           0 :                 a.alpharefractsx1 = std::min(a.alpharefractsx1, sx1);
    2761           0 :                 a.alpharefractsy1 = std::min(a.alpharefractsy1, sy1);
    2762           0 :                 a.alpharefractsx2 = std::max(a.alpharefractsx2, sx2);
    2763           0 :                 a.alpharefractsy2 = std::max(a.alpharefractsy2, sy2);
    2764             :             }
    2765             :         }
    2766             :     }
    2767           0 :     a.hasalphavas = (alpharefractvas ? 4 : 0) | (alphavas.size() ? 2 : 0) | (alphabackvas ? 1 : 0);
    2768           0 :     return a;
    2769             : }
    2770             : 
    2771           0 : void renderrefractmask()
    2772             : {
    2773           0 :     gle::enablevertex();
    2774             : 
    2775           0 :     const vtxarray *prev = nullptr;
    2776           0 :     for(const vtxarray *va : alphavas)
    2777             :     {
    2778           0 :         if(!va->refracttris)
    2779             :         {
    2780           0 :             continue;
    2781             :         }
    2782           0 :         if(!prev || va->vbuf != prev->vbuf)
    2783             :         {
    2784           0 :             gle::bindvbo(va->vbuf);
    2785           0 :             gle::bindebo(va->ebuf);
    2786           0 :             const vertex *ptr = 0;
    2787           0 :             gle::vertexpointer(sizeof(vertex), ptr->pos.data());
    2788             :         }
    2789           0 :         drawvatris(*va, 3*va->refracttris, 3*(va->tris + va->alphabacktris + va->alphafronttris));
    2790           0 :         xtravertsva += 3*va->refracttris;
    2791           0 :         prev = va;
    2792             :     }
    2793             : 
    2794           0 :     gle::clearvbo();
    2795           0 :     gle::clearebo();
    2796           0 :     gle::disablevertex();
    2797           0 : }
    2798             : 
    2799           0 : void renderalphageom(int side)
    2800             : {
    2801           0 :     resetbatches();
    2802             : 
    2803           0 :     renderstate cur;
    2804           0 :     cur.alphaing = side;
    2805           0 :     cur.invalidatealphascale();
    2806             : 
    2807           0 :     setupgeom();
    2808             : 
    2809           0 :     if(side == 2)
    2810             :     {
    2811           0 :         for(const vtxarray *i : alphavas)
    2812             :         {
    2813           0 :             renderva(cur, *i, RenderPass_GBuffer);
    2814             :         }
    2815           0 :         if(geombatches.size())
    2816             :         {
    2817           0 :             cur.renderbatches(RenderPass_GBuffer);
    2818             :         }
    2819             :     }
    2820             :     else
    2821             :     {
    2822           0 :         glCullFace(GL_FRONT);
    2823           0 :         for(const vtxarray *i : alphavas)
    2824             :         {
    2825           0 :             if(i->alphabacktris)
    2826             :             {
    2827           0 :                 renderva(cur, *i, RenderPass_GBuffer);
    2828             :             }
    2829             :         }
    2830           0 :         if(geombatches.size())
    2831             :         {
    2832           0 :             cur.renderbatches(RenderPass_GBuffer);
    2833             :         }
    2834           0 :         glCullFace(GL_BACK);
    2835             :     }
    2836             : 
    2837           0 :     cur.cleanupgeom();
    2838           0 : }
    2839             : 
    2840           0 : void GBuffer::rendergeom()
    2841             : {
    2842           0 :     bool doOQ = oqfrags && oqgeom && !drawtex,
    2843           0 :          multipassing = false;
    2844           0 :     renderstate cur;
    2845             : 
    2846           0 :     if(doOQ)
    2847             :     {
    2848           0 :         for(vtxarray *va = visibleva; va; va = va->next)
    2849             :         {
    2850           0 :             if(va->texs)
    2851             :             {
    2852           0 :                 if(!camera1->o.insidebb(va->o, va->size, 2))
    2853             :                 {
    2854           0 :                     if(va->parent && va->parent->occluded >= Occlude_BB)
    2855             :                     {
    2856           0 :                         va->query = nullptr;
    2857           0 :                         va->occluded = Occlude_Parent;
    2858           0 :                         continue;
    2859             :                     }
    2860           0 :                     va->occluded = va->query && va->query->owner == va && occlusionengine.checkquery(va->query) ?
    2861           0 :                                    std::min(va->occluded+1, static_cast<int>(Occlude_BB)) :
    2862             :                                    Occlude_Nothing;
    2863           0 :                     va->query = occlusionengine.newquery(va);
    2864           0 :                     if(!va->query || !va->occluded)
    2865             :                     {
    2866           0 :                         va->occluded = Occlude_Nothing;
    2867             :                     }
    2868           0 :                     if(va->occluded >= Occlude_Geom)
    2869             :                     {
    2870           0 :                         if(va->query)
    2871             :                         {
    2872           0 :                             if(cur.vattribs)
    2873             :                             {
    2874           0 :                                 cur.disablevattribs(false);
    2875             :                             }
    2876           0 :                             if(cur.vbuf)
    2877             :                             {
    2878           0 :                                 cur.disablevbuf();
    2879             :                             }
    2880           0 :                             renderquery(cur, *va->query, *va);
    2881             :                         }
    2882           0 :                         continue;
    2883             :                     }
    2884             :                 }
    2885             :                 else
    2886             :                 {
    2887           0 :                     va->query = nullptr;
    2888           0 :                     va->occluded = Occlude_Nothing;
    2889           0 :                     if(va->occluded >= Occlude_Geom)
    2890             :                     {
    2891           0 :                         continue;
    2892             :                     }
    2893             :                 }
    2894           0 :                 renderva(cur, *va, RenderPass_Z, true);
    2895             :             }
    2896             :         }
    2897             : 
    2898           0 :         if(cur.vquery)
    2899             :         {
    2900           0 :             cur.disablevquery();
    2901             :         }
    2902           0 :         if(cur.vattribs)
    2903             :         {
    2904           0 :             cur.disablevattribs(false);
    2905             :         }
    2906           0 :         if(cur.vbuf)
    2907             :         {
    2908           0 :             cur.disablevbuf();
    2909             :         }
    2910           0 :         glFlush();
    2911           0 :         if(cur.colormask)
    2912             :         {
    2913           0 :             cur.colormask = false;
    2914           0 :             glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
    2915             :         }
    2916           0 :         if(cur.depthmask)
    2917             :         {
    2918           0 :             cur.depthmask = false;
    2919           0 :             glDepthMask(GL_FALSE);
    2920             :         }
    2921           0 :         workinoq();
    2922           0 :         if(!cur.colormask)
    2923             :         {
    2924           0 :             cur.colormask = true;
    2925           0 :             glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    2926             :         }
    2927           0 :         if(!cur.depthmask)
    2928             :         {
    2929           0 :             cur.depthmask = true;
    2930           0 :             glDepthMask(GL_TRUE);
    2931             :         }
    2932           0 :         if(!multipassing)
    2933             :         {
    2934           0 :             multipassing = true;
    2935           0 :             glDepthFunc(GL_LEQUAL);
    2936             :         }
    2937           0 :         cur.invalidatetexgenorient();
    2938           0 :         setupgeom();
    2939           0 :         resetbatches();
    2940           0 :         for(vtxarray *va = visibleva; va; va = va->next)
    2941             :         {
    2942           0 :             if(va->texs && va->occluded < Occlude_Geom)
    2943             :             {
    2944           0 :                 renderva(cur, *va, RenderPass_GBuffer);
    2945             :             }
    2946             :         }
    2947           0 :         if(geombatches.size())
    2948             :         {
    2949           0 :             cur.renderbatches(RenderPass_GBuffer);
    2950           0 :             glFlush();
    2951             :         }
    2952           0 :         for(vtxarray *va = visibleva; va; va = va->next)
    2953             :         {
    2954           0 :             if(va->texs && va->occluded >= Occlude_Geom)
    2955             :             {
    2956           0 :                 if((va->parent && va->parent->occluded >= Occlude_BB) || (va->query && occlusionengine.checkquery(va->query)))
    2957             :                 {
    2958           0 :                     va->occluded = Occlude_BB;
    2959           0 :                     continue;
    2960             :                 }
    2961             :                 else
    2962             :                 {
    2963           0 :                     va->occluded = Occlude_Nothing;
    2964           0 :                     if(va->occluded >= Occlude_Geom)
    2965             :                     {
    2966           0 :                         continue;
    2967             :                     }
    2968             :                 }
    2969             : 
    2970           0 :                 renderva(cur, *va, RenderPass_GBuffer);
    2971             :             }
    2972             :         }
    2973           0 :         if(geombatches.size())
    2974             :         {
    2975           0 :             cur.renderbatches(RenderPass_GBuffer);
    2976             :         }
    2977             :     }
    2978             :     else
    2979             :     {
    2980           0 :         setupgeom();
    2981           0 :         resetbatches();
    2982           0 :         for(vtxarray *va = visibleva; va; va = va->next)
    2983             :         {
    2984           0 :             if(va->texs)
    2985             :             {
    2986           0 :                 va->query = nullptr;
    2987           0 :                 va->occluded = Occlude_Nothing;
    2988           0 :                 if(va->occluded >= Occlude_Geom)
    2989             :                 {
    2990           0 :                     continue;
    2991             :                 }
    2992           0 :                 renderva(cur, *va, RenderPass_GBuffer);
    2993             :             }
    2994             :         }
    2995           0 :         if(geombatches.size())
    2996             :         {
    2997           0 :             cur.renderbatches(RenderPass_GBuffer);
    2998             :         }
    2999             :     }
    3000           0 :     if(multipassing)
    3001             :     {
    3002           0 :         glDepthFunc(GL_LESS);
    3003             :     }
    3004           0 :     cur.cleanupgeom();
    3005           0 :     if(!doOQ)
    3006             :     {
    3007           0 :         glFlush();
    3008           0 :         if(cur.colormask)
    3009             :         {
    3010           0 :             cur.colormask = false;
    3011           0 :             glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
    3012             :         }
    3013           0 :         if(cur.depthmask)
    3014             :         {
    3015           0 :             cur.depthmask = false;
    3016           0 :             glDepthMask(GL_FALSE);
    3017             :         }
    3018           0 :         workinoq();
    3019           0 :         if(!cur.colormask)
    3020             :         {
    3021           0 :             cur.colormask = true;
    3022           0 :             glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    3023             :         }
    3024           0 :         if(!cur.depthmask)
    3025             :         {
    3026           0 :             cur.depthmask = true;
    3027           0 :             glDepthMask(GL_TRUE);
    3028             :         }
    3029             :     }
    3030           0 : }
    3031             : 
    3032           0 : void renderdecals()
    3033             : {
    3034             :     vtxarray *decalva;
    3035           0 :     for(decalva = visibleva; decalva; decalva = decalva->next)
    3036             :     {
    3037           0 :         if(decalva->decaltris && decalva->occluded < Occlude_BB)
    3038             :         {
    3039           0 :             break;
    3040             :         }
    3041             :     }
    3042           0 :     if(!decalva)
    3043             :     {
    3044           0 :         return;
    3045             :     }
    3046           0 :     decalrenderer cur;
    3047             : 
    3048           0 :     setupdecals();
    3049           0 :     resetdecalbatches();
    3050             : 
    3051           0 :     if(maxdualdrawbufs)
    3052             :     {
    3053           0 :         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC1_ALPHA);
    3054           0 :         maskgbuffer("c");
    3055           0 :         for(vtxarray *va = decalva; va; va = va->next)
    3056             :         {
    3057           0 :             if(va->decaltris && va->occluded < Occlude_BB)
    3058             :             {
    3059           0 :                 mergedecals(*va);
    3060           0 :                 if(!batchdecals && decalbatches.size())
    3061             :                 {
    3062           0 :                     cur.renderdecalbatches(0);
    3063             :                 }
    3064             :             }
    3065             :         }
    3066           0 :         if(decalbatches.size())
    3067             :         {
    3068           0 :             cur.renderdecalbatches(0);
    3069             :         }
    3070           0 :         if(usepacknorm())
    3071             :         {
    3072           0 :             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    3073           0 :             glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
    3074             :         }
    3075             :         else
    3076             :         {
    3077           0 :             glBlendFunc(GL_SRC1_ALPHA, GL_ONE_MINUS_SRC1_ALPHA);
    3078             :         }
    3079           0 :         maskgbuffer("n");
    3080           0 :         cur.vbuf = 0;
    3081           0 :         for(vtxarray *va = decalva; va; va = va->next)
    3082             :         {
    3083           0 :             if(va->decaltris && va->occluded < Occlude_BB)
    3084             :             {
    3085           0 :                 mergedecals(*va);
    3086           0 :                 if(!batchdecals && decalbatches.size())
    3087             :                 {
    3088           0 :                     cur.renderdecalbatches(1);
    3089             :                 }
    3090             :             }
    3091             :         }
    3092           0 :         if(decalbatches.size())
    3093             :         {
    3094           0 :             cur.renderdecalbatches(1);
    3095             :         }
    3096             :     }
    3097             :     else
    3098             :     {
    3099           0 :         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    3100           0 :         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
    3101           0 :         maskgbuffer("cn");
    3102           0 :         for(vtxarray *va = decalva; va; va = va->next)
    3103             :         {
    3104           0 :             if(va->decaltris && va->occluded < Occlude_BB)
    3105             :             {
    3106           0 :                 mergedecals(*va);
    3107           0 :                 if(!batchdecals && decalbatches.size())
    3108             :                 {
    3109           0 :                     cur.renderdecalbatches(0);
    3110             :                 }
    3111             :             }
    3112             :         }
    3113           0 :         if(decalbatches.size())
    3114             :         {
    3115           0 :             cur.renderdecalbatches(0);
    3116             :         }
    3117             :     }
    3118           0 :     cleanupdecals();
    3119             : }
    3120             : 
    3121             : //shadowmeshes
    3122             : 
    3123           1 : void clearshadowmeshes()
    3124             : {
    3125           1 :     if(shadowvbos.size())
    3126             :     {
    3127           0 :         glDeleteBuffers(shadowvbos.size(), shadowvbos.data());
    3128           0 :         shadowvbos.clear();
    3129             :     }
    3130           1 :     if(shadowmeshes.size())
    3131             :     {
    3132           0 :         std::vector<extentity *> &ents = entities::getents();
    3133           0 :         for(uint i = 0; i < ents.size(); i++)
    3134             :         {
    3135           0 :             extentity &e = *ents[i];
    3136           0 :             if(e.flags&EntFlag_ShadowMesh)
    3137             :             {
    3138           0 :                 e.flags &= ~EntFlag_ShadowMesh;
    3139             :             }
    3140             :         }
    3141             :     }
    3142           1 :     shadowmeshes.clear();
    3143           1 :     shadowdraws.clear();
    3144           1 : }
    3145             : 
    3146           0 : void genshadowmeshes()
    3147             : {
    3148           0 :     clearshadowmeshes();
    3149             : 
    3150           0 :     if(!smmesh)
    3151             :     {
    3152           0 :         return;
    3153             :     }
    3154           0 :     renderprogress(0, "generating shadow meshes..");
    3155             : 
    3156           0 :     std::vector<extentity *> &ents = entities::getents();
    3157           0 :     for(uint i = 0; i < ents.size(); i++)
    3158             :     {
    3159           0 :         extentity &e = *ents[i];
    3160           0 :         if(e.type != EngineEnt_Light)
    3161             :         {
    3162           0 :             continue;
    3163             :         }
    3164           0 :         genshadowmesh(i, e);
    3165             :     }
    3166             : }
    3167             : 
    3168           0 : shadowmesh *findshadowmesh(int idx, const extentity &e)
    3169             : {
    3170           0 :     auto itr = shadowmeshes.find(idx);
    3171           0 :     if(itr == shadowmeshes.end()
    3172           0 :     || (*itr).second.type != shadowmapping
    3173           0 :     || (*itr).second.origin != shadoworigin
    3174           0 :     || (*itr).second.radius < shadowradius)
    3175             :     {
    3176           0 :         return nullptr;
    3177             :     }
    3178           0 :     switch((*itr).second.type)
    3179             :     {
    3180           0 :         case ShadowMap_Spot:
    3181             :         {
    3182           0 :             if(!e.attached || e.attached->type != EngineEnt_Spotlight || (*itr).second.spotloc != e.attached->o || (*itr).second.spotangle < std::clamp(static_cast<int>(e.attached->attr1), 1, 89))
    3183             :             {
    3184           0 :                 return nullptr;
    3185             :             }
    3186           0 :             break;
    3187             :         }
    3188             :     }
    3189           0 :     return &(*itr).second;
    3190             : }
    3191             : 
    3192           0 : void rendershadowmesh(const shadowmesh *m)
    3193             : {
    3194           0 :     int draw = m->draws[shadowside];
    3195           0 :     if(draw < 0)
    3196             :     {
    3197           0 :         return;
    3198             :     }
    3199           0 :     SETSHADER(shadowmapworld,);
    3200           0 :     gle::enablevertex();
    3201           0 :     GLuint ebuf = 0,
    3202           0 :            vbuf = 0;
    3203           0 :     while(draw >= 0)
    3204             :     {
    3205           0 :         shadowdraw &d = shadowdraws[draw];
    3206           0 :         if(ebuf != d.ebuf)
    3207             :         {
    3208           0 :             gle::bindebo(d.ebuf);
    3209           0 :             ebuf = d.ebuf;
    3210             :         }
    3211           0 :         if(vbuf != d.vbuf)
    3212             :         {
    3213           0 :             gle::bindvbo(d.vbuf);
    3214           0 :             vbuf = d.vbuf; gle::vertexpointer(sizeof(vec), 0);
    3215             :         }
    3216           0 :         drawtris(3*d.tris, (ushort *)0 + d.offset, d.minvert, d.maxvert);
    3217           0 :         xtravertsva += 3*d.tris;
    3218           0 :         draw = d.next;
    3219             :     }
    3220           0 :     gle::disablevertex();
    3221           0 :     gle::clearebo();
    3222           0 :     gle::clearvbo();
    3223             : }
    3224             : 
    3225             : //external api functions
    3226           0 : int calcspheresidemask(const vec &p, float radius, float bias)
    3227             : {
    3228             :     // p is in the cubemap's local coordinate system
    3229             :     // bias = border/(size - border)
    3230           0 :     float dxyp = p.x + p.y,
    3231           0 :           dxyn = p.x - p.y,
    3232           0 :           axyp = std::fabs(dxyp),
    3233           0 :           axyn = std::fabs(dxyn),
    3234           0 :           dyzp = p.y + p.z,
    3235           0 :           dyzn = p.y - p.z,
    3236           0 :           ayzp = std::fabs(dyzp),
    3237           0 :           ayzn = std::fabs(dyzn),
    3238           0 :           dzxp = p.z + p.x,
    3239           0 :           dzxn = p.z - p.x,
    3240           0 :           azxp = std::fabs(dzxp),
    3241           0 :           azxn = std::fabs(dzxn);
    3242           0 :     int mask = 0x3F;
    3243           0 :     radius *= SQRT2;
    3244           0 :     if(axyp > bias*axyn + radius)
    3245             :     {
    3246           0 :         mask &= dxyp < 0 ? ~((1<<0)|(1<<2)) : ~((2<<0)|(2<<2));
    3247             :     }
    3248           0 :     if(axyn > bias*axyp + radius)
    3249             :     {
    3250           0 :         mask &= dxyn < 0 ? ~((1<<0)|(2<<2)) : ~((2<<0)|(1<<2));
    3251             :     }
    3252           0 :     if(ayzp > bias*ayzn + radius)
    3253             :     {
    3254           0 :         mask &= dyzp < 0 ? ~((1<<2)|(1<<4)) : ~((2<<2)|(2<<4));
    3255             :     }
    3256           0 :     if(ayzn > bias*ayzp + radius)
    3257             :     {
    3258           0 :         mask &= dyzn < 0 ? ~((1<<2)|(2<<4)) : ~((2<<2)|(1<<4));
    3259             :     }
    3260           0 :     if(azxp > bias*azxn + radius)
    3261             :     {
    3262           0 :         mask &= dzxp < 0 ? ~((1<<4)|(1<<0)) : ~((2<<4)|(2<<0));
    3263             :     }
    3264           0 :     if(azxn > bias*azxp + radius)
    3265             :     {
    3266           0 :         mask &= dzxn < 0 ? ~((1<<4)|(2<<0)) : ~((2<<4)|(1<<0));
    3267             :     }
    3268           0 :     return mask;
    3269             : }
    3270             : 
    3271           0 : int vfc::cullfrustumsides(const vec &lightpos, float lightradius, float size, float border)
    3272             : {
    3273           0 :     int sides = 0x3F,
    3274           0 :         masks[6] = { 3<<4, 3<<4, 3<<0, 3<<0, 3<<2, 3<<2 };
    3275           0 :     float scale = (size - 2*border)/size,
    3276           0 :           bias = border / static_cast<float>(size - border);
    3277             :     // check if cone enclosing side would cross frustum plane
    3278           0 :     scale = 2 / (scale*scale + 2);
    3279           0 :     for(int i = 0; i < 5; ++i)
    3280             :     {
    3281           0 :         if(vfcP[i].dist(lightpos) <= -0.03125f)
    3282             :         {
    3283           0 :             vec n = vec(vfcP[i]).div(lightradius);
    3284           0 :             float len = scale*n.squaredlen();
    3285           0 :             if(n.x*n.x > len)
    3286             :             {
    3287           0 :                 sides &= n.x < 0 ? ~(1<<0) : ~(2 << 0);
    3288             :             }
    3289           0 :             if(n.y*n.y > len)
    3290             :             {
    3291           0 :                 sides &= n.y < 0 ? ~(1<<2) : ~(2 << 2);
    3292             :             }
    3293           0 :             if(n.z*n.z > len)
    3294             :             {
    3295           0 :                 sides &= n.z < 0 ? ~(1<<4) : ~(2 << 4);
    3296             :             }
    3297             :         }
    3298             :     }
    3299           0 :     if (vfcP[4].dist(lightpos) >= vfcDfog + 0.03125f)
    3300             :     {
    3301           0 :         vec n = vec(vfcP[4]).div(lightradius);
    3302           0 :         float len = scale*n.squaredlen();
    3303           0 :         if(n.x*n.x > len)
    3304             :         {
    3305           0 :             sides &= n.x >= 0 ? ~(1<<0) : ~(2 << 0);
    3306             :         }
    3307           0 :         if(n.y*n.y > len)
    3308             :         {
    3309           0 :             sides &= n.y >= 0 ? ~(1<<2) : ~(2 << 2);
    3310             :         }
    3311           0 :         if(n.z*n.z > len)
    3312             :         {
    3313           0 :             sides &= n.z >= 0 ? ~(1<<4) : ~(2 << 4);
    3314             :         }
    3315             :     }
    3316             :     // this next test usually clips off more sides than the former, but occasionally clips fewer/different ones, so do both and combine results
    3317             :     // check if frustum corners/origin cross plane sides
    3318             :     // infinite version, assumes frustum corners merely give direction and extend to infinite distance
    3319           0 :     vec p = vec(camera1->o).sub(lightpos).div(lightradius);
    3320           0 :     float dp = p.x + p.y,
    3321           0 :           dn = p.x - p.y,
    3322           0 :           ap = std::fabs(dp),
    3323           0 :           an = std::fabs(dn);
    3324           0 :     masks[0] |= ap <= bias*an ? 0x3F : (dp >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2));
    3325           0 :     masks[1] |= an <= bias*ap ? 0x3F : (dn >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2));
    3326           0 :     dp = p.y + p.z, dn = p.y - p.z,
    3327           0 :     ap = std::fabs(dp),
    3328           0 :     an = std::fabs(dn);
    3329           0 :     masks[2] |= ap <= bias*an ? 0x3F : (dp >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4));
    3330           0 :     masks[3] |= an <= bias*ap ? 0x3F : (dn >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4));
    3331           0 :     dp = p.z + p.x,
    3332           0 :     dn = p.z - p.x,
    3333           0 :     ap = std::fabs(dp),
    3334           0 :     an = std::fabs(dn);
    3335           0 :     masks[4] |= ap <= bias*an ? 0x3F : (dp >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0));
    3336           0 :     masks[5] |= an <= bias*ap ? 0x3F : (dn >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0));
    3337           0 :     for(int i = 0; i < 4; ++i)
    3338             :     {
    3339           0 :         vec n;
    3340           0 :         switch(i)
    3341             :         {
    3342           0 :             case 0:
    3343             :             {
    3344           0 :                 n.cross(vfcP[0], vfcP[2]);
    3345           0 :                 break;
    3346             :             }
    3347           0 :             case 1:
    3348             :             {
    3349           0 :                 n.cross(vfcP[3], vfcP[0]);
    3350           0 :                 break;
    3351             :             }
    3352           0 :             case 2:
    3353             :             {
    3354           0 :                 n.cross(vfcP[2], vfcP[1]);
    3355           0 :                 break;
    3356             :             }
    3357           0 :             case 3:
    3358             :             {
    3359           0 :                 n.cross(vfcP[1], vfcP[3]);
    3360           0 :                 break;
    3361             :             }
    3362             :         }
    3363           0 :         dp = n.x + n.y,
    3364           0 :         dn = n.x - n.y,
    3365           0 :         ap = std::fabs(dp),
    3366           0 :         an = std::fabs(dn);
    3367           0 :         if(ap > 0)
    3368             :         {
    3369           0 :             masks[0] |= dp >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2);
    3370             :         }
    3371           0 :         if(an > 0)
    3372             :         {
    3373           0 :             masks[1] |= dn >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2);
    3374             :         }
    3375           0 :         dp = n.y + n.z,
    3376           0 :         dn = n.y - n.z,
    3377           0 :         ap = std::fabs(dp),
    3378           0 :         an = std::fabs(dn);
    3379           0 :         if(ap > 0)
    3380             :         {
    3381           0 :             masks[2] |= dp >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4);
    3382             :         }
    3383           0 :         if(an > 0)
    3384             :         {
    3385           0 :             masks[3] |= dn >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4);
    3386             :         }
    3387           0 :         dp = n.z + n.x,
    3388           0 :         dn = n.z - n.x,
    3389           0 :         ap = std::fabs(dp),
    3390           0 :         an = std::fabs(dn);
    3391           0 :         if(ap > 0)
    3392             :         {
    3393           0 :             masks[4] |= dp >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0);
    3394             :         }
    3395           0 :         if(an > 0)
    3396             :         {
    3397           0 :             masks[5] |= dn >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0);
    3398             :         }
    3399             :     }
    3400           0 :     return sides & masks[0] & masks[1] & masks[2] & masks[3] & masks[4] & masks[5];
    3401             : }
    3402             : 
    3403           0 : static void findshadowvas(std::vector<vtxarray *> &vas, std::array<vtxarray *, vasortsize> &vasort)
    3404             : {
    3405           0 :     for(vtxarray *&v : vas)
    3406             :     {
    3407           0 :         float dist = vadist(*v, shadoworigin);
    3408           0 :         if(dist < shadowradius || !smdistcull)
    3409             :         {
    3410           0 :             v->shadowmask = !smbbcull ? 0x3F : (v->children.size() || v->mapmodels.size() ?
    3411           0 :                                 calcbbsidemask(v->bbmin, v->bbmax, shadoworigin, shadowradius, shadowbias) :
    3412           0 :                                 calcbbsidemask(v->geommin, v->geommax, shadoworigin, shadowradius, shadowbias));
    3413           0 :             addshadowva(v, dist, vasort);
    3414           0 :             if(v->children.size())
    3415             :             {
    3416           0 :                 findshadowvas(v->children, vasort);
    3417             :             }
    3418             :         }
    3419             :     }
    3420           0 : }
    3421             : 
    3422           0 : void renderrsmgeom(bool dyntex)
    3423             : {
    3424           0 :     renderstate cur;
    3425           0 :     if(!dyntex)
    3426             :     {
    3427           0 :         cur.cleartexgenmillis();
    3428             :     }
    3429           0 :     setupgeom();
    3430           0 :     if(skyshadow)
    3431             :     {
    3432           0 :         cur.enablevattribs(false);
    3433           0 :         SETSHADER(rsmsky,);
    3434           0 :         vtxarray *prev = nullptr;
    3435           0 :         for(vtxarray *va = shadowva; va; va = va->rnext)
    3436             :         {
    3437           0 :             if(va->sky)
    3438             :             {
    3439           0 :                 if(!prev || va->vbuf != prev->vbuf)
    3440             :                 {
    3441           0 :                     gle::bindvbo(va->vbuf);
    3442           0 :                     gle::bindebo(va->skybuf);
    3443           0 :                     const vertex *ptr = nullptr; //note: offset of nullptr is technically UB
    3444           0 :                     gle::vertexpointer(sizeof(vertex), ptr->pos.data());
    3445             :                 }
    3446           0 :                 drawvaskytris(*va);
    3447           0 :                 xtravertsva += va->sky/3;
    3448           0 :                 prev = va;
    3449             :             }
    3450             :         }
    3451           0 :         if(cur.vattribs)
    3452             :         {
    3453           0 :             cur.disablevattribs(false);
    3454             :         }
    3455             :     }
    3456           0 :     resetbatches();
    3457           0 :     for(vtxarray *va = shadowva; va; va = va->rnext)
    3458             :     {
    3459           0 :         if(va->texs)
    3460             :         {
    3461           0 :             renderva(cur, *va, RenderPass_ReflectiveShadowMap);
    3462             :         }
    3463             :     }
    3464           0 :     if(geombatches.size())
    3465             :     {
    3466           0 :         cur.renderbatches(RenderPass_ReflectiveShadowMap);
    3467             :     }
    3468           0 :     cur.cleanupgeom();
    3469           0 : }
    3470             : 
    3471           0 : void dynamicshadowvabounds(int mask, vec &bbmin, vec &bbmax)
    3472             : {
    3473           0 :     for(vtxarray *va = shadowva; va; va = va->rnext)
    3474             :     {
    3475           0 :         if(va->shadowmask&mask && va->dyntexs)
    3476             :         {
    3477           0 :             bbmin.min(vec(va->geommin));
    3478           0 :             bbmax.max(vec(va->geommax));
    3479             :         }
    3480             :     }
    3481           0 : }
    3482             : 
    3483           0 : void findshadowmms()
    3484             : {
    3485           0 :     shadowmms = nullptr;
    3486           0 :     octaentities **lastmms = &shadowmms;
    3487           0 :     for(vtxarray *va = shadowva; va; va = va->rnext)
    3488             :     {
    3489           0 :         for(uint j = 0; j < va->mapmodels.size(); j++)
    3490             :         {
    3491           0 :             octaentities *oe = va->mapmodels[j];
    3492           0 :             switch(shadowmapping)
    3493             :             {
    3494           0 :                 case ShadowMap_Reflect:
    3495             :                 {
    3496           0 :                     break;
    3497             :                 }
    3498           0 :                 case ShadowMap_Cascade:
    3499             :                 {
    3500           0 :                     if(!csm.calcbbcsmsplits(oe->bbmin, oe->bbmax))
    3501             :                     {
    3502           0 :                         continue;
    3503             :                     }
    3504           0 :                     break;
    3505             :                 }
    3506           0 :                 case ShadowMap_CubeMap:
    3507             :                 {
    3508           0 :                     if(smdistcull && shadoworigin.dist_to_bb(oe->bbmin, oe->bbmax) >= shadowradius)
    3509             :                     {
    3510           0 :                         continue;
    3511             :                     }
    3512           0 :                     break;
    3513             :                 }
    3514           0 :                 case ShadowMap_Spot:
    3515             :                 {
    3516           0 :                     if(smdistcull && shadoworigin.dist_to_bb(oe->bbmin, oe->bbmax) >= shadowradius)
    3517             :                     {
    3518           0 :                         continue;
    3519             :                     }
    3520           0 :                     if(smbbcull && !bbinsidespot(shadoworigin, shadowdir, shadowspot, oe->bbmin, oe->bbmax))
    3521             :                     {
    3522           0 :                         continue;
    3523             :                     }
    3524           0 :                     break;
    3525             :                 }
    3526             :             }
    3527           0 :             oe->rnext = nullptr;
    3528           0 :             *lastmms = oe;
    3529           0 :             lastmms = &oe->rnext;
    3530             :         }
    3531             :     }
    3532           0 : }
    3533             : 
    3534           0 : void rendershadowmapworld()
    3535             : {
    3536           0 :     SETSHADER(shadowmapworld,);
    3537             : 
    3538           0 :     gle::enablevertex();
    3539             : 
    3540           0 :     vtxarray *prev = nullptr;
    3541           0 :     for(vtxarray *va = shadowva; va; va = va->rnext)
    3542             :     {
    3543           0 :         if(va->tris && va->shadowmask&(1<<shadowside))
    3544             :         {
    3545           0 :             if(!prev || va->vbuf != prev->vbuf)
    3546             :             {
    3547           0 :                 gle::bindvbo(va->vbuf);
    3548           0 :                 gle::bindebo(va->ebuf);
    3549           0 :                 const vertex *ptr = 0;
    3550           0 :                 gle::vertexpointer(sizeof(vertex), ptr->pos.data());
    3551             :             }
    3552           0 :             if(!smnodraw)
    3553             :             {
    3554           0 :                 drawvatris(*va, 3*va->tris, 0);
    3555             :             }
    3556           0 :             xtravertsva += va->verts;
    3557           0 :             prev = va;
    3558             :         }
    3559             :     }
    3560           0 :     if(skyshadow)
    3561             :     {
    3562           0 :         prev = nullptr;
    3563           0 :         for(vtxarray *va = shadowva; va; va = va->rnext)
    3564             :         {
    3565           0 :             if(va->sky && va->shadowmask&(1<<shadowside))
    3566             :             {
    3567           0 :                 if(!prev || va->vbuf != prev->vbuf)
    3568             :                 {
    3569           0 :                     gle::bindvbo(va->vbuf);
    3570           0 :                     gle::bindebo(va->skybuf);
    3571           0 :                     const vertex *ptr = 0;
    3572           0 :                     gle::vertexpointer(sizeof(vertex), ptr->pos.data()); //note offset from nullptr
    3573             :                 }
    3574           0 :                 if(!smnodraw)
    3575             :                 {
    3576           0 :                     drawvaskytris(*va);
    3577             :                 }
    3578           0 :                 xtravertsva += va->sky/3;
    3579           0 :                 prev = va;
    3580             :             }
    3581             :         }
    3582             :     }
    3583             : 
    3584           0 :     gle::clearvbo();
    3585           0 :     gle::clearebo();
    3586           0 :     gle::disablevertex();
    3587           0 : }
    3588             : 
    3589           0 : void batchshadowmapmodels(bool skipmesh)
    3590             : {
    3591           0 :     if(!shadowmms)
    3592             :     {
    3593           0 :         return;
    3594             :     }
    3595           0 :     int nflags = EntFlag_NoVis|EntFlag_NoShadow;
    3596           0 :     if(skipmesh)
    3597             :     {
    3598           0 :         nflags |= EntFlag_ShadowMesh;
    3599             :     }
    3600           0 :     const std::vector<extentity *> &ents = entities::getents();
    3601           0 :     for(octaentities *oe = shadowmms; oe; oe = oe->rnext)
    3602             :     {
    3603           0 :         for(const int &k : oe->mapmodels)
    3604             :         {
    3605           0 :             extentity &e = *ents[k];
    3606           0 :             if(e.flags&nflags)
    3607             :             {
    3608           0 :                 continue;
    3609             :             }
    3610           0 :             e.flags |= EntFlag_Render;
    3611             :         }
    3612             :     }
    3613           0 :     for(octaentities *oe = shadowmms; oe; oe = oe->rnext)
    3614             :     {
    3615           0 :         for(const int &j : oe->mapmodels)
    3616             :         {
    3617           0 :             extentity &e = *ents[j];
    3618           0 :             if(!(e.flags&EntFlag_Render))
    3619             :             {
    3620           0 :                 continue;
    3621             :             }
    3622           0 :             rendermapmodel(e);
    3623           0 :             e.flags &= ~EntFlag_Render;
    3624             :         }
    3625             :     }
    3626             : }
    3627             : 
    3628           0 : void findshadowvas()
    3629             : {
    3630             :     std::array<vtxarray *, vasortsize> vasort;
    3631           0 :     vasort.fill(nullptr);
    3632           0 :     switch(shadowmapping)
    3633             :     {
    3634           0 :         case ShadowMap_Reflect:
    3635             :         {
    3636           0 :             findrsmshadowvas(varoot, vasort);
    3637           0 :             break;
    3638             :         }
    3639           0 :         case ShadowMap_CubeMap:
    3640             :         {
    3641           0 :             findshadowvas(varoot, vasort);
    3642           0 :             break;
    3643             :         }
    3644           0 :         case ShadowMap_Cascade:
    3645             :         {
    3646           0 :             findcsmshadowvas(varoot, vasort);
    3647           0 :             break;
    3648             :         }
    3649           0 :         case ShadowMap_Spot:
    3650             :         {
    3651           0 :             findspotshadowvas(varoot, vasort);
    3652           0 :             break;
    3653             :         }
    3654             :     }
    3655           0 :     sortshadowvas(vasort);
    3656           0 : }

Generated by: LCOV version 1.14