LCOV - code coverage report
Current view: top level - engine/render - vacollect.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 8 990 0.8 %
Date: 2024-11-22 05:07:59 Functions: 2 44 4.5 %

          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          14 : 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.orient = k.orient;
     583           0 :             e.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(i, 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 1.14