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

Generated by: LCOV version 2.0-1