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

Generated by: LCOV version 2.0-1