LCOV - code coverage report
Current view: top level - engine/render - vacollect.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 0.8 % 990 8
Test Date: 2025-04-16 12:09:02 Functions: 4.5 % 44 2

            Line data    Source code
       1              : 
       2              : // vacollect.cpp: fill vertex array structure using vacollect object
       3              : #include "../libprimis-headers/cube.h"
       4              : #include "../../shared/geomexts.h"
       5              : #include "../../shared/glemu.h"
       6              : #include "../../shared/glexts.h"
       7              : 
       8              : #include "grass.h"
       9              : #include "octarender.h"
      10              : #include "rendergl.h"
      11              : #include "renderlights.h"
      12              : #include "renderparticles.h"
      13              : #include "rendersky.h"
      14              : #include "renderva.h"
      15              : #include "shader.h"
      16              : #include "shaderparam.h"
      17              : #include "texture.h"
      18              : 
      19              : #include "interface/menus.h"
      20              : 
      21              : #include "world/entities.h"
      22              : #include "world/light.h"
      23              : #include "world/material.h"
      24              : #include "world/octaworld.h"
      25              : #include "world/world.h"
      26              : 
      27              : enum AlphaState
      28              : {
      29              :     Alpha_None = 0,
      30              :     Alpha_Back,
      31              :     Alpha_Front,
      32              :     Alpha_Refract
      33              : };
      34              : 
      35              : class sortkey
      36              : {
      37              :     public:
      38              :         ushort tex;
      39              :         uchar orient, layer, alpha;
      40              : 
      41              :         sortkey() {}
      42            0 :         sortkey(ushort tex, uchar orient, uchar layer = BlendLayer_Top, uchar alpha = Alpha_None)
      43            0 :          : tex(tex), orient(orient), layer(layer), alpha(alpha)
      44            0 :         {}
      45              : 
      46            0 :         bool operator==(const sortkey &o) const
      47              :         {
      48            0 :             return tex==o.tex && orient==o.orient && layer==o.layer && alpha==o.alpha;
      49              :         }
      50              : 
      51            0 :         static bool sort(const sortkey &x, const sortkey &y)
      52              :         {
      53            0 :             if(x.alpha != y.alpha)
      54              :             {
      55            0 :                 return x.alpha < y.alpha;
      56              :             }
      57            0 :             if(x.layer != y.layer)
      58              :             {
      59            0 :                 return x.layer < y.layer;
      60              :             }
      61            0 :             if(x.tex == y.tex)
      62              :             {
      63            0 :                 return x.orient < y.orient;
      64              :             }
      65            0 :             VSlot &xs = lookupvslot(x.tex, false),
      66            0 :                   &ys = lookupvslot(y.tex, false);
      67            0 :             if(xs.slot->shader != ys.slot->shader)
      68              :             {
      69            0 :                 return xs.slot->shader < ys.slot->shader;
      70              :             }
      71            0 :             if(xs.slot->params.size() != ys.slot->params.size())
      72              :             {
      73            0 :                 return xs.slot->params.size() < ys.slot->params.size();
      74              :             }
      75            0 :             if(x.tex < y.tex)
      76              :             {
      77            0 :                 return true;
      78              :             }
      79            0 :             return false;
      80              :         }
      81              : };
      82              : 
      83              : template<>
      84              : struct std::hash<sortkey>
      85              : {
      86            0 :     size_t operator()(const sortkey &k) const
      87              :     {
      88            0 :         return k.tex;
      89              :     }
      90              : };
      91              : 
      92              : struct decalkey
      93              : {
      94              :     ushort tex, reuse;
      95              : 
      96              :     decalkey() {}
      97            0 :     decalkey(ushort tex, ushort reuse = 0)
      98            0 :      : tex(tex), reuse(reuse)
      99            0 :     {}
     100              : 
     101            0 :     bool operator==(const decalkey &o) const
     102              :     {
     103            0 :         return tex==o.tex && reuse==o.reuse;
     104              :     }
     105              : 
     106              :     static bool sort(const decalkey &x, const decalkey &y)
     107              :     {
     108              :         if(x.tex == y.tex)
     109              :         {
     110              :             if(x.reuse < y.reuse)
     111              :             {
     112              :                 return true;
     113              :             }
     114              :             else
     115              :             {
     116              :                 return false;
     117              :             }
     118              :         }
     119              :         const DecalSlot &xs = lookupdecalslot(x.tex, false),
     120              :                         &ys = lookupdecalslot(y.tex, false);
     121              :         if(xs.slot->shader < ys.slot->shader)
     122              :         {
     123              :             return true;
     124              :         }
     125              :         if(xs.slot->shader > ys.slot->shader)
     126              :         {
     127              :             return false;
     128              :         }
     129              :         if(xs.slot->params.size() < ys.slot->params.size())
     130              :         {
     131              :             return true;
     132              :         }
     133              :         if(xs.slot->params.size() > ys.slot->params.size())
     134              :         {
     135              :             return false;
     136              :         }
     137              :         if(x.tex < y.tex)
     138              :         {
     139              :             return true;
     140              :         }
     141              :         else
     142              :         {
     143              :             return false;
     144              :         }
     145              :     }
     146              : };
     147              : 
     148              : template<>
     149              : struct std::hash<decalkey>
     150              : {
     151            0 :     size_t operator()(const decalkey &k) const
     152              :     {
     153            0 :         return k.tex;
     154              :     }
     155              : };
     156              : 
     157              : struct sortval
     158              : {
     159              :     std::vector<ushort> tris;
     160              : 
     161            0 :     sortval() {}
     162              : };
     163              : 
     164              : struct vboinfo
     165              : {
     166              :     int uses;
     167              :     uchar *data;
     168              : };
     169              : 
     170              : std::unordered_map<GLuint, vboinfo> vbos;
     171              : 
     172            0 : VARFN(vbosize, maxvbosize, 0, 1<<14, 1<<16, rootworld.allchanged());
     173              : 
     174              : //vbo (vertex buffer object) enum is local to this file
     175              : enum
     176              : {
     177              :     VBO_VBuf = 0,
     178              :     VBO_EBuf,
     179              :     VBO_SkyBuf,
     180              :     VBO_DecalBuf,
     181              :     VBO_NumVBOs
     182              : };
     183              : 
     184              : std::vector<uchar> vbodata[VBO_NumVBOs];
     185              : std::vector<vtxarray *> vbovas[VBO_NumVBOs];
     186              : int vbosize[VBO_NumVBOs];
     187              : 
     188            0 : void destroyvbo(GLuint vbo)
     189              : {
     190            0 :     auto exists = vbos.find(vbo);
     191            0 :     if(exists == vbos.end())
     192              :     {
     193            0 :         return;
     194              :     }
     195            0 :     vboinfo &vbi = (*exists).second;
     196            0 :     if(vbi.uses <= 0)
     197              :     {
     198            0 :         return;
     199              :     }
     200            0 :     vbi.uses--;
     201            0 :     if(!vbi.uses)
     202              :     {
     203            0 :         glDeleteBuffers(1, &vbo);
     204            0 :         if(vbi.data)
     205              :         {
     206            0 :             delete[] vbi.data;
     207              :         }
     208            0 :         vbos.erase(vbo);
     209              :     }
     210              : }
     211              : 
     212              : //sets up vbos (vertex buffer objects) for each entry in the vas vector
     213              : //by setting up each vertex array's vbuf and vdata
     214            0 : void genvbo(int type, std::vector<uchar> &buf, std::vector<vtxarray *> &vas)
     215              : {
     216            0 :     gle::disable();
     217              : 
     218              :     GLuint vbo;
     219            0 :     glGenBuffers(1, &vbo);
     220            0 :     const GLenum target = type==VBO_VBuf ? GL_ARRAY_BUFFER : GL_ELEMENT_ARRAY_BUFFER;
     221            0 :     glBindBuffer(target, vbo);
     222            0 :     glBufferData(target, buf.size(), buf.data(), GL_STATIC_DRAW);
     223            0 :     glBindBuffer(target, 0);
     224              : 
     225            0 :     vboinfo &vbi = vbos[vbo];
     226            0 :     vbi.uses = vas.size();
     227            0 :     vbi.data = new uchar[buf.size()];
     228            0 :     std::memcpy(vbi.data, buf.data(), buf.size());
     229              : 
     230            0 :     for(vtxarray *va: vas)
     231              :     {
     232            0 :         switch(type)
     233              :         {
     234            0 :             case VBO_VBuf:
     235              :             {
     236            0 :                 va->vbuf = vbo;
     237            0 :                 va->vdata = reinterpret_cast<vertex *>(vbi.data);
     238            0 :                 break;
     239              :             }
     240            0 :             case VBO_EBuf:
     241              :             {
     242            0 :                 va->ebuf = vbo;
     243            0 :                 va->edata = reinterpret_cast<ushort *>(vbi.data);
     244            0 :                 break;
     245              :             }
     246            0 :             case VBO_SkyBuf:
     247              :             {
     248            0 :                 va->skybuf = vbo;
     249            0 :                 va->skydata = reinterpret_cast<ushort *>(vbi.data);
     250            0 :                 break;
     251              :             }
     252            0 :             case VBO_DecalBuf:
     253              :             {
     254            0 :                 va->decalbuf = vbo;
     255            0 :                 va->decaldata = reinterpret_cast<ushort *>(vbi.data);
     256            0 :                 break;
     257              :             }
     258              :         }
     259              :     }
     260            0 : }
     261              : 
     262              : //default type = -1: flush every vbo
     263              : //otherwise type specifies which of the VBOs to flush
     264              : 
     265              : //flushvbo: flushes data out of the specified VBO object and calls genvbo()
     266              : //then destroys the data in the relevant VBO
     267            0 : void flushvbo(int type = -1)
     268              : {
     269            0 :     if(type < 0)
     270              :     {
     271            0 :         for(int i = 0; i < VBO_NumVBOs; ++i)
     272              :         {
     273            0 :             flushvbo(i);
     274              :         }
     275            0 :         return;
     276              :     }
     277              : 
     278            0 :     std::vector<uchar> &data = vbodata[type];
     279            0 :     if(data.empty())
     280              :     {
     281            0 :         return;
     282              :     }
     283            0 :     std::vector<vtxarray *> &vas = vbovas[type];
     284            0 :     genvbo(type, data, vas);
     285            0 :     data.clear();
     286            0 :     vas.clear();
     287            0 :     vbosize[type] = 0;
     288              : }
     289              : 
     290            0 : uchar *addvbo(vtxarray *va, int type, int numelems, int elemsize)
     291              : {
     292            0 :     switch(type)
     293              :     {
     294            0 :         case VBO_VBuf:
     295              :         {
     296            0 :             va->voffset = vbosize[type];
     297            0 :             break;
     298              :         }
     299            0 :         case VBO_EBuf:
     300              :         {
     301            0 :             va->eoffset = vbosize[type];
     302            0 :             break;
     303              :         }
     304            0 :         case VBO_SkyBuf:
     305              :         {
     306            0 :             va->skyoffset = vbosize[type];
     307            0 :             break;
     308              :         }
     309            0 :         case VBO_DecalBuf:
     310              :         {
     311            0 :             va->decaloffset = vbosize[type];
     312            0 :             break;
     313              :         }
     314              :     }
     315            0 :     vbosize[type] += numelems;
     316            0 :     std::vector<uchar> &data = vbodata[type];
     317            0 :     std::vector<vtxarray *> &vas = vbovas[type];
     318            0 :     vas.push_back(va);
     319            0 :     int len = numelems*elemsize;
     320            0 :     data.reserve(len);
     321            0 :     for(int i = 0; i < len; ++i)
     322              :     {
     323            0 :         data.emplace_back();
     324              :     }
     325            0 :     return &(*(data.end()-len)); //return pointer to where iterator points
     326              : }
     327              : 
     328              : //takes a packed ushort vector and turns it into a vec3 vector object
     329            0 : vec decodenormal(ushort norm)
     330              : {
     331            0 :     if(!norm)
     332              :     {
     333            0 :         return vec(0, 0, 1);
     334              :     }
     335            0 :     norm--;
     336            0 :     const vec2 &yaw = sincos360[norm%360],
     337            0 :                &pitch = sincos360[norm/360+270];
     338            0 :     return vec(-yaw.y*pitch.x, yaw.x*pitch.x, pitch.y);
     339              : }
     340              : 
     341              : class vacollect
     342              : {
     343              : 
     344              :     public:
     345              :         int updateva(std::array<cube, 8> &c, const ivec &co, int size, int csi);
     346              :         vacollect();
     347              : 
     348              :     private:
     349              :         int size;
     350              :         std::vector<materialsurface> matsurfs;
     351              :         std::vector<octaentities *> mapmodels, decals;
     352              :         std::vector<const octaentities *> extdecals;
     353              :         vec skymin, skymax;
     354              :         vec alphamin, alphamax;
     355              :         vec refractmin, refractmax;
     356              :         std::vector<grasstri> grasstris;
     357              :         int worldtris, skytris;
     358              :         std::vector<ushort> skyindices;
     359              :         std::unordered_map<sortkey, sortval> indices;
     360              :         std::unordered_map<decalkey, sortval> decalindices;
     361              :         std::vector<sortkey> texs;
     362              :         std::vector<decalkey> decaltexs;
     363              :         int decaltris;
     364              :         std::vector<octaentities *> entstack;
     365              : 
     366              : 
     367              :         static const int hashsize = 1<<13;
     368              :         std::array<int, hashsize> table;
     369              : 
     370              :         std::vector<int> chain;
     371              :         std::vector<vertex> verts;
     372              : 
     373              :         struct mergedface
     374              :         {
     375              :             uchar orient, numverts;
     376              :             ushort mat, tex;
     377              :             const vertinfo *verts;
     378              :             int tjoints;
     379              :         };
     380              : 
     381              :         static constexpr int maxmergelevel = 12;
     382              :         int vahasmerges = 0,
     383              :             vamergemax = 0;
     384              :         std::vector<mergedface> vamerges[maxmergelevel+1];
     385              : 
     386              :         const vec orientation_bitangent[8][6] =
     387              :         {
     388              :             { vec( 0,  0, -1), vec( 0,  0, -1), vec( 0,  0, -1), vec( 0,  0, -1), vec( 0, -1,  0), vec( 0,  1,  0) },
     389              :             { vec( 0, -1,  0), vec( 0,  1,  0), vec( 1,  0,  0), vec(-1,  0,  0), vec(-1,  0,  0), vec(-1,  0,  0) },
     390              :             { vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  1,  0), vec( 0, -1,  0) },
     391              :             { vec( 0,  1,  0), vec( 0, -1,  0), vec(-1,  0,  0), vec( 1,  0,  0), vec( 1,  0,  0), vec( 1,  0,  0) },
     392              :             { vec( 0,  0, -1), vec( 0,  0, -1), vec( 0,  0, -1), vec( 0,  0, -1), vec( 0, -1,  0), vec( 0,  1,  0) },
     393              :             { vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  1,  0), vec( 0, -1,  0) },
     394              :             { vec( 0,  1,  0), vec( 0, -1,  0), vec(-1,  0,  0), vec( 1,  0,  0), vec( 1,  0,  0), vec( 1,  0,  0) },
     395              :             { vec( 0, -1,  0), vec( 0,  1,  0), vec( 1,  0,  0), vec(-1,  0,  0), vec(-1,  0,  0), vec(-1,  0,  0) },
     396              :         };
     397              : 
     398              :         static constexpr int vamaxsize = 0x1000; //4096 = 2^12
     399              : 
     400              :         // pos is an array of length numverts
     401              :         void addcubeverts(VSlot &vslot, int orient, const vec *pos, ushort texture, const vertinfo *vinfo, int numverts, int tj = -1, int grassy = 0, bool alpha = false, int layer = BlendLayer_Top);
     402              : 
     403              :         // verts is an array of length numverts
     404              :         void addtris(const VSlot &vslot, int orient, const sortkey &key, vertex *verts, const int *index, int numverts, int tj);
     405              : 
     406              :         // verts is an array of length face + 2 or face + 3
     407              :         void addgrasstri(int face, const vertex *verts, int numv, ushort texture);
     408              :         vtxarray *newva(const ivec &o, int size);
     409              :         void rendercube(cube &c, const ivec &co, int size, int csi, int &maxlevel); // creates vertices and indices ready to be put into a va
     410              :         void finddecals(const vtxarray *va);
     411              :         void calcgeombb(const ivec &co, int size, ivec &bbmin, ivec &bbmax) const;
     412              :         void setva(cube &c, const ivec &co, int size, int csi);
     413              :         void gencubeverts(const cube &c, const ivec &co, int size);
     414              :         void addmergedverts(int level, const ivec &o);
     415              : 
     416              :         void clear();
     417              :         void setupdata(vtxarray *va);
     418              :         bool emptyva();
     419              :         void optimize();
     420              :         void genverts(uchar *buf);
     421              :         void gendecal(const extentity &e, const DecalSlot &s, const decalkey &key);
     422              :         void gendecals();
     423              :         int findmergedfaces(cube &c, const ivec &co, int size, int csi, int minlevel);
     424              :         int genmergedfaces(cube &c, const ivec &co, int size, int minlevel = -1);
     425              :         void calctexgen(const VSlot &vslot, int orient, vec4<float> &sgen, vec4<float> &tgen);
     426              : 
     427              :         int addvert(const vertex &v);
     428              :         void clearverts();
     429              :         int addvert(const vec &pos, const vec &tc = vec(0, 0, 0), const bvec &norm = bvec(128, 128, 128), const vec4<uchar> &tangent = vec4<uchar>(128, 128, 128, 128));
     430              : 
     431              : } vc;
     432              : 
     433            0 : int vacollect::addvert(const vertex &v)
     434              : {
     435              :     auto vechash = std::hash<vec>();
     436            0 :     uint h = vechash(v.pos)&(hashsize-1);
     437            0 :     for(int i = table[h]; i>=0; i = chain[i])
     438              :     {
     439            0 :         const vertex &c = verts[i];
     440            0 :         if(c.pos==v.pos && c.tc==v.tc && c.norm==v.norm && c.tangent==v.tangent)
     441              :         {
     442            0 :              return i;
     443              :          }
     444              :     }
     445            0 :     if(verts.size() >= USHRT_MAX)
     446              :     {
     447            0 :         return -1;
     448              :     }
     449            0 :     verts.push_back(v);
     450            0 :     chain.emplace_back(table[h]);
     451            0 :     return table[h] = verts.size()-1;
     452              : }
     453            1 : void vacollect::clearverts()
     454              : {
     455            1 :     table.fill(-1);
     456            1 :     chain.clear();
     457            1 :     verts.clear();
     458            1 : }
     459              : 
     460            1 : vacollect::vacollect()
     461              : {
     462            1 :     clearverts();
     463            1 : }
     464              : 
     465            0 : int vacollect::addvert(const vec &pos, const vec &tc, const bvec &norm, const vec4<uchar> &tangent)
     466              : {
     467            0 :     vertex vtx;
     468            0 :     vtx.pos = pos;
     469            0 :     vtx.tc = tc;
     470            0 :     vtx.norm = norm;
     471            0 :     vtx.tangent = tangent;
     472            0 :     return addvert(vtx);
     473              : }
     474              : 
     475            0 : void vacollect::clear()
     476              : {
     477            0 :     clearverts();
     478            0 :     worldtris = skytris = decaltris = 0;
     479            0 :     indices.clear();
     480            0 :     decalindices.clear();
     481            0 :     skyindices.clear();
     482            0 :     matsurfs.clear();
     483            0 :     mapmodels.clear();
     484            0 :     decals.clear();
     485            0 :     extdecals.clear();
     486            0 :     grasstris.clear();
     487            0 :     texs.clear();
     488            0 :     decaltexs.clear();
     489            0 :     alphamin = refractmin = skymin = vec(1e16f, 1e16f, 1e16f);
     490            0 :     alphamax = refractmax = skymax = vec(-1e16f, -1e16f, -1e16f);
     491            0 : }
     492              : 
     493            0 : void vacollect::setupdata(vtxarray *va)
     494              : {
     495            0 :     optimize();
     496            0 :     gendecals();
     497              : 
     498            0 :     va->verts = verts.size();
     499            0 :     va->tris = worldtris/3;
     500            0 :     va->vbuf = 0;
     501            0 :     va->vdata = 0;
     502            0 :     va->minvert = 0;
     503            0 :     va->maxvert = va->verts-1;
     504            0 :     va->voffset = 0;
     505            0 :     if(va->verts)
     506              :     {
     507            0 :         if(vbosize[VBO_VBuf] + static_cast<int>(verts.size()) > maxvbosize ||
     508            0 :            vbosize[VBO_EBuf] + worldtris > USHRT_MAX ||
     509            0 :            vbosize[VBO_SkyBuf] + skytris > USHRT_MAX ||
     510            0 :            vbosize[VBO_DecalBuf] + decaltris > USHRT_MAX)
     511              :         {
     512            0 :             flushvbo();
     513              :         }
     514            0 :         uchar *vdata = addvbo(va, VBO_VBuf, va->verts, sizeof(vertex));
     515            0 :         genverts(vdata);
     516            0 :         va->minvert += va->voffset;
     517            0 :         va->maxvert += va->voffset;
     518              :     }
     519              : 
     520            0 :     va->matbuf.clear();
     521            0 :     va->matsurfs = matsurfs.size();
     522            0 :     va->matmask = 0;
     523            0 :     if(va->matsurfs)
     524              :     {
     525            0 :         va->matbuf = matsurfs;
     526            0 :         for(materialsurface &m : matsurfs)
     527              :         {
     528            0 :             if(m.visible == MatSurf_EditOnly)
     529              :             {
     530            0 :                 continue;
     531              :             }
     532            0 :             switch(m.material)
     533              :             {
     534            0 :                 case Mat_Glass:
     535              :                 case Mat_Water:
     536              :                 {
     537            0 :                     break;
     538              :                 }
     539            0 :                 default:
     540              :                 {
     541            0 :                     continue;
     542              :                 }
     543              :             }
     544            0 :             va->matmask |= 1<<m.material;
     545              :         }
     546              :     }
     547              : 
     548            0 :     va->skybuf = 0;
     549            0 :     va->skydata = 0;
     550            0 :     va->skyoffset = 0;
     551            0 :     va->sky = skyindices.size();
     552            0 :     if(va->sky)
     553              :     {
     554            0 :         ushort *skydata = reinterpret_cast<ushort *>(addvbo(va, VBO_SkyBuf, va->sky, sizeof(ushort)));
     555            0 :         std::memcpy(skydata, skyindices.data(), va->sky*sizeof(ushort));
     556            0 :         if(va->voffset)
     557              :         {
     558            0 :             for(uint i = 0; i < va->sky; ++i)
     559              :             {
     560            0 :                 skydata[i] += va->voffset;
     561              :             }
     562              :         }
     563              :     }
     564              : 
     565            0 :     va->texelems = nullptr;
     566            0 :     va->texs = texs.size();
     567            0 :     va->alphabacktris = 0;
     568            0 :     va->alphaback = 0;
     569            0 :     va->alphafronttris = 0;
     570            0 :     va->alphafront = 0;
     571            0 :     va->refracttris = 0;
     572            0 :     va->refract = 0;
     573            0 :     va->ebuf = 0;
     574            0 :     va->edata = 0;
     575            0 :     va->eoffset = 0;
     576            0 :     if(va->texs)
     577              :     {
     578            0 :         va->texelems = new elementset[va->texs];
     579            0 :         ushort *edata = reinterpret_cast<ushort *>(addvbo(va, VBO_EBuf, worldtris, sizeof(ushort))),
     580            0 :                *curbuf = edata;
     581            0 :         for(size_t i = 0; i < texs.size(); i++)
     582              :         {
     583            0 :             const sortkey &k = texs[i];
     584            0 :             const sortval &t = indices[k];
     585            0 :             elementset &e = va->texelems[i];
     586            0 :             e.texture = k.tex;
     587            0 :             e.attrs.orient = k.orient;
     588            0 :             e.attrs.layer = k.layer;
     589            0 :             const ushort *startbuf = curbuf;
     590            0 :             e.minvert = USHRT_MAX;
     591            0 :             e.maxvert = 0;
     592              : 
     593            0 :             if(t.tris.size())
     594              :             {
     595            0 :                 std::memcpy(curbuf, t.tris.data(), t.tris.size() * sizeof(ushort));
     596            0 :                 for(size_t j = 0; j < t.tris.size(); j++)
     597              :                 {
     598            0 :                     curbuf[j] += va->voffset;
     599            0 :                     e.minvert = std::min(e.minvert, curbuf[j]);
     600            0 :                     e.maxvert = std::max(e.maxvert, curbuf[j]);
     601              :                 }
     602            0 :                 curbuf += t.tris.size();
     603              :             }
     604            0 :             e.length = curbuf-startbuf;
     605            0 :             if(k.alpha==Alpha_Back)
     606              :             {
     607            0 :                 va->texs--;
     608            0 :                 va->tris -= e.length/3;
     609            0 :                 va->alphaback++;
     610            0 :                 va->alphabacktris += e.length/3;
     611              :             }
     612            0 :             else if(k.alpha==Alpha_Front)
     613              :             {
     614            0 :                 va->texs--;
     615            0 :                 va->tris -= e.length/3;
     616            0 :                 va->alphafront++;
     617            0 :                 va->alphafronttris += e.length/3;
     618              :             }
     619            0 :             else if(k.alpha==Alpha_Refract)
     620              :             {
     621            0 :                 va->texs--;
     622            0 :                 va->tris -= e.length/3;
     623            0 :                 va->refract++;
     624            0 :                 va->refracttris += e.length/3;
     625              :             }
     626              :         }
     627              :     }
     628              : 
     629            0 :     va->texmask = 0;
     630            0 :     va->dyntexs = 0;
     631            0 :     for(int i = 0; i < (va->texs+va->alphaback+va->alphafront+va->refract); ++i)
     632              :     {
     633            0 :         VSlot &vslot = lookupvslot(va->texelems[i].texture, false);
     634            0 :         if(vslot.isdynamic())
     635              :         {
     636            0 :             va->dyntexs++;
     637              :         }
     638            0 :         Slot &slot = *vslot.slot;
     639            0 :         for(DecalSlot::Tex j : slot.sts)
     640              :         {
     641            0 :             va->texmask |= 1 << j.type;
     642              :         }
     643              :     }
     644              : 
     645            0 :     va->decalbuf = 0;
     646            0 :     va->decaldata = 0;
     647            0 :     va->decaloffset = 0;
     648            0 :     va->decalelems = nullptr;
     649            0 :     va->decaltexs = decaltexs.size();
     650            0 :     va->decaltris = decaltris/3;
     651            0 :     if(va->decaltexs)
     652              :     {
     653            0 :         va->decalelems = new elementset[va->decaltexs];
     654            0 :         ushort *edata = reinterpret_cast<ushort *>(addvbo(va, VBO_DecalBuf, decaltris, sizeof(ushort))),
     655            0 :                *curbuf = edata;
     656            0 :         for(size_t i = 0; i < decaltexs.size(); i++)
     657              :         {
     658            0 :             const decalkey &k = decaltexs[i];
     659            0 :             const sortval &t = decalindices[k];
     660            0 :             elementset &e = va->decalelems[i];
     661            0 :             e.texture = k.tex;
     662            0 :             e.reuse = k.reuse;
     663            0 :             ushort *startbuf = curbuf;
     664            0 :             e.minvert = USHRT_MAX;
     665            0 :             e.maxvert = 0;
     666            0 :             if(t.tris.size())
     667              :             {
     668            0 :                 std::memcpy(curbuf, t.tris.data(), t.tris.size() * sizeof(ushort));
     669            0 :                 for(size_t j = 0; j < t.tris.size(); j++)
     670              :                 {
     671            0 :                     curbuf[j] += va->voffset;
     672            0 :                     e.minvert = std::min(e.minvert, curbuf[j]);
     673            0 :                     e.maxvert = std::max(e.maxvert, curbuf[j]);
     674              :                 }
     675            0 :                 curbuf += t.tris.size();
     676              :             }
     677            0 :             e.length = curbuf-startbuf;
     678              :         }
     679              :     }
     680            0 :     if(grasstris.size())
     681              :     {
     682            0 :         std::swap(va->grasstris, grasstris);
     683            0 :         loadgrassshaders();
     684              :     }
     685            0 :     if(mapmodels.size())
     686              :     {
     687            0 :         va->mapmodels.insert(va->decals.end(), mapmodels.begin(), mapmodels.end());
     688              :     }
     689            0 :     if(decals.size())
     690              :     {
     691            0 :         va->decals.insert(va->decals.end(), decals.begin(), decals.end());
     692              :     }
     693            0 : }
     694              : 
     695            0 : bool vacollect::emptyva()
     696              : {
     697            0 :     return verts.empty() && matsurfs.empty() && skyindices.empty() && grasstris.empty() && mapmodels.empty() && decals.empty();
     698              : }
     699              : 
     700            0 : void vacollect::optimize()
     701              : {
     702            0 :     for(auto &[k, t] : indices)
     703              :     {
     704            0 :         if(t.tris.size())
     705              :         {
     706            0 :             texs.push_back(k);
     707              :         }
     708              :     }
     709            0 :     std::sort(texs.begin(), texs.end(), sortkey::sort);
     710              : 
     711            0 :     matsurfs.resize(optimizematsurfs(matsurfs.data(), matsurfs.size()));
     712            0 : }
     713              : 
     714            0 : void vacollect::genverts(uchar *buf)
     715              : {
     716            0 :     vertex *f = reinterpret_cast<vertex *>(buf);
     717            0 :     for(const vertex &i : verts)
     718              :     {
     719            0 :         const vertex &v = i;
     720            0 :         *f = v;
     721            0 :         f->norm.flip();
     722            0 :         f->tangent.flip();
     723            0 :         f++;
     724              :     }
     725            0 : }
     726              : 
     727            0 : void vacollect::gendecal(const extentity &e, const DecalSlot &s, const decalkey &key)
     728              : {
     729            0 :     matrix3 orient;
     730            0 :     orient.identity();
     731            0 :     if(e.attr2)
     732              :     {
     733            0 :         orient.rotate_around_z(sincosmod360(e.attr2));
     734              :     }
     735            0 :     if(e.attr3)
     736              :     {
     737            0 :         orient.rotate_around_x(sincosmod360(e.attr3));
     738              :     }
     739            0 :     if(e.attr4)
     740              :     {
     741            0 :         orient.rotate_around_y(sincosmod360(-e.attr4));
     742              :     }
     743            0 :     vec size(std::max(static_cast<float>(e.attr5), 1.0f));
     744            0 :     size.y *= s.depth;
     745            0 :     if(!s.sts.empty())
     746              :     {
     747            0 :         Texture *t = s.sts[0].t;
     748            0 :         if(t->xs < t->ys)
     749              :         {
     750            0 :             size.x *= t->xs / static_cast<float>(t->ys);
     751              :         }
     752            0 :         else if(t->xs > t->ys)
     753              :         {
     754            0 :             size.z *= t->ys / static_cast<float>(t->xs);
     755              :         }
     756              :     }
     757            0 :     vec center = orient.transform(vec(0, size.y*0.5f, 0)).add(e.o),
     758            0 :         radius = orient.abstransform(vec(size).mul(0.5f)),
     759            0 :         bbmin = vec(center).sub(radius),
     760            0 :         bbmax = vec(center).add(radius),
     761            0 :         clipoffset = orient.transposedtransform(center).msub(size, 0.5f);
     762            0 :     for(size_t i = 0; i < texs.size(); i++)
     763              :     {
     764            0 :         const sortkey &k = texs[i];
     765            0 :         if(k.layer == BlendLayer_Blend || k.alpha != Alpha_None)
     766              :         {
     767            0 :             continue;
     768              :         }
     769            0 :         const sortval &t = indices[k];
     770            0 :         if(t.tris.empty())
     771              :         {
     772            0 :             continue;
     773              :         }
     774            0 :         decalkey tkey(key);
     775            0 :         if(shouldreuseparams(s, lookupvslot(k.tex, false)))
     776              :         {
     777            0 :             tkey.reuse = k.tex;
     778              :         }
     779            0 :         for(size_t j = 0; j < t.tris.size(); j += 3)
     780              :         {
     781            0 :             const vertex &t0 = verts[t.tris[j]],
     782            0 :                          &t1 = verts[t.tris[j+1]],
     783            0 :                          &t2 = verts[t.tris[j+2]];
     784            0 :             vec v0 = t0.pos,
     785            0 :                 v1 = t1.pos,
     786            0 :                 v2 = t2.pos,
     787            0 :                 tmin = vec(v0).min(v1).min(v2),
     788            0 :                 tmax = vec(v0).max(v1).max(v2);
     789            0 :             if(tmin.x >= bbmax.x || tmin.y >= bbmax.y || tmin.z >= bbmax.z ||
     790            0 :                tmax.x <= bbmin.x || tmax.y <= bbmin.y || tmax.z <= bbmin.z)
     791              :             {
     792            0 :                 continue;
     793              :             }
     794            0 :             float f0 = t0.norm.tonormal().dot(orient.b),
     795            0 :                   f1 = t1.norm.tonormal().dot(orient.b),
     796            0 :                   f2 = t2.norm.tonormal().dot(orient.b);
     797            0 :             if(f0 >= 0 && f1 >= 0 && f2 >= 0)
     798              :             {
     799            0 :                 continue;
     800              :             }
     801            0 :             vec p1[9], p2[9];
     802            0 :             p1[0] = v0;
     803            0 :             p1[1] = v1;
     804            0 :             p1[2] = v2;
     805            0 :             int nump = polyclip(p1, 3, orient.b, clipoffset.y, clipoffset.y + size.y, p2);
     806            0 :             if(nump < 3)
     807              :             {
     808            0 :                 continue;
     809              :             }
     810            0 :             nump = polyclip(p2, nump, orient.a, clipoffset.x, clipoffset.x + size.x, p1);
     811            0 :             if(nump < 3)
     812              :             {
     813            0 :                 continue;
     814              :             }
     815            0 :             nump = polyclip(p1, nump, orient.c, clipoffset.z, clipoffset.z + size.z, p2);
     816            0 :             if(nump < 3)
     817              :             {
     818            0 :                 continue;
     819              :             }
     820            0 :             vec4<uchar> n0 = t0.norm,
     821            0 :                   n1 = t1.norm,
     822            0 :                   n2 = t2.norm,
     823            0 :                   x0 = t0.tangent,
     824            0 :                   x1 = t1.tangent,
     825            0 :                   x2 = t2.tangent;
     826            0 :             vec e1 = vec(v1).sub(v0),
     827            0 :                 e2 = vec(v2).sub(v0);
     828            0 :             float d11 = e1.dot(e1),
     829            0 :                   d12 = e1.dot(e2),
     830            0 :                   d22 = e2.dot(e2);
     831              :             int idx[9];
     832            0 :             for(int k = 0; k < nump; ++k)
     833              :             {
     834            0 :                 vertex v;
     835            0 :                 v.pos = p2[k];
     836            0 :                 vec ep = vec(v.pos).sub(v0);
     837            0 :                 float dp1 = ep.dot(e1),
     838            0 :                       dp2 = ep.dot(e2),
     839            0 :                       denom = d11*d22 - d12*d12,
     840            0 :                       b1 = (d22*dp1 - d12*dp2) / denom,
     841            0 :                       b2 = (d11*dp2 - d12*dp1) / denom,
     842            0 :                       b0 = 1 - b1 - b2;
     843            0 :                 v.norm.lerp(n0, n1, n2, b0, b1, b2);
     844            0 :                 v.norm.w = static_cast<uchar>(127.5f - 127.5f*(f0*b0 + f1*b1 + f2*b2));
     845            0 :                 vec tc = orient.transposedtransform(vec(center).sub(v.pos)).div(size).add(0.5f);
     846            0 :                 v.tc = vec(tc.x, tc.z, s.fade ? tc.y * s.depth / s.fade : 1.0f);
     847            0 :                 v.tangent.lerp(x0, x1, x2, b0, b1, b2);
     848            0 :                 idx[k] = addvert(v);
     849              :             }
     850            0 :             std::vector<ushort> &tris = decalindices[tkey].tris;
     851            0 :             for(int k = 0; k < nump-2; ++k)
     852              :             {
     853            0 :                 if(idx[0] != idx[k+1] && idx[k+1] != idx[k+2] && idx[k+2] != idx[0])
     854              :                 {
     855            0 :                     tris.push_back(idx[0]);
     856            0 :                     tris.push_back(idx[k+1]);
     857            0 :                     tris.push_back(idx[k+2]);
     858            0 :                     decaltris += 3;
     859              :                 }
     860              :             }
     861              :         }
     862              :     }
     863            0 : }
     864              : 
     865            0 : void vacollect::gendecals()
     866              : {
     867            0 :     if(decals.size())
     868              :     {
     869            0 :         extdecals.insert(extdecals.end(), decals.begin(), decals.end());
     870              :     }
     871            0 :     if(extdecals.empty())
     872              :     {
     873            0 :         return;
     874              :     }
     875            0 :     const std::vector<extentity *> &ents = entities::getents();
     876            0 :     for(const octaentities* oe : extdecals)
     877              :     {
     878              :         //get an index to ents in this VA from oe->decals
     879            0 :         for(const uint j : oe->decals)
     880              :         {
     881            0 :             extentity &e = *ents[j];
     882            0 :             if(e.flags&EntFlag_Render)
     883              :             {
     884            0 :                 continue;
     885              :             }
     886            0 :             e.flags |= EntFlag_Render;
     887            0 :             const DecalSlot &s = lookupdecalslot(e.attr1, true);
     888            0 :             if(!s.shader)
     889              :             {
     890            0 :                 continue;
     891              :             }
     892            0 :             const decalkey k(e.attr1);
     893            0 :             gendecal(e, s, k);
     894              :         }
     895              :     }
     896            0 :     for(const octaentities* oe : extdecals)
     897              :     {
     898              :         //get an index to ents in this VA from oe->decals
     899            0 :         for(const uint j : oe->decals)
     900              :         {
     901            0 :             extentity &e = *ents[j];
     902            0 :             if(e.flags&EntFlag_Render)
     903              :             {
     904            0 :                 e.flags &= ~EntFlag_Render;
     905              :             }
     906              :         }
     907              :     }
     908            0 :     for(auto &[k, t] : decalindices)
     909              :     {
     910            0 :         if(t.tris.size())
     911              :         {
     912            0 :             decaltexs.push_back(k);
     913              :         }
     914              :     }
     915            0 :     std::sort(texs.begin(), texs.end(), sortkey::sort);
     916              : }
     917              : 
     918            0 : int vacollect::genmergedfaces(cube &c, const ivec &co, int size, int minlevel)
     919              : {
     920            0 :     if(!c.ext || c.isempty())
     921              :     {
     922            0 :         return -1;
     923              :     }
     924            0 :     int tj = c.ext->tjoints,
     925            0 :         maxlevel = -1;
     926            0 :     for(size_t i = 0; i < c.ext->surfaces.size(); ++i)
     927              :     {
     928            0 :         if(c.merged&(1<<i))
     929              :         {
     930            0 :             surfaceinfo &surf = c.ext->surfaces[i];
     931            0 :             const int numverts = surf.numverts&Face_MaxVerts;
     932            0 :             if(!numverts)
     933              :             {
     934            0 :                 if(minlevel < 0)
     935              :                 {
     936            0 :                     vahasmerges |= Merge_Part;
     937              :                 }
     938            0 :                 continue;
     939              :             }
     940              :             mergedface mf;
     941            0 :             mf.orient = i;
     942            0 :             mf.mat = c.material;
     943            0 :             mf.tex = c.texture[i];
     944            0 :             mf.numverts = surf.numverts;
     945            0 :             mf.verts = c.ext->verts() + surf.verts;
     946            0 :             mf.tjoints = -1;
     947            0 :             const int level = calcmergedsize(co, size, mf.verts, mf.numverts&Face_MaxVerts);
     948            0 :             if(level > minlevel)
     949              :             {
     950            0 :                 maxlevel = std::max(maxlevel, level);
     951              : 
     952            0 :                 while(tj >= 0 && tjoints[tj].edge < i*(Face_MaxVerts+1))
     953              :                 {
     954            0 :                     tj = tjoints[tj].next;
     955              :                 }
     956            0 :                 if(tj >= 0 && tjoints[tj].edge < (i+1)*(Face_MaxVerts+1))
     957              :                 {
     958            0 :                     mf.tjoints = tj;
     959              :                 }
     960            0 :                 if(surf.numverts&BlendLayer_Top)
     961              :                 {
     962            0 :                     vamerges[level].push_back(mf);
     963              :                 }
     964            0 :                 if(surf.numverts&BlendLayer_Bottom)
     965              :                 {
     966            0 :                     mf.numverts &= ~BlendLayer_Blend;
     967            0 :                     mf.numverts |= surf.numverts&BlendLayer_Top ? BlendLayer_Bottom : BlendLayer_Top;
     968            0 :                     vamerges[level].push_back(mf);
     969              :                 }
     970              :             }
     971              :         }
     972              :     }
     973            0 :     if(maxlevel >= 0)
     974              :     {
     975            0 :         vamergemax = std::max(vamergemax, maxlevel);
     976            0 :         vahasmerges |= Merge_Origin;
     977              :     }
     978            0 :     return maxlevel;
     979              : }
     980              : 
     981            0 : int vacollect::findmergedfaces(cube &c, const ivec &co, int size, int csi, int minlevel)
     982              : {
     983            0 :     if(c.ext && c.ext->va && !(c.ext->va->hasmerges&Merge_Origin))
     984              :     {
     985            0 :         return c.ext->va->mergelevel;
     986              :     }
     987            0 :     else if(c.children)
     988              :     {
     989            0 :         int maxlevel = -1;
     990            0 :         for(int i = 0; i < 8; ++i)
     991              :         {
     992            0 :             ivec o(i, co, size/2);
     993            0 :             int level = findmergedfaces((*c.children)[i], o, size/2, csi-1, minlevel);
     994            0 :             maxlevel = std::max(maxlevel, level);
     995              :         }
     996            0 :         return maxlevel;
     997              :     }
     998            0 :     else if(c.ext && c.merged)
     999              :     {
    1000            0 :         return genmergedfaces(c, co, size, minlevel);
    1001              :     }
    1002              :     else
    1003              :     {
    1004            0 :         return -1;
    1005              :     }
    1006              : }
    1007              : 
    1008            0 : int setcubevisibility(cube &c, const ivec &co, int size)
    1009              : {
    1010            0 :     if(c.isempty() && (c.material&MatFlag_Clip) != Mat_Clip)
    1011              :     {
    1012            0 :         return 0;
    1013              :     }
    1014            0 :     int numvis = 0,
    1015            0 :         vismask = 0,
    1016            0 :         collidemask = 0,
    1017            0 :         checkmask = 0;
    1018            0 :     for(int i = 0; i < 6; ++i)
    1019              :     {
    1020            0 :         int facemask = classifyface(c, i, co, size);
    1021            0 :         if(facemask&1)
    1022              :         {
    1023            0 :             vismask |= 1<<i;
    1024            0 :             if(c.merged&(1<<i))
    1025              :             {
    1026            0 :                 if(c.ext && c.ext->surfaces[i].numverts&Face_MaxVerts)
    1027              :                 {
    1028            0 :                     numvis++;
    1029              :                 }
    1030              :             }
    1031              :             else
    1032              :             {
    1033            0 :                 numvis++;
    1034            0 :                 if(c.texture[i] != Default_Sky && !(c.ext && c.ext->surfaces[i].numverts & Face_MaxVerts))
    1035              :                 {
    1036            0 :                     checkmask |= 1<<i;
    1037              :                 }
    1038              :             }
    1039              :         }
    1040            0 :         if(facemask&2)
    1041              :         {
    1042            0 :             collidemask |= 1<<i;
    1043              :         }
    1044              :     }
    1045            0 :     c.visible = collidemask | (vismask ? (vismask != collidemask ? (checkmask ? 0x80|0x40 : 0x80) : 0x40) : 0);
    1046            0 :     return numvis;
    1047              : }
    1048              : 
    1049              : //index array must be >= numverts long
    1050              : //verts array must be >= Face_MaxVerts + 1 and >= numverts long
    1051            0 : void vacollect::addtris(const VSlot &vslot, int orient, const sortkey &key, vertex *verts, const int *index, int numverts, int tj)
    1052              : {
    1053            0 :     int &total = key.tex == Default_Sky ? skytris : worldtris,
    1054            0 :          edge  = orient*(Face_MaxVerts+1);
    1055            0 :     for(int i = 0; i < numverts-2; ++i)
    1056              :     {
    1057            0 :         if(index[0]!=index[i+1] && index[i+1]!=index[i+2] && index[i+2]!=index[0])
    1058              :         {
    1059            0 :             std::vector<ushort> &idxs = key.tex == Default_Sky ? skyindices : indices[key].tris;
    1060            0 :             int left  = index[0],
    1061            0 :                 mid   = index[i+1],
    1062            0 :                 right = index[i+2],
    1063            0 :                 start = left,
    1064            0 :                 i0    = left,
    1065            0 :                 i1    = -1;
    1066            0 :             for(int k = 0; k < 4; ++k)
    1067              :             {
    1068            0 :                 int i2    = -1,
    1069            0 :                     ctj   = -1,
    1070            0 :                     cedge = -1;
    1071            0 :                 switch(k)
    1072              :                 {
    1073            0 :                     case 1:
    1074              :                     {
    1075            0 :                         i1 = i2 = mid;
    1076            0 :                         cedge = edge+i+1;
    1077            0 :                         break;
    1078              :                     }
    1079            0 :                     case 2:
    1080              :                     {
    1081            0 :                         if(i1 != mid || i0 == left)
    1082              :                         {
    1083            0 :                             i0 = i1;
    1084            0 :                             i1 = right;
    1085              :                         }
    1086            0 :                         i2 = right;
    1087            0 :                         if(i+1 == numverts-2)
    1088              :                         {
    1089            0 :                             cedge = edge+i+2;
    1090              :                         }
    1091            0 :                         break;
    1092              :                     }
    1093            0 :                     case 3:
    1094              :                     {
    1095            0 :                         if(i0 == start)
    1096              :                         {
    1097            0 :                             i0 = i1;
    1098            0 :                             i1 = left;
    1099              :                         }
    1100            0 :                         i2 = left;
    1101              :                     }
    1102              :                     [[fallthrough]];
    1103            0 :                     default:
    1104              :                     {
    1105            0 :                         if(!i)
    1106              :                         {
    1107            0 :                             cedge = edge;
    1108              :                         }
    1109            0 :                         break;
    1110              :                     }
    1111              :                 }
    1112            0 :                 if(i1 != i2)
    1113              :                 {
    1114            0 :                     if(total + 3 > USHRT_MAX)
    1115              :                     {
    1116            0 :                         return;
    1117              :                     }
    1118            0 :                     total += 3;
    1119            0 :                     idxs.push_back(i0);
    1120            0 :                     idxs.push_back(i1);
    1121            0 :                     idxs.push_back(i2);
    1122            0 :                     i1 = i2;
    1123              :                 }
    1124            0 :                 if(cedge >= 0)
    1125              :                 {
    1126            0 :                     for(ctj = tj;;)
    1127              :                     {
    1128            0 :                         if(ctj < 0)
    1129              :                         {
    1130            0 :                             break;
    1131              :                         }
    1132            0 :                         if(tjoints[ctj].edge < cedge)
    1133              :                         {
    1134            0 :                             ctj = tjoints[ctj].next;
    1135            0 :                             continue;
    1136              :                         }
    1137            0 :                         if(tjoints[ctj].edge != cedge)
    1138              :                         {
    1139            0 :                             ctj = -1;
    1140              :                         }
    1141            0 :                         break;
    1142              :                     }
    1143              :                 }
    1144            0 :                 if(ctj >= 0)
    1145              :                 {
    1146            0 :                     int e1 = cedge%(Face_MaxVerts+1),
    1147            0 :                         e2 = (e1+1)%numverts;
    1148            0 :                     vertex &v1 = verts[e1],
    1149            0 :                            &v2 = verts[e2];
    1150            0 :                     ivec d(vec(v2.pos).sub(v1.pos).mul(8));
    1151            0 :                     int axis = std::abs(d.x) > std::abs(d.y) ? (std::abs(d.x) > std::abs(d.z) ? 0 : 2) : (std::abs(d.y) > std::abs(d.z) ? 1 : 2);
    1152            0 :                     if(d[axis] < 0)
    1153              :                     {
    1154            0 :                         d.neg();
    1155              :                     }
    1156            0 :                     reduceslope(d);
    1157            0 :                     int origin =  static_cast<int>(std::min(v1.pos[axis], v2.pos[axis])*8)&~0x7FFF,
    1158            0 :                         offset1 = (static_cast<int>(v1.pos[axis]*8) - origin) / d[axis],
    1159            0 :                         offset2 = (static_cast<int>(v2.pos[axis]*8) - origin) / d[axis];
    1160            0 :                     vec o = vec(v1.pos).sub(vec(d).mul(offset1/8.0f));
    1161            0 :                     float doffset = 1.0f / (offset2 - offset1);
    1162              : 
    1163            0 :                     if(i1 < 0)
    1164              :                     {
    1165              :                         for(;;)
    1166              :                         {
    1167            0 :                             tjoint &t = tjoints[ctj];
    1168            0 :                             if(t.next < 0 || tjoints[t.next].edge != cedge)
    1169              :                             {
    1170            0 :                                 break;
    1171              :                             }
    1172            0 :                             ctj = t.next;
    1173            0 :                         }
    1174              :                     }
    1175            0 :                     while(ctj >= 0)
    1176              :                     {
    1177            0 :                         tjoint &t = tjoints[ctj];
    1178            0 :                         if(t.edge != cedge)
    1179              :                         {
    1180            0 :                             break;
    1181              :                         }
    1182            0 :                         float offset = (t.offset - offset1) * doffset;
    1183            0 :                         vertex vt;
    1184            0 :                         vt.pos = vec(d).mul(t.offset/8.0f).add(o);
    1185            0 :                         vt.tc.lerp(v1.tc, v2.tc, offset);
    1186            0 :                         vt.norm.lerp(v1.norm, v2.norm, offset);
    1187            0 :                         vt.tangent.lerp(v1.tangent, v2.tangent, offset);
    1188            0 :                         if(v1.tangent.w != v2.tangent.w)
    1189              :                         {
    1190            0 :                             vt.tangent.w = orientation_bitangent[vslot.rotation][orient].scalartriple(vt.norm.tonormal(), vt.tangent.tonormal()) < 0 ? 0 : 255;
    1191              :                         }
    1192            0 :                         int i2 = addvert(vt);
    1193            0 :                         if(i2 < 0)
    1194              :                         {
    1195            0 :                             return;
    1196              :                         }
    1197            0 :                         if(i1 >= 0)
    1198              :                         {
    1199            0 :                             if(total + 3 > USHRT_MAX)
    1200              :                             {
    1201            0 :                                 return;
    1202              :                             }
    1203            0 :                             total += 3;
    1204            0 :                             idxs.push_back(i0);
    1205            0 :                             idxs.push_back(i1);
    1206            0 :                             idxs.push_back(i2);
    1207            0 :                             i1 = i2;
    1208              :                         }
    1209              :                         else
    1210              :                         {
    1211            0 :                             start = i0 = i2;
    1212              :                         }
    1213            0 :                         ctj = t.next;
    1214              :                     }
    1215              :                 }
    1216              :             }
    1217              :         }
    1218              :     }
    1219              : }
    1220              : 
    1221              : //face: index of which face to cover
    1222              : //verts: information about grass texs' face, array of vertices, must be >= face+3 long
    1223              : //numv: number of grass vertices
    1224              : //texture: index for the grass texture to use
    1225            0 : void vacollect::addgrasstri(int face, const vertex *verts, int numv, ushort texture)
    1226              : {
    1227            0 :     grasstris.emplace_back();
    1228            0 :     grasstri &g = grasstris.back();
    1229              :     int i1, i2, i3, i4;
    1230            0 :     if(numv <= 3 && face%2)
    1231              :     {
    1232            0 :         i1 = face+1;
    1233            0 :         i2 = face+2;
    1234            0 :         i3 = i4 = 0;
    1235              :     }
    1236              :     else
    1237              :     {
    1238            0 :         i1 = 0;
    1239            0 :         i2 = face+1;
    1240            0 :         i3 = face+2;
    1241            0 :         i4 = numv > 3 ? face+3 : i3;
    1242              :     }
    1243            0 :     g.v[0] = verts[i1].pos;
    1244            0 :     g.v[1] = verts[i2].pos;
    1245            0 :     g.v[2] = verts[i3].pos;
    1246            0 :     g.v[3] = verts[i4].pos;
    1247            0 :     g.numv = numv;
    1248            0 :     g.surface.toplane(g.v[0], g.v[1], g.v[2]);
    1249            0 :     if(g.surface.z <= 0)
    1250              :     {
    1251            0 :         grasstris.pop_back();
    1252            0 :         return;
    1253              :     }
    1254            0 :     g.minz = std::min(std::min(g.v[0].z, g.v[1].z), std::min(g.v[2].z, g.v[3].z));
    1255            0 :     g.maxz = std::max(std::max(g.v[0].z, g.v[1].z), std::max(g.v[2].z, g.v[3].z));
    1256            0 :     g.center = vec(0, 0, 0);
    1257            0 :     for(int k = 0; k < numv; ++k)
    1258              :     {
    1259            0 :         g.center.add(g.v[k]);
    1260              :     }
    1261            0 :     g.center.div(numv);
    1262            0 :     g.radius = 0;
    1263            0 :     for(int k = 0; k < numv; ++k)
    1264              :     {
    1265            0 :         g.radius = std::max(g.radius, g.v[k].dist(g.center));
    1266              :     }
    1267            0 :     g.texture = texture;
    1268              : }
    1269              : 
    1270            0 : void vacollect::gencubeverts(const cube &c, const ivec &co, int size)
    1271              : {
    1272            0 :     if(!(c.visible&0xC0))
    1273              :     {
    1274            0 :         return;
    1275              :     }
    1276            0 :     int vismask = ~c.merged & 0x3F;
    1277            0 :     if(!(c.visible&0x80))
    1278              :     {
    1279            0 :         vismask &= c.visible;
    1280              :     }
    1281            0 :     if(!vismask)
    1282              :     {
    1283            0 :         return;
    1284              :     }
    1285            0 :     int tj = filltjoints && c.ext ? c.ext->tjoints : -1, vis;
    1286            0 :     for(int i = 0; i < 6; ++i)
    1287              :     {
    1288            0 :         if(vismask&(1<<i) && (vis = visibletris(c, i, co, size)))
    1289              :         {
    1290            0 :             vec pos[Face_MaxVerts];
    1291            0 :             vertinfo *verts = nullptr;
    1292            0 :             int numverts = c.ext ? c.ext->surfaces[i].numverts&Face_MaxVerts : 0,
    1293            0 :                 convex   = 0;
    1294            0 :             if(numverts)
    1295              :             {
    1296            0 :                 verts = c.ext->verts() + c.ext->surfaces[i].verts;
    1297            0 :                 vec vo(ivec(co).mask(~0xFFF));
    1298            0 :                 for(int j = 0; j < numverts; ++j)
    1299              :                 {
    1300            0 :                     pos[j] = vec(verts[j].getxyz()).mul(1.0f/8).add(vo);
    1301              :                 }
    1302            0 :                 if(!flataxisface(c, i))
    1303              :                 {
    1304            0 :                     convex = faceconvexity(verts, numverts, size);
    1305              :                 }
    1306              :             }
    1307              :             else
    1308              :             {
    1309            0 :                 std::array<ivec, 4> v;
    1310            0 :                 genfaceverts(c, i, v);
    1311            0 :                 if(!flataxisface(c, i))
    1312              :                 {
    1313            0 :                     convex = faceconvexity(v);
    1314              :                 }
    1315            0 :                 int order = vis&4 || convex < 0 ? 1 : 0;
    1316            0 :                 vec vo(co);
    1317            0 :                 pos[numverts++] = vec(v[order]).mul(size/8.0f).add(vo);
    1318            0 :                 if(vis&1)
    1319              :                 {
    1320            0 :                     pos[numverts++] = vec(v[order+1]).mul(size/8.0f).add(vo);
    1321              :                 }
    1322            0 :                 pos[numverts++] = vec(v[order+2]).mul(size/8.0f).add(vo);
    1323            0 :                 if(vis&2)
    1324              :                 {
    1325            0 :                     pos[numverts++] = vec(v[(order+3)&3]).mul(size/8.0f).add(vo);
    1326              :                 }
    1327              :             }
    1328              : 
    1329            0 :             VSlot &vslot = lookupvslot(c.texture[i], true);
    1330            0 :             while(tj >= 0 && tjoints[tj].edge < i*(Face_MaxVerts+1))
    1331              :             {
    1332            0 :                 tj = tjoints[tj].next;
    1333              :             }
    1334            0 :             int hastj = tj >= 0 && tjoints[tj].edge < (i+1)*(Face_MaxVerts+1) ? tj : -1,
    1335            0 :                 grassy = vslot.slot->grass && i!=Orient_Bottom ? (vis!=3 || convex ? 1 : 2) : 0;
    1336            0 :             if(!c.ext)
    1337              :             {
    1338            0 :                 addcubeverts(vslot, i, pos, c.texture[i], nullptr, numverts, hastj, grassy, (c.material&Mat_Alpha)!=0);
    1339              :             }
    1340              :             else
    1341              :             {
    1342            0 :                 const surfaceinfo &surf = c.ext->surfaces[i];
    1343            0 :                 if(!surf.numverts || surf.numverts&BlendLayer_Top)
    1344              :                 {
    1345            0 :                     addcubeverts(vslot, i, pos, c.texture[i], verts, numverts, hastj, grassy, (c.material&Mat_Alpha)!=0, surf.numverts&BlendLayer_Blend);
    1346              :                 }
    1347            0 :                 if(surf.numverts&BlendLayer_Bottom)
    1348              :                 {
    1349            0 :                     addcubeverts(vslot, i, pos, 0, verts, numverts, hastj, 0, false, surf.numverts&BlendLayer_Top ? BlendLayer_Bottom : BlendLayer_Top);
    1350              :                 }
    1351              :             }
    1352              :         }
    1353              :     }
    1354              : }
    1355              : 
    1356              : 
    1357            0 : vtxarray *vacollect::newva(const ivec &o, int size)
    1358              : {
    1359            0 :     auto *va = new vtxarray;
    1360            0 :     va->parent = nullptr;
    1361            0 :     va->o = o;
    1362            0 :     va->size = size;
    1363            0 :     va->curvfc = ViewFrustumCull_NotVisible;
    1364            0 :     va->occluded = Occlude_Nothing;
    1365            0 :     va->query = nullptr;
    1366            0 :     va->bbmin = va->alphamin = va->refractmin = va->skymin = ivec(-1, -1, -1);
    1367            0 :     va->bbmax = va->alphamax = va->refractmax = va->skymax = ivec(-1, -1, -1);
    1368            0 :     va->hasmerges = 0;
    1369            0 :     va->mergelevel = -1;
    1370              : 
    1371            0 :     setupdata(va);
    1372              : 
    1373            0 :     if(va->alphafronttris || va->alphabacktris || va->refracttris)
    1374              :     {
    1375            0 :         va->alphamin = ivec(vec(alphamin).mul(8)).shr(3);
    1376            0 :         va->alphamax = ivec(vec(alphamax).mul(8)).add(7).shr(3);
    1377              :     }
    1378            0 :     if(va->refracttris)
    1379              :     {
    1380            0 :         va->refractmin = ivec(vec(refractmin).mul(8)).shr(3);
    1381            0 :         va->refractmax = ivec(vec(refractmax).mul(8)).add(7).shr(3);
    1382              :     }
    1383            0 :     if(va->sky && skymax.x >= 0)
    1384              :     {
    1385            0 :         va->skymin = ivec(vec(skymin).mul(8)).shr(3);
    1386            0 :         va->skymax = ivec(vec(skymax).mul(8)).add(7).shr(3);
    1387              :     }
    1388              : 
    1389            0 :     wverts += va->verts;
    1390            0 :     wtris  += va->tris + va->alphabacktris + va->alphafronttris + va->refracttris + va->decaltris;
    1391            0 :     allocva++;
    1392            0 :     valist.push_back(va);
    1393            0 :     return va;
    1394              : }
    1395              : 
    1396            0 : void vacollect::addmergedverts(int level, const ivec &o)
    1397              : {
    1398            0 :     std::vector<mergedface> &mfl = vamerges[level];
    1399            0 :     if(mfl.empty())
    1400              :     {
    1401            0 :         return;
    1402              :     }
    1403            0 :     vec vo(ivec(o).mask(~0xFFF));
    1404            0 :     vec pos[Face_MaxVerts];
    1405            0 :     for(mergedface &mf : mfl)
    1406              :     {
    1407            0 :         int numverts = mf.numverts&Face_MaxVerts;
    1408            0 :         for(int i = 0; i < numverts; ++i)
    1409              :         {
    1410            0 :             const vertinfo &v = mf.verts[i];
    1411            0 :             pos[i] = vec(v.x, v.y, v.z).mul(1.0f/8).add(vo);
    1412              :         }
    1413            0 :         VSlot &vslot = lookupvslot(mf.tex, true);
    1414            0 :         int grassy = vslot.slot->grass && mf.orient!=Orient_Bottom && mf.numverts&BlendLayer_Top ? 2 : 0;
    1415            0 :         addcubeverts(vslot, mf.orient, pos, mf.tex, mf.verts, numverts, mf.tjoints, grassy, (mf.mat&Mat_Alpha)!=0, mf.numverts&BlendLayer_Blend);
    1416            0 :         vahasmerges |= Merge_Use;
    1417              :     }
    1418            0 :     mfl.clear();
    1419              : }
    1420              : 
    1421              : //recursively finds and adds decals to vacollect object
    1422            0 : void vacollect::finddecals(const vtxarray *va)
    1423              : {
    1424            0 :     if(va->hasmerges&(Merge_Origin|Merge_Part))
    1425              :     {
    1426            0 :         for(const octaentities * const i : va->decals)
    1427              :         {
    1428            0 :             extdecals.push_back(i);
    1429              :         }
    1430            0 :         for(const vtxarray * const i : va->children)
    1431              :         {
    1432            0 :             finddecals(i);
    1433              :         }
    1434              :     }
    1435            0 : }
    1436              : 
    1437            0 : void vacollect::rendercube(cube &c, const ivec &co, int size, int csi, int &maxlevel)
    1438              : {
    1439            0 :     if(c.ext && c.ext->va)
    1440              :     {
    1441            0 :         maxlevel = std::max(maxlevel, c.ext->va->mergelevel);
    1442            0 :         finddecals(c.ext->va);
    1443            0 :         return; // don't re-render
    1444              :     }
    1445              : 
    1446            0 :     if(c.children)
    1447              :     {
    1448            0 :         neighborstack[++neighbordepth] = &(*c.children)[0];
    1449            0 :         c.escaped = 0;
    1450            0 :         for(int i = 0; i < 8; ++i)
    1451              :         {
    1452            0 :             ivec o(i, co, size/2);
    1453            0 :             int level = -1;
    1454            0 :             rendercube((*c.children)[i], o, size/2, csi-1, level);
    1455            0 :             if(level >= csi)
    1456              :             {
    1457            0 :                 c.escaped |= 1<<i;
    1458              :             }
    1459            0 :             maxlevel = std::max(maxlevel, level);
    1460              :         }
    1461            0 :         --neighbordepth;
    1462              : 
    1463            0 :         if(csi <= maxmergelevel && vamerges[csi].size())
    1464              :         {
    1465            0 :             addmergedverts(csi, co);
    1466              :         }
    1467            0 :         if(c.ext && c.ext->ents)
    1468              :         {
    1469            0 :             if(c.ext->ents->mapmodels.size())
    1470              :             {
    1471            0 :                 mapmodels.push_back(c.ext->ents);
    1472              :             }
    1473            0 :             if(c.ext->ents->decals.size())
    1474              :             {
    1475            0 :                 decals.push_back(c.ext->ents);
    1476              :             }
    1477              :         }
    1478            0 :         return;
    1479              :     }
    1480              : 
    1481            0 :     if(!(c.isempty()))
    1482              :     {
    1483            0 :         gencubeverts(c, co, size);
    1484            0 :         if(c.merged)
    1485              :         {
    1486            0 :             maxlevel = std::max(maxlevel, genmergedfaces(c, co, size));
    1487              :         }
    1488              :     }
    1489            0 :     if(c.material != Mat_Air)
    1490              :     {
    1491            0 :         genmatsurfs(c, co, size, matsurfs);
    1492              :     }
    1493            0 :     if(c.ext && c.ext->ents)
    1494              :     {
    1495            0 :         if(c.ext->ents->mapmodels.size())
    1496              :         {
    1497            0 :             mapmodels.push_back(c.ext->ents);
    1498              :         }
    1499            0 :         if(c.ext->ents->decals.size())
    1500              :         {
    1501            0 :             decals.push_back(c.ext->ents);
    1502              :         }
    1503              :     }
    1504              : 
    1505            0 :     if(csi <= maxmergelevel && vamerges[csi].size())
    1506              :     {
    1507            0 :         addmergedverts(csi, co);
    1508              :     }
    1509              : }
    1510              : 
    1511            0 : void vacollect::calcgeombb(const ivec &co, int size, ivec &bbmin, ivec &bbmax) const
    1512              : {
    1513            0 :     vec vmin(co),
    1514            0 :         vmax = vmin;
    1515            0 :     vmin.add(size);
    1516              : 
    1517            0 :     for(size_t i = 0; i < verts.size(); i++)
    1518              :     {
    1519            0 :         const vec &v = verts[i].pos;
    1520            0 :         vmin.min(v);
    1521            0 :         vmax.max(v);
    1522              :     }
    1523              : 
    1524            0 :     bbmin = ivec(vmin.mul(8)).shr(3);
    1525            0 :     bbmax = ivec(vmax.mul(8)).add(7).shr(3);
    1526            0 : }
    1527              : 
    1528            0 : void vacollect::setva(cube &c, const ivec &co, int sz, int csi)
    1529              : {
    1530              :     int vamergeoffset[maxmergelevel+1];
    1531            0 :     for(int i = 0; i < maxmergelevel+1; ++i)
    1532              :     {
    1533            0 :         vamergeoffset[i] = vamerges[i].size();
    1534              :     }
    1535            0 :     size = sz;
    1536            0 :     for(octaentities *oe : entstack)
    1537              :     {
    1538            0 :         if(oe->decals.size())
    1539              :         {
    1540            0 :             extdecals.push_back(oe);
    1541              :         }
    1542              :     }
    1543            0 :     int maxlevel = -1;
    1544            0 :     rendercube(c, co, size, csi, maxlevel);
    1545              :     //this is what determines the root VA cubes:
    1546            0 :     if(size == std::min(vamaxsize, rootworld.mapsize()/2) || !emptyva())
    1547              :     {
    1548            0 :         vtxarray *va = newva(co, size);
    1549            0 :         ext(c).va = va;
    1550            0 :         calcgeombb(co, size, va->geommin, va->geommax);
    1551            0 :         calcmatbb(va, co, size, matsurfs);
    1552            0 :         va->hasmerges = vahasmerges;
    1553            0 :         va->mergelevel = vamergemax;
    1554              :     }
    1555              :     else
    1556              :     {
    1557            0 :         for(int i = 0; i < maxmergelevel+1; ++i)
    1558              :         {
    1559            0 :             vamerges[i].resize(vamergeoffset[i]);
    1560              :         }
    1561              :     }
    1562            0 :     clear();
    1563            0 : }
    1564              : 
    1565            0 : void vacollect::calctexgen(const VSlot &vslot, int orient, vec4<float> &sgen, vec4<float> &tgen)
    1566              : {
    1567            0 :     const Texture *tex = vslot.slot->sts.empty() ? notexture : vslot.slot->sts[0].t;
    1568            0 :     const texrotation &r = texrotations[vslot.rotation];
    1569            0 :     float k = defaulttexscale/vslot.scale,
    1570            0 :           xs = r.flipx ? -tex->xs : tex->xs,
    1571            0 :           ys = r.flipy ? -tex->ys : tex->ys,
    1572            0 :           sk = k/xs, tk = k/ys,
    1573            0 :           soff = -(r.swapxy ? vslot.offset.y() : vslot.offset.x())/xs,
    1574            0 :           toff = -(r.swapxy ? vslot.offset.x() : vslot.offset.y())/ys;
    1575            0 :     sgen = vec4<float>(0, 0, 0, soff);
    1576            0 :     tgen = vec4<float>(0, 0, 0, toff);
    1577            0 :     if(r.swapxy)
    1578              :     {
    1579            0 :         switch(orient)
    1580              :         {
    1581            0 :             case 0:
    1582              :             {
    1583            0 :                 sgen.z = -sk;
    1584            0 :                 tgen.y =  tk;
    1585            0 :                 break;
    1586              :             }
    1587            0 :             case 1:
    1588              :             {
    1589            0 :                 sgen.z = -sk;
    1590            0 :                 tgen.y = -tk;
    1591            0 :                 break;
    1592              :             }
    1593            0 :             case 2:
    1594              :             {
    1595            0 :                 sgen.z = -sk;
    1596            0 :                 tgen.x = -tk;
    1597            0 :                 break;
    1598              :             }
    1599            0 :             case 3:
    1600              :             {
    1601            0 :                 sgen.z = -sk;
    1602            0 :                 tgen.x =  tk;
    1603            0 :                 break;
    1604              :             }
    1605            0 :             case 4:
    1606              :             {
    1607            0 :                 sgen.y = -sk;
    1608            0 :                 tgen.x =  tk;
    1609            0 :                 break;
    1610              :             }
    1611            0 :             case 5:
    1612              :             {
    1613            0 :                 sgen.y =  sk;
    1614            0 :                 tgen.x =  tk;
    1615            0 :                 break;
    1616              :             }
    1617              :         }
    1618              :     }
    1619              :     else
    1620              :     {
    1621            0 :         switch(orient)
    1622              :         {
    1623            0 :             case 0:
    1624              :             {
    1625            0 :                 sgen.y =  sk;
    1626            0 :                 tgen.z = -tk;
    1627            0 :                 break;
    1628              :             }
    1629            0 :             case 1:
    1630              :             {
    1631            0 :                 sgen.y = -sk;
    1632            0 :                 tgen.z = -tk;
    1633            0 :                 break;
    1634              :             }
    1635            0 :             case 2:
    1636              :             {
    1637            0 :                 sgen.x = -sk;
    1638            0 :                 tgen.z = -tk;
    1639            0 :                 break;
    1640              :             }
    1641            0 :             case 3:
    1642              :             {
    1643            0 :                 sgen.x =  sk;
    1644            0 :                 tgen.z = -tk;
    1645            0 :                 break;
    1646              :             }
    1647            0 :             case 4:
    1648              :             {
    1649            0 :                 sgen.x =  sk;
    1650            0 :                 tgen.y = -tk;
    1651            0 :                 break;
    1652              :             }
    1653            0 :             case 5:
    1654              :             {
    1655            0 :                 sgen.x =  sk;
    1656            0 :                 tgen.y =  tk;
    1657            0 :                 break;
    1658              :             }
    1659              :         }
    1660              :     }
    1661            0 : }
    1662              : 
    1663            0 : void vacollect::addcubeverts(VSlot &vslot, int orient, const vec *pos, ushort texture, const vertinfo *vinfo, int numverts, int tj, int grassy, bool alpha, int layer)
    1664              : {
    1665              :     // [rotation][orient]
    1666              :     const vec orientation_tangent[8][6] =
    1667              :     {
    1668              :         { vec( 0,  1,  0), vec( 0, -1,  0), vec(-1,  0,  0), vec( 1,  0,  0), vec( 1,  0,  0), vec( 1,  0,  0) },
    1669              :         { vec( 0,  0, -1), vec( 0,  0, -1), vec( 0,  0, -1), vec( 0,  0, -1), vec( 0, -1,  0), vec( 0,  1,  0) },
    1670              :         { vec( 0, -1,  0), vec( 0,  1,  0), vec( 1,  0,  0), vec(-1,  0,  0), vec(-1,  0,  0), vec(-1,  0,  0) },
    1671              :         { vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  1,  0), vec( 0, -1,  0) },
    1672              :         { vec( 0, -1,  0), vec( 0,  1,  0), vec( 1,  0,  0), vec(-1,  0,  0), vec(-1,  0,  0), vec(-1,  0,  0) },
    1673              :         { vec( 0,  1,  0), vec( 0, -1,  0), vec(-1,  0,  0), vec( 1,  0,  0), vec( 1,  0,  0), vec( 1,  0,  0) },
    1674              :         { vec( 0,  0, -1), vec( 0,  0, -1), vec( 0,  0, -1), vec( 0,  0, -1), vec( 0, -1,  0), vec( 0,  1,  0) },
    1675              :         { vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  1,  0), vec( 0, -1,  0) },
    1676            0 :     };
    1677              : 
    1678            0 :     vec4<float> sgen, tgen;
    1679            0 :     calctexgen(vslot, orient, sgen, tgen);
    1680            0 :     vertex verts[Face_MaxVerts];
    1681              :     int index[Face_MaxVerts];
    1682            0 :     vec normals[Face_MaxVerts];
    1683            0 :     for(int k = 0; k < numverts; ++k)
    1684              :     {
    1685            0 :         vertex &v = verts[k];
    1686            0 :         v.pos = pos[k];
    1687            0 :         v.tc = vec(sgen.dot(v.pos), tgen.dot(v.pos), 0);
    1688            0 :         if(vinfo && vinfo[k].norm)
    1689              :         {
    1690            0 :             vec n = decodenormal(vinfo[k].norm),
    1691            0 :                 t = orientation_tangent[vslot.rotation][orient];
    1692            0 :             t.project(n).normalize();
    1693            0 :             v.norm = bvec(n);
    1694            0 :             v.tangent = vec4<uchar>(bvec(t), orientation_bitangent[vslot.rotation][orient].scalartriple(n, t) < 0 ? 0 : 255);
    1695            0 :         }
    1696            0 :         else if(texture != Default_Sky)
    1697              :         {
    1698            0 :             if(!k)
    1699              :             {
    1700            0 :                 guessnormals(pos, numverts, normals);
    1701              :             }
    1702            0 :             const vec &n = normals[k];
    1703            0 :             vec t = orientation_tangent[vslot.rotation][orient];
    1704            0 :             t.project(n).normalize();
    1705            0 :             v.norm = bvec(n);
    1706            0 :             v.tangent = vec4<uchar>(bvec(t), orientation_bitangent[vslot.rotation][orient].scalartriple(n, t) < 0 ? 0 : 255);
    1707              :         }
    1708              :         else
    1709              :         {
    1710            0 :             v.norm = bvec(128, 128, 255);
    1711            0 :             v.tangent = vec4<uchar>(255, 128, 128, 255);
    1712              :         }
    1713            0 :         index[k] = addvert(v);
    1714            0 :         if(index[k] < 0)
    1715              :         {
    1716            0 :             return;
    1717              :         }
    1718              :     }
    1719              : 
    1720            0 :     if(alpha)
    1721              :     {
    1722            0 :         for(int k = 0; k < numverts; ++k)
    1723              :         {
    1724            0 :             alphamin.min(pos[k]);
    1725            0 :             alphamax.max(pos[k]);
    1726              :         }
    1727            0 :         if(vslot.refractscale > 0)
    1728              :         {
    1729            0 :             for(int k = 0; k < numverts; ++k)
    1730              :             {
    1731            0 :                 refractmin.min(pos[k]);
    1732            0 :                 refractmax.max(pos[k]);
    1733              :             }
    1734              :         }
    1735              :     }
    1736            0 :     if(texture == Default_Sky)
    1737              :     {
    1738            0 :         for(int i = 0; i < numverts; ++i)
    1739              :         {
    1740            0 :             if(pos[i][orient>>1] != ((orient&1)<<rootworld.mapscale()))
    1741              :             {
    1742            0 :                 for(int k = 0; k < numverts; ++k)
    1743              :                 {
    1744            0 :                     skymin.min(pos[k]);
    1745            0 :                     skymax.max(pos[k]);
    1746              :                 }
    1747            0 :                 break;
    1748              :             }
    1749              :         }
    1750              :     }
    1751            0 :     sortkey key(texture, vslot.scroll.iszero() ? Orient_Any : orient, layer&BlendLayer_Bottom ? layer : BlendLayer_Top, alpha ? (vslot.refractscale > 0 ? Alpha_Refract : (vslot.alphaback ? Alpha_Back : Alpha_Front)) : Alpha_None);
    1752            0 :     addtris(vslot, orient, key, verts, index, numverts, tj);
    1753            0 :     if(grassy)
    1754              :     {
    1755            0 :         for(int i = 0; i < numverts-2; i += 2)
    1756              :         {
    1757            0 :             int faces = 0;
    1758            0 :             if(index[0]!=index[i+1] && index[i+1]!=index[i+2] && index[i+2]!=index[0])
    1759              :             {
    1760            0 :                 faces |= 1;
    1761              :             }
    1762            0 :             if(i+3 < numverts && index[0]!=index[i+2] && index[i+2]!=index[i+3] && index[i+3]!=index[0])
    1763              :             {
    1764            0 :                 faces |= 2;
    1765              :             }
    1766            0 :             if(grassy > 1 && faces==3)
    1767              :             {
    1768            0 :                 addgrasstri(i, verts, 4, texture);
    1769              :             }
    1770              :             else
    1771              :             {
    1772            0 :                 if(faces&1)
    1773              :                 {
    1774            0 :                     addgrasstri(i, verts, 3, texture);
    1775              :                 }
    1776            0 :                 if(faces&2)
    1777              :                 {
    1778            0 :                     addgrasstri(i+1, verts, 3, texture);
    1779              :                 }
    1780              :             }
    1781              :         }
    1782              :     }
    1783              : }
    1784              : 
    1785              : //va settings, used in updateva below
    1786            0 : VARF(vafacemax, 64, 384, 256*256, rootworld.allchanged());
    1787            0 : VARF(vafacemin, 0, 96, 256*256, rootworld.allchanged());
    1788            0 : VARF(vacubesize, 32, 128, 0x1000, rootworld.allchanged()); //note that performance drops off at low values -> large numbers of VAs
    1789              : 
    1790              : //updates the va that contains the cube c
    1791            0 : int vacollect::updateva(std::array<cube, 8> &c, const ivec &co, int size, int csi)
    1792              : {
    1793            0 :     int ccount = 0,
    1794            0 :         cmergemax  = vamergemax,
    1795            0 :         chasmerges = vahasmerges;
    1796            0 :     neighborstack[++neighbordepth] = &c[0];
    1797            0 :     for(int i = 0; i < 8; ++i)                                  // counting number of semi-solid/solid children cubes
    1798              :     {
    1799            0 :         int count = 0,
    1800            0 :             childpos = varoot.size();
    1801            0 :         ivec o(i, co, size);                                    //translate cube vector to world vector
    1802            0 :         vamergemax = 0;
    1803            0 :         vahasmerges = 0;
    1804            0 :         if(c[i].ext && c[i].ext->va)
    1805              :         {
    1806            0 :             varoot.push_back(c[i].ext->va);
    1807            0 :             if(c[i].ext->va->hasmerges&Merge_Origin)
    1808              :             {
    1809            0 :                 findmergedfaces(c[i], o, size, csi, csi);
    1810              :             }
    1811              :         }
    1812              :         else
    1813              :         {
    1814            0 :             if(c[i].children)
    1815              :             {
    1816            0 :                 if(c[i].ext && c[i].ext->ents)
    1817              :                 {
    1818            0 :                     entstack.push_back(c[i].ext->ents);
    1819              :                 }
    1820            0 :                 count += updateva(*c[i].children, o, size/2, csi-1);
    1821            0 :                 if(c[i].ext && c[i].ext->ents)
    1822              :                 {
    1823            0 :                     entstack.pop_back();
    1824              :                 }
    1825              :             }
    1826              :             else
    1827              :             {
    1828            0 :                 count += setcubevisibility(c[i], o, size);
    1829              :             }
    1830            0 :             int tcount = count + (csi <= maxmergelevel ? vamerges[csi].size() : 0);
    1831            0 :             if(tcount > vafacemax || (tcount >= vafacemin && size >= vacubesize) || size == std::min(0x1000, rootworld.mapsize()/2))
    1832              :             {
    1833            0 :                 setva(c[i], o, size, csi);
    1834            0 :                 if(c[i].ext && c[i].ext->va)
    1835              :                 {
    1836            0 :                     while(static_cast<long>(varoot.size()) > childpos)
    1837              :                     {
    1838            0 :                         vtxarray *child = varoot.back();
    1839            0 :                         varoot.pop_back();
    1840            0 :                         c[i].ext->va->children.push_back(child);
    1841            0 :                         child->parent = c[i].ext->va;
    1842              :                     }
    1843            0 :                     varoot.push_back(c[i].ext->va);
    1844            0 :                     if(vamergemax > size)
    1845              :                     {
    1846            0 :                         cmergemax = std::max(cmergemax, vamergemax);
    1847            0 :                         chasmerges |= vahasmerges&~Merge_Use;
    1848              :                     }
    1849            0 :                     continue;
    1850            0 :                 }
    1851              :                 else
    1852              :                 {
    1853            0 :                     count = 0;
    1854              :                 }
    1855              :             }
    1856              :         }
    1857            0 :         if(csi+1 <= maxmergelevel && vamerges[csi].size())
    1858              :         {
    1859            0 :             vamerges[csi+1].swap(vamerges[csi]);
    1860              :         }
    1861            0 :         cmergemax = std::max(cmergemax, vamergemax);
    1862            0 :         chasmerges |= vahasmerges;
    1863            0 :         ccount += count;
    1864              :     }
    1865            0 :     --neighbordepth;
    1866            0 :     vamergemax = cmergemax;
    1867            0 :     vahasmerges = chasmerges;
    1868              : 
    1869            0 :     return ccount;
    1870              : }
    1871              : 
    1872            0 : void cubeworld::octarender()                               // creates va s for all leaf cubes that don't already have them
    1873              : {
    1874            0 :     int csi = 0;
    1875            0 :     while(1<<csi < mapsize())
    1876              :     {
    1877            0 :         csi++;
    1878              :     }
    1879            0 :     varoot.clear();
    1880            0 :     vc.updateva(*worldroot, ivec(0, 0, 0), mapsize()/2, csi-1);
    1881            0 :     flushvbo();
    1882            0 :     explicitsky = false;
    1883            0 :     for(size_t i = 0; i < valist.size(); i++)
    1884              :     {
    1885            0 :         if(valist[i]->sky)
    1886              :         {
    1887            0 :             explicitsky = true;
    1888            0 :             break;
    1889              :         }
    1890              :     }
    1891            0 :     visibleva = nullptr;
    1892            0 : }
        

Generated by: LCOV version 2.0-1