LCOV - code coverage report
Current view: top level - engine/render - renderva.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 0.7 % 1962 14
Test Date: 2026-08-20 06:51:03 Functions: 3.1 % 127 4

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

Generated by: LCOV version 2.0-1