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

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

Generated by: LCOV version 2.0-1