LCOV - code coverage report
Current view: top level - engine/world - octaworld.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 130 979 13.3 %
Date: 2024-11-22 05:07:59 Functions: 14 56 25.0 %

          Line data    Source code
       1             : // core world management routines
       2             : 
       3             : /* octaworld.cpp: the octree world
       4             :  *
       5             :  * the octree world structure, consisting of nested cube objects (with tex, geom info)
       6             :  * is defined here
       7             :  *
       8             :  * unlike vector worlds (like most engines), the octree relies on different mathmatical
       9             :  * semantics due to not being vector-based
      10             :  *
      11             :  * also handles geometry simplification, where octree nodes are combined to reduce rendering
      12             :  * overhead
      13             :  */
      14             : #include "../libprimis-headers/cube.h"
      15             : #include "../../shared/geomexts.h"
      16             : 
      17             : #include "light.h"
      18             : #include "octacube.h"
      19             : #include "octaworld.h"
      20             : #include "raycube.h"
      21             : #include "world.h"
      22             : 
      23             : #include "interface/console.h"
      24             : #include "interface/control.h"
      25             : 
      26             : #include "render/octarender.h"
      27             : #include "render/renderwindow.h"
      28             : 
      29             : int allocnodes = 0;
      30             : 
      31             : const uchar faceedgesidx[6][4] = // ordered edges surrounding each orient
      32             : {//0..1 = row edges, 2..3 = column edges
      33             :     { 4,  5,  8, 10 },
      34             :     { 6,  7,  9, 11 },
      35             :     { 8,  9,  0, 2 },
      36             :     { 10, 11, 1, 3 },
      37             :     { 0,  1,  4, 6 },
      38             :     { 2,  3,  5, 7 },
      39             : };
      40             : 
      41             : cubeworld rootworld;
      42             : 
      43           0 : cubeext *growcubeext(cubeext *old, int maxverts)
      44             : {
      45           0 :     cubeext *ext = reinterpret_cast<cubeext *>(new uchar[sizeof(cubeext) + maxverts*sizeof(vertinfo)]);
      46           0 :     if(old)
      47             :     {
      48           0 :         ext->va = old->va;
      49           0 :         ext->ents = old->ents;
      50           0 :         ext->tjoints = old->tjoints;
      51             :     }
      52             :     else
      53             :     {
      54           0 :         ext->va = nullptr;
      55           0 :         ext->ents = nullptr;
      56           0 :         ext->tjoints = -1;
      57             :     }
      58           0 :     ext->maxverts = maxverts;
      59           0 :     return ext;
      60             : }
      61             : 
      62           0 : void setcubeext(cube &c, cubeext *ext)
      63             : {
      64           0 :     cubeext *old = c.ext;
      65           0 :     if(old == ext)
      66             :     {
      67           0 :         return;
      68             :     }
      69           0 :     c.ext = ext;
      70           0 :     if(old)
      71             :     {
      72           0 :         delete[] reinterpret_cast<uchar *>(old);
      73             :     }
      74             : }
      75             : 
      76           0 : cubeext *newcubeext(cube &c, int maxverts, bool init)
      77             : {
      78           0 :     if(c.ext && c.ext->maxverts >= maxverts)
      79             :     {
      80           0 :         return c.ext;
      81             :     }
      82           0 :     cubeext *ext = growcubeext(c.ext, maxverts);
      83           0 :     if(init)
      84             :     {
      85           0 :         if(c.ext)
      86             :         {
      87           0 :             std::copy(ext->surfaces.begin(), ext->surfaces.end(), c.ext->surfaces.begin());
      88           0 :             std::memcpy(ext->verts(), c.ext->verts(), c.ext->maxverts*sizeof(vertinfo));
      89             :         }
      90             :         else
      91             :         {
      92           0 :             ext->surfaces.fill({0,0});
      93             :         }
      94             :     }
      95           0 :     setcubeext(c, ext);
      96           0 :     return ext;
      97             : }
      98             : 
      99             : /**
     100             :  * @brief Returns an octet of cubes allocated on the heap.
     101             :  *
     102             :  * These cubes should be freed with freeocta() to prevent a leak.
     103             :  *
     104             :  * `allocnodes` is incremented by one for each call of this function.
     105             :  *
     106             :  * @param face The face values to set the eight cubes
     107             :  * @param mat The material mask to assign to the cubes
     108             :  *
     109             :  * @return a std::array<cube, 8> pointer pointing to an array containing the created cubes
     110             :  */
     111           6 : std::array<cube, 8> *newcubes(uint face, int mat)
     112             : {
     113           6 :     std::array<cube, 8> *c = new std::array<cube, 8> ;
     114          54 :     for(int i = 0; i < 8; ++i)
     115             :     {
     116          48 :         (*c)[i].children = nullptr;
     117          48 :         (*c)[i].ext = nullptr;
     118          48 :         (*c)[i].visible = 0;
     119          48 :         (*c)[i].merged = 0;
     120          48 :         setcubefaces((*c)[i], face);
     121         336 :         for(int l = 0; l < 6; ++l) //note this is a loop l (level 4)
     122             :         {
     123         288 :             (*c)[i].texture[l] = Default_Geom;
     124             :         }
     125          48 :         (*c)[i].material = mat;
     126             :     }
     127           6 :     allocnodes++;
     128           6 :     return c;
     129             : }
     130             : 
     131             : //returns the size of the tree starting from the specified cube going down
     132             : //the cube in question is counted as part of the family
     133          35 : int familysize(const cube &c)
     134             : {
     135          35 :     int size = 1;
     136          35 :     if(c.children)
     137             :     {
     138          36 :         for(int i = 0; i < 8; ++i)
     139             :         {
     140          32 :             size += familysize((*c.children)[i]);
     141             :         }
     142             :     }
     143          35 :     return size;
     144             : }
     145             : 
     146           1 : void freeocta(std::array<cube, 8> *&c)
     147             : {
     148           1 :     if(!c)
     149             :     {
     150           0 :         return;
     151             :     }
     152           9 :     for(int i = 0; i < 8; ++i)
     153             :     {
     154           8 :         (*c)[i].discardchildren();
     155             :     }
     156           1 :     delete c;
     157           1 :     c = nullptr;
     158           1 :     allocnodes--;
     159             : }
     160             : 
     161           1 : void getcubevector(const cube &c, int d, int x, int y, int z, ivec &p)
     162             : {
     163           1 :     ivec v(d, x, y, z);
     164           4 :     for(int i = 0; i < 3; ++i)
     165             :     {
     166           3 :         p[i] = EDGE_GET(CUBE_EDGE(c, i, v[R[i]], v[C[i]]), v[D[i]]);
     167             :     }
     168           1 : }
     169             : 
     170           0 : void setcubevector(cube &c, int d, int x, int y, int z, const ivec &p)
     171             : {
     172           0 :     ivec v(d, x, y, z);
     173           0 :     for(int i = 0; i < 3; ++i)
     174             :     {
     175           0 :         EDGE_SET(CUBE_EDGE(c, i, v[R[i]], v[C[i]]), v[D[i]], p[i]);
     176             :     }
     177           0 : }
     178             : 
     179           0 : static void getcubevector(const cube &c, int i, ivec &p)
     180             : {
     181           0 :     p.x = EDGE_GET(CUBE_EDGE(c, 0, (i>>R[0])&1, (i>>C[0])&1), (i>>D[0])&1);
     182           0 :     p.y = EDGE_GET(CUBE_EDGE(c, 1, (i>>R[1])&1, (i>>C[1])&1), (i>>D[1])&1);
     183           0 :     p.z = EDGE_GET(CUBE_EDGE(c, 2, (i>>R[2])&1, (i>>C[2])&1), (i>>D[2])&1);
     184           0 : }
     185             : 
     186           0 : static void setcubevector(cube &c, int i, const ivec &p)
     187             : {
     188           0 :     EDGE_SET(CUBE_EDGE(c, 0, (i>>R[0])&1, (i>>C[0])&1), (i>>D[0])&1, p.x);
     189           0 :     EDGE_SET(CUBE_EDGE(c, 1, (i>>R[1])&1, (i>>C[1])&1), (i>>D[1])&1, p.y);
     190           0 :     EDGE_SET(CUBE_EDGE(c, 2, (i>>R[2])&1, (i>>C[2])&1), (i>>D[2])&1, p.z);
     191           0 : }
     192             : 
     193           0 : void optiface(const uchar *p, cube &c)
     194             : {
     195           0 :     uint f = *reinterpret_cast<const uint *>(p);
     196           0 :     if(((f>>4)&0x0F0F0F0FU) == (f&0x0F0F0F0FU))
     197             :     {
     198           0 :         setcubefaces(c, faceempty);
     199             :     }
     200           0 : }
     201             : 
     202           1 : void printcube()
     203             : {
     204           1 :     const cube &c = rootworld.lookupcube(lu); // assume this is cube being pointed at
     205           1 :     conoutf(Console_Debug, "= %p = (%d, %d, %d) @ %d", static_cast<const void *>(&c), lu.x, lu.y, lu.z, lusize);
     206           1 :     conoutf(Console_Debug, " x  %.8x", c.faces[0]);
     207           1 :     conoutf(Console_Debug, " y  %.8x", c.faces[1]);
     208           1 :     conoutf(Console_Debug, " z  %.8x", c.faces[2]);
     209           1 : }
     210             : 
     211           0 : void validatec(std::array<cube, 8> *&c, int size)
     212             : {
     213           0 :     for(int i = 0; i < 8; ++i)
     214             :     {
     215           0 :         if((*c)[i].children)
     216             :         {
     217           0 :             if(size<=1)
     218             :             {
     219           0 :                 setcubefaces((*c)[i], facesolid);
     220           0 :                 (*c)[i].discardchildren(true);
     221             :             }
     222             :             else
     223             :             {
     224           0 :                 validatec((*c)[i].children, size>>1);
     225             :             }
     226             :         }
     227           0 :         else if(size > 0x1000)
     228             :         {
     229           0 :             subdividecube((*c)[i], true, false);
     230           0 :             validatec((*c)[i].children, size>>1);
     231             :         }
     232             :         else
     233             :         {
     234           0 :             for(int j = 0; j < 3; ++j)
     235             :             {
     236           0 :                 uint f  = (*c)[i].faces[j],
     237           0 :                      e0 = f&0x0F0F0F0FU,
     238           0 :                      e1 = (f>>4)&0x0F0F0F0FU;
     239           0 :                 if(e0 == e1 || ((e1+0x07070707U)|(e1-e0))&0xF0F0F0F0U)
     240             :                 {
     241           0 :                     setcubefaces((*c)[i], faceempty);
     242           0 :                     break;
     243             :                 }
     244             :             }
     245             :         }
     246             :     }
     247           0 : }
     248             : 
     249             : ivec lu;
     250             : int lusize;
     251             : 
     252           2 : cube &cubeworld::lookupcube(const ivec &to, int tsize, ivec &ro, int &rsize)
     253             : {
     254           2 :     int tx = std::clamp(to.x, 0, mapsize()-1),
     255           2 :         ty = std::clamp(to.y, 0, mapsize()-1),
     256           2 :         tz = std::clamp(to.z, 0, mapsize()-1);
     257           2 :     int scale = worldscale-1,
     258           2 :         csize = std::abs(tsize);
     259           2 :     cube *c = &(*worldroot)[OCTA_STEP(tx, ty, tz, scale)];
     260           2 :     if(!c)
     261             :     {
     262           2 :         return emptycube;
     263             :     }
     264           0 :     if(!(csize>>scale))
     265             :     {
     266             :         do
     267             :         {
     268           0 :             if(!c->children)
     269             :             {
     270           0 :                 if(tsize > 0)
     271             :                 {
     272             :                     do
     273             :                     {
     274           0 :                         subdividecube(*c);
     275           0 :                         scale--;
     276           0 :                         c = &(*c->children)[OCTA_STEP(tx, ty, tz, scale)];
     277           0 :                     } while(!(csize>>scale));
     278             :                 }
     279           0 :                 break;
     280             :             }
     281           0 :             scale--;
     282           0 :             c = &(*c->children)[OCTA_STEP(tx, ty, tz, scale)];
     283           0 :         } while(!(csize>>scale));
     284             :     }
     285           0 :     ro = ivec(tx, ty, tz).mask(~0U<<scale);
     286           0 :     rsize = 1<<scale;
     287           0 :     return *c;
     288             : }
     289             : 
     290           0 : int cubeworld::lookupmaterial(const vec &v)
     291             : {
     292           0 :     ivec o(v);
     293           0 :     if(!insideworld(o))
     294             :     {
     295           0 :         return Mat_Air;
     296             :     }
     297           0 :     int scale = worldscale-1;
     298           0 :     cube *c = &(*worldroot)[OCTA_STEP(o.x, o.y, o.z, scale)];
     299           0 :     while(c->children)
     300             :     {
     301           0 :         scale--;
     302           0 :         c = &(*c->children)[OCTA_STEP(o.x, o.y, o.z, scale)];
     303             :     }
     304           0 :     return c->material;
     305             : }
     306             : 
     307             : const cube *neighborstack[32];
     308             : int neighbordepth = -1;
     309             : 
     310          90 : const cube &cubeworld::neighborcube(int orient, const ivec &co, int size, ivec &ro, int &rsize)
     311             : {
     312          90 :     ivec n = co;
     313          90 :     int dim = DIMENSION(orient);
     314          90 :     uint diff = n[dim];
     315          90 :     if(DIM_COORD(orient))
     316             :     {
     317          45 :         n[dim] += size;
     318             :     }
     319             :     else
     320             :     {
     321          45 :         n[dim] -= size;
     322             :     }
     323          90 :     diff ^= n[dim];
     324          90 :     if(diff >= static_cast<uint>(mapsize()))
     325             :     {
     326          90 :         ro = n;
     327          90 :         rsize = size;
     328          90 :         return emptycube;
     329             :     }
     330           0 :     int scale = worldscale;
     331           0 :     const cube *nc = &(*worldroot)[0];
     332           0 :     if(neighbordepth >= 0)
     333             :     {
     334           0 :         scale -= neighbordepth + 1;
     335           0 :         diff >>= scale;
     336             :         do
     337             :         {
     338           0 :             scale++;
     339           0 :             diff >>= 1;
     340           0 :         } while(diff);
     341           0 :         nc = neighborstack[worldscale - scale];
     342             :     }
     343           0 :     scale--;
     344           0 :     nc = &nc[OCTA_STEP(n.x, n.y, n.z, scale)];
     345           0 :     if(!(size>>scale) && nc->children)
     346             :     {
     347             :         do
     348             :         {
     349           0 :             scale--;
     350           0 :             nc = &(*nc->children)[OCTA_STEP(n.x, n.y, n.z, scale)];
     351           0 :         } while(!(size>>scale) && nc->children);
     352             :     }
     353           0 :     ro = n.mask(~0U<<scale);
     354           0 :     rsize = 1<<scale;
     355           0 :     return *nc;
     356             : }
     357             : 
     358             : /**
     359             :  * @brief Returns the index (0-7) of a given cube.
     360             :  *
     361             :  * for a given dimension (x,y,z) orientation and 0/1 for x, y, and z, returns the
     362             :  * cube child index (0-7) in the cube array
     363             :  *
     364             :  * @param d the dimension to look from
     365             :  * @param x position in x direction (0/1)
     366             :  * @param y position in y direction (0/1)
     367             :  * @param z position in z direction (0/1)
     368             :  * @return an integer between 0 and 7 corresponding to the cube child index of the given cube
     369             :  */
     370           0 : static int octacubeindex(int d, int x, int y, int z)
     371             : {
     372           0 :     return (((z)<<D[d])+
     373           0 :             ((y)<<C[d])+
     374           0 :             ((x)<<R[d]));
     375             : }
     376             : 
     377             : ////////// (re)mip //////////
     378             : 
     379           0 : int getmippedtexture(const cube &p, int orient)
     380             : {
     381           0 :     std::array<cube, 8> &c = *p.children;
     382           0 :     int d = DIMENSION(orient),
     383           0 :         dc = DIM_COORD(orient),
     384           0 :         texs[4] = { -1, -1, -1, -1 },
     385           0 :         numtexs = 0;
     386           0 :     for(int x = 0; x < 2; ++x)
     387             :     {
     388           0 :         for(int y = 0; y < 2; ++y)
     389             :         {
     390           0 :             int n = octacubeindex(d, x, y, dc);
     391           0 :             if(c[n].isempty())
     392             :             {
     393           0 :                 n = n^octadim(D[d]);
     394           0 :                 if(c[n].isempty())
     395             :                 {
     396           0 :                     continue;
     397             :                 }
     398             :             }
     399           0 :             int tex = c[n].texture[orient];
     400           0 :             if(tex > Default_Sky)
     401             :             {
     402           0 :                 for(int i = 0; i < numtexs; ++i)
     403             :                 {
     404           0 :                     if(texs[i] == tex)
     405             :                     {
     406           0 :                         return tex;
     407             :                     }
     408             :                 }
     409             :             }
     410           0 :             texs[numtexs++] = tex;
     411             :         }
     412             :     }
     413           0 :     for(int i = numtexs; --i >= 0;) //note reverse iteration
     414             :     {
     415           0 :         if(!i || texs[i] > Default_Sky)
     416             :         {
     417           0 :             return texs[i];
     418             :         }
     419             :     }
     420           0 :     return Default_Geom;
     421             : }
     422             : 
     423           0 : void forcemip(cube &c, bool fixtex)
     424             : {
     425           0 :     std::array<cube, 8> &ch = *c.children;
     426           0 :     setcubefaces(c, faceempty);
     427           0 :     for(int i = 0; i < 8; ++i)
     428             :     {
     429           0 :         for(int j = 0; j < 8; ++j)
     430             :         {
     431           0 :             int n = i^(j==3 ? 4 : (j==4 ? 3 : j));
     432           0 :             if(!(ch[n].isempty())) // breadth first search for cube near vert
     433             :             {
     434           0 :                 ivec v;
     435           0 :                 getcubevector(ch[n], i, v);
     436             :                 // adjust vert to parent size
     437           0 :                 setcubevector(c, i, ivec(n, v, 8).shr(1));
     438           0 :                 break;
     439             :             }
     440             :         }
     441             :     }
     442           0 :     if(fixtex)
     443             :     {
     444           0 :         for(int j = 0; j < 6; ++j)
     445             :         {
     446           0 :             c.texture[j] = getmippedtexture(c, j);
     447             :         }
     448             :     }
     449           0 : }
     450             : 
     451           0 : static int midedge(const ivec &a, const ivec &b, int xd, int yd, bool &perfect)
     452             : {
     453           0 :     int ax = a[xd],
     454           0 :         ay = a[yd],
     455           0 :         bx = b[xd],
     456           0 :         by = b[yd];
     457           0 :     if(ay==by)
     458             :     {
     459           0 :         return ay;
     460             :     }
     461           0 :     if(ax==bx)
     462             :     {
     463           0 :         perfect = false;
     464           0 :         return ay;
     465             :     }
     466           0 :     bool crossx = (ax<8 && bx>8) || (ax>8 && bx<8),
     467           0 :          crossy = (ay<8 && by>8) || (ay>8 && by<8);
     468           0 :     if(crossy && !crossx)
     469             :     {
     470           0 :         midedge(a,b,yd,xd,perfect);
     471           0 :         return 8;
     472             :     } // to test perfection
     473           0 :     if(ax<=8 && bx<=8)
     474             :     {
     475           0 :         return ax>bx ? ay : by;
     476             :     }
     477           0 :     if(ax>=8 && bx>=8)
     478             :     {
     479           0 :         return ax<bx ? ay : by;
     480             :     }
     481           0 :     int risex = (by-ay)*(8-ax)*256,
     482           0 :         s = risex/(bx-ax),
     483           0 :         y = s/256 + ay;
     484           0 :     if(((std::abs(s)&0xFF)!=0) || // ie: rounding error
     485           0 :         (crossy && y!=8) ||
     486           0 :         (y<0 || y>16))
     487             :     {
     488           0 :         perfect = false;
     489             :     }
     490           0 :     return crossy ? 8 : std::min(std::max(y, 0), 16);
     491             : }
     492             : 
     493           0 : static bool crosscenter(const ivec &a, const ivec &b, int xd, int yd)
     494             : {
     495           0 :     int ax = a[xd],
     496           0 :         ay = a[yd],
     497           0 :         bx = b[xd],
     498           0 :         by = b[yd];
     499           0 :     return (((ax <= 8 && bx <= 8) || (ax >= 8 && bx >= 8)) && ((ay <= 8 && by <= 8) || (ay >= 8 && by >= 8))) ||
     500           0 :            (ax + bx == 16 && ay + by == 16);
     501             : }
     502             : 
     503           0 : bool subdividecube(cube &c, bool fullcheck, bool brighten)
     504             : {
     505           0 :     if(c.children)
     506             :     {
     507           0 :         return true;
     508             :     }
     509           0 :     if(c.ext)
     510             :     {
     511           0 :         c.ext->surfaces.fill({0,0});
     512             :     }
     513           0 :     if(c.isempty() || c.issolid())
     514             :     {
     515           0 :         c.children = newcubes(c.isempty() ? faceempty : facesolid, c.material);
     516           0 :         for(int i = 0; i < 8; ++i)
     517             :         {
     518           0 :             for(int l = 0; l < 6; ++l) //note this is a loop l (level 4)
     519             :             {
     520           0 :                 (*c.children)[i].texture[l] = c.texture[l];
     521             :             }
     522           0 :             if(brighten && !(c.isempty()))
     523             :             {
     524           0 :                 brightencube((*c.children)[i]);
     525             :             }
     526             :         }
     527           0 :         return true;
     528             :     }
     529           0 :     c.children = newcubes(facesolid, c.material);
     530           0 :     std::array<cube, 8> &ch = *c.children;
     531           0 :     bool perfect = true;
     532           0 :     ivec v[8];
     533           0 :     for(int i = 0; i < 8; ++i)
     534             :     {
     535           0 :         getcubevector(c, i, v[i]);
     536           0 :         v[i].mul(2);
     537             :     }
     538             : 
     539           0 :     for(int j = 0; j < 6; ++j)
     540             :     {
     541           0 :         int d = DIMENSION(j),
     542           0 :             z = DIM_COORD(j);
     543           0 :         const ivec &v00 = v[octacubeindex(d, 0, 0, z)],
     544           0 :                    &v10 = v[octacubeindex(d, 1, 0, z)],
     545           0 :                    &v01 = v[octacubeindex(d, 0, 1, z)],
     546           0 :                    &v11 = v[octacubeindex(d, 1, 1, z)];
     547             :         int e[3][3];
     548             :         // corners
     549           0 :         e[0][0] = v00[d];
     550           0 :         e[0][2] = v01[d];
     551           0 :         e[2][0] = v10[d];
     552           0 :         e[2][2] = v11[d];
     553             :         // edges
     554           0 :         e[0][1] = midedge(v00, v01, C[d], d, perfect);
     555           0 :         e[1][0] = midedge(v00, v10, R[d], d, perfect);
     556           0 :         e[1][2] = midedge(v11, v01, R[d], d, perfect);
     557           0 :         e[2][1] = midedge(v11, v10, C[d], d, perfect);
     558             :         // center
     559           0 :         bool p1 = perfect,
     560           0 :              p2 = perfect;
     561           0 :         int c1 = midedge(v00, v11, R[d], d, p1),
     562           0 :             c2 = midedge(v01, v10, R[d], d, p2);
     563           0 :         if(z ? c1 > c2 : c1 < c2)
     564             :         {
     565           0 :             e[1][1] = c1;
     566           0 :             perfect = p1 && (c1 == c2 || crosscenter(v00, v11, C[d], R[d]));
     567             :         }
     568             :         else
     569             :         {
     570           0 :             e[1][1] = c2;
     571           0 :             perfect = p2 && (c1 == c2 || crosscenter(v01, v10, C[d], R[d]));
     572             :         }
     573             : 
     574           0 :         for(int i = 0; i < 8; ++i)
     575             :         {
     576           0 :             ch[i].texture[j] = c.texture[j];
     577           0 :             int rd = (i>>R[d])&1,
     578           0 :                 cd = (i>>C[d])&1,
     579           0 :                 dd = (i>>D[d])&1;
     580           0 :             EDGE_SET(CUBE_EDGE(ch[i], d, 0, 0), z, std::clamp(e[rd][cd] - dd*8, 0, 8));
     581           0 :             EDGE_SET(CUBE_EDGE(ch[i], d, 1, 0), z, std::clamp(e[1+rd][cd] - dd*8, 0, 8));
     582           0 :             EDGE_SET(CUBE_EDGE(ch[i], d, 0, 1), z, std::clamp(e[rd][1+cd] - dd*8, 0, 8));
     583           0 :             EDGE_SET(CUBE_EDGE(ch[i], d, 1, 1), z, std::clamp(e[1+rd][1+cd] - dd*8, 0, 8));
     584             :         }
     585             :     }
     586             : 
     587           0 :     validatec(c.children);
     588           0 :     if(fullcheck)
     589             :     {
     590           0 :         for(int i = 0; i < 8; ++i)
     591             :         {
     592           0 :             if(!ch[i].isvalidcube()) // not so good...
     593             :             {
     594           0 :                 setcubefaces(ch[i], faceempty);
     595           0 :                 perfect=false;
     596             :             }
     597             :         }
     598             :     }
     599           0 :     if(brighten)
     600             :     {
     601           0 :         for(int i = 0; i < 8; ++i)
     602             :         {
     603           0 :             if(!(ch[i].isempty()))
     604             :             {
     605           0 :                 brightencube(ch[i]);
     606             :             }
     607             :         }
     608             :     }
     609           0 :     return perfect;
     610             : }
     611             : 
     612           0 : static bool crushededge(uchar e, int dc)
     613             : {
     614           0 :     return dc ? e==0 : e==0x88;
     615             : }
     616             : 
     617           0 : int visibleorient(const cube &c, int orient)
     618             : {
     619           0 :     for(int i = 0; i < 2; ++i)
     620             :     {
     621           0 :         int a = faceedgesidx[orient][i*2 + 0],
     622           0 :             b = faceedgesidx[orient][i*2 + 1];
     623           0 :         for(int j = 0; j < 2; ++j)
     624             :         {
     625           0 :             if(crushededge(c.edges[a],j) &&
     626           0 :                crushededge(c.edges[b],j) &&
     627           0 :                 touchingface(c, orient))
     628             :             {
     629           0 :                 return ((a>>2)<<1) + j;
     630             :             }
     631             :         }
     632             :     }
     633           0 :     return orient;
     634             : }
     635             : 
     636             : VAR(mipvis, 0, 0, 1);
     637             : 
     638           0 : static bool remip(cube &c, const ivec &co, int size)
     639             : {
     640           0 :     std::array<cube, 8> *ch = nullptr;
     641           0 :     if(!c.children)
     642             :     {
     643           0 :         if(size<<1 <= 0x1000)
     644             :         {
     645           0 :             return true;
     646             :         }
     647           0 :         subdividecube(c);
     648           0 :         ch = c.children;
     649             :     }
     650             :     else
     651             :     {
     652           0 :         ch = c.children;
     653             :     }
     654           0 :     bool perfect = true;
     655           0 :     for(int i = 0; i < 8; ++i)
     656             :     {
     657           0 :         ivec o(i, co, size);
     658           0 :         if(!remip((*ch)[i], o, size>>1))
     659             :         {
     660           0 :             perfect = false;
     661             :         }
     662             :     }
     663           0 :     setcubefaces(c, facesolid); // so texmip is more consistent
     664           0 :     for(int j = 0; j < 6; ++j)
     665             :     {
     666           0 :         c.texture[j] = getmippedtexture(c, j); // parents get child texs regardless
     667             :     }
     668           0 :     if(!perfect)
     669             :     {
     670           0 :         return false;
     671             :     }
     672           0 :     if(size<<1 > 0x1000)
     673             :     {
     674           0 :         return false;
     675             :     }
     676           0 :     ushort mat = Mat_Air;
     677           0 :     for(int i = 0; i < 8; ++i)
     678             :     {
     679           0 :         mat = (*ch)[i].material;
     680           0 :         if((mat&MatFlag_Clip) == Mat_NoClip || mat&Mat_Alpha)
     681             :         {
     682           0 :             if(i > 0)
     683             :             {
     684           0 :                 return false;
     685             :             }
     686           0 :             while(++i < 8)
     687             :             {
     688           0 :                 if((*ch)[i].material != mat)
     689             :                 {
     690           0 :                     return false;
     691             :                 }
     692             :             }
     693           0 :             break;
     694             :         }
     695           0 :         else if(!((*ch)[i].issolid()))
     696             :         {
     697           0 :             while(++i < 8)
     698             :             {
     699           0 :                 int omat = (*ch)[i].material;
     700           0 :                 if((*ch)[i].issolid() ? (omat&MatFlag_Clip) == Mat_NoClip || omat&Mat_Alpha : mat != omat)
     701             :                 {
     702           0 :                     return false;
     703             :                 }
     704             :             }
     705           0 :             break;
     706             :         }
     707             :     }
     708           0 :     cube n = c;
     709           0 :     n.ext = nullptr;
     710           0 :     forcemip(n);
     711           0 :     n.children = nullptr;
     712           0 :     if(!subdividecube(n, false, false))
     713             :     {
     714           0 :         freeocta(n.children);
     715           0 :         return false;
     716             :     }
     717           0 :     std::array<cube, 8> *nh = n.children;
     718           0 :     uchar vis[6] = {0, 0, 0, 0, 0, 0};
     719           0 :     for(int i = 0; i < 8; ++i)
     720             :     {
     721           0 :         if((*ch)[i].faces[0] != (*nh)[i].faces[0] ||
     722           0 :            (*ch)[i].faces[1] != (*nh)[i].faces[1] ||
     723           0 :            (*ch)[i].faces[2] != (*nh)[i].faces[2])
     724             :         {
     725           0 :             freeocta(nh);
     726           0 :             return false;
     727             :         }
     728             : 
     729           0 :         if((*ch)[i].isempty() && (*nh)[i].isempty())
     730             :         {
     731           0 :             continue;
     732             :         }
     733             : 
     734           0 :         ivec o(i, co, size);
     735           0 :         for(int orient = 0; orient < 6; ++orient)
     736           0 :             if(visibleface((*ch)[i], orient, o, size, Mat_Air, (mat&Mat_Alpha)^Mat_Alpha, Mat_Alpha))
     737             :             {
     738           0 :                 if((*ch)[i].texture[orient] != n.texture[orient])
     739             :                 {
     740           0 :                     freeocta(nh);
     741           0 :                     return false;
     742             :                 }
     743           0 :                 vis[orient] |= 1<<i;
     744             :             }
     745             :     }
     746           0 :     if(mipvis)
     747             :     {
     748           0 :         for(int orient = 0; orient < 6; ++orient)
     749             :         {
     750           0 :             int mask = 0;
     751           0 :             for(int x = 0; x < 2; ++x)
     752             :             {
     753           0 :                 for(int y = 0; y < 2; ++y)
     754             :                 {
     755           0 :                     mask |= 1<<octacubeindex(DIMENSION(orient), x, y, DIM_COORD(orient));
     756             :                 }
     757             :             }
     758           0 :             if(vis[orient]&mask && (vis[orient]&mask)!=mask)
     759             :             {
     760           0 :                 freeocta(nh);
     761           0 :                 return false;
     762             :             }
     763             :         }
     764             :     }
     765           0 :     freeocta(nh);
     766           0 :     c.discardchildren();
     767           0 :     for(int i = 0; i < 3; ++i)
     768             :     {
     769           0 :         c.faces[i] = n.faces[i];
     770             :     }
     771           0 :     c.material = mat;
     772           0 :     for(int i = 0; i < 6; ++i)
     773             :     {
     774           0 :         if(vis[i])
     775             :         {
     776           0 :             c.visible |= 1<<i;
     777             :         }
     778             :     }
     779           0 :     if(c.visible)
     780             :     {
     781           0 :         c.visible |= 0x40;
     782             :     }
     783           0 :     brightencube(c);
     784           0 :     return true;
     785             : }
     786             : 
     787           0 : void cubeworld::remip()
     788             : {
     789           0 :     for(int i = 0; i < 8; ++i)
     790             :     {
     791           0 :         ivec o(i, ivec(0, 0, 0), mapsize()>>1);
     792           0 :         ::remip((*worldroot)[i], o, mapsize()>>2);
     793             :     }
     794           0 :     (*worldroot)[0].calcmerges(); //created as result of calcmerges being cube member
     795           0 : }
     796             : 
     797             : const ivec cubecoords[8] =
     798             : {
     799             :     ivec(8, 8, 0),
     800             :     ivec(0, 8, 0),
     801             :     ivec(0, 8, 8),
     802             :     ivec(8, 8, 8),
     803             :     ivec(8, 0, 8),
     804             :     ivec(0, 0, 8),
     805             :     ivec(0, 0, 0),
     806             :     ivec(8, 0, 0)
     807             : };
     808             : 
     809             : //================================================================= GENCUBEVERTS
     810             : #define GENCUBEVERTS(x0,x1, y0,y1, z0,z1) \
     811             :     GENCUBEVERT(0, x1, y1, z0) \
     812             :     GENCUBEVERT(1, x0, y1, z0) \
     813             :     GENCUBEVERT(2, x0, y1, z1) \
     814             :     GENCUBEVERT(3, x1, y1, z1) \
     815             :     GENCUBEVERT(4, x1, y0, z1) \
     816             :     GENCUBEVERT(5, x0, y0, z1) \
     817             :     GENCUBEVERT(6, x0, y0, z0) \
     818             :     GENCUBEVERT(7, x1, y0, z0)
     819             : 
     820           0 : static void gencubevert(const cube &c, int i, vec &v)
     821             : {
     822           0 :     switch(i)
     823             :     {
     824           0 :         default:
     825             : //================================================================== GENCUBEVERT
     826             : #define GENCUBEVERT(n, x, y, z) \
     827             :         case n: \
     828             :             v = vec(EDGE_GET(CUBE_EDGE(c, 0, y, z), x), \
     829             :                     EDGE_GET(CUBE_EDGE(c, 1, z, x), y), \
     830             :                     EDGE_GET(CUBE_EDGE(c, 2, x, y), z)); \
     831             :             break;
     832           0 :         GENCUBEVERTS(0, 1, 0, 1, 0, 1)
     833             : #undef GENCUBEVERT
     834             : //==============================================================================
     835             :     }
     836           0 : }
     837             : 
     838         138 : void genfaceverts(const cube &c, int orient, std::array<ivec, 4> &v)
     839             : {
     840         138 :     switch(orient)
     841             :     {
     842          23 :         default:
     843             : //==================================================== GENFACEORIENT GENFACEVERT
     844             : #define GENFACEORIENT(o, v0, v1, v2, v3) \
     845             :         case o: v0 v1 v2 v3 break;
     846             : #define GENFACEVERT(o, n, x,y,z, xv,yv,zv) \
     847             :             v[n] = ivec(EDGE_GET(CUBE_EDGE(c, 0, y, z), x), \
     848             :                         EDGE_GET(CUBE_EDGE(c, 1, z, x), y), \
     849             :                         EDGE_GET(CUBE_EDGE(c, 2, x, y), z));
     850         138 :         GENFACEVERTS(0, 1, 0, 1, 0, 1, , , , , , )
     851             :     #undef GENFACEORIENT
     852             :     #undef GENFACEVERT
     853             :     }
     854         138 : }
     855             : //==============================================================================
     856             : 
     857             : #undef GENCUBEVERTS
     858             : //==============================================================================
     859             : 
     860          48 : bool flataxisface(const cube &c, int orient)
     861             : {
     862          48 :     uint face = c.faces[DIMENSION(orient)];
     863          48 :     if(DIM_COORD(orient))
     864             :     {
     865          24 :         face >>= 4;
     866             :     }
     867          48 :     return (face&0x0F0F0F0F) == 0x01010101*(face&0x0F);
     868             : }
     869             : 
     870           0 : bool collideface(const cube &c, int orient)
     871             : {
     872           0 :     if(flataxisface(c, orient))
     873             :     {
     874           0 :         uchar r1 = c.edges[faceedgesidx[orient][0]],
     875           0 :               r2 = c.edges[faceedgesidx[orient][1]];
     876           0 :         if(static_cast<uchar>((r1>>4)|(r2&0xF0)) == static_cast<uchar>((r1&0x0F)|(r2<<4)))
     877             :         {
     878           0 :             return false;
     879             :         }
     880           0 :         uchar c1 = c.edges[faceedgesidx[orient][2]],
     881           0 :               c2 = c.edges[faceedgesidx[orient][3]];
     882           0 :         if(static_cast<uchar>((c1>>4)|(c2&0xF0)) == static_cast<uchar>((c1&0x0F)|(c2<<4)))
     883             :         {
     884           0 :             return false;
     885             :         }
     886             :     }
     887           0 :     return true;
     888             : }
     889             : 
     890           0 : int faceconvexity(const std::array<ivec, 4> &v)
     891             : {
     892           0 :     ivec n;
     893           0 :     n.cross(ivec(v[1]).sub(v[0]), ivec(v[2]).sub(v[0]));
     894           0 :     return ivec(v[0]).sub(v[3]).dot(n);
     895             :     // 1 if convex, -1 if concave, 0 if flat
     896             : }
     897             : 
     898           0 : int faceconvexity(const vertinfo *verts, int numverts, int size)
     899             : {
     900           0 :     if(numverts < 4)
     901             :     {
     902           0 :         return 0;
     903             :     }
     904           0 :     ivec v0 = verts[0].getxyz(),
     905           0 :          e1 = verts[1].getxyz().sub(v0),
     906           0 :          e2 = verts[2].getxyz().sub(v0),
     907           0 :          n;
     908           0 :     if(size >= (8<<5))
     909             :     {
     910           0 :         if(size >= (8<<10))
     911             :         {
     912           0 :             n.cross(e1.shr(10), e2.shr(10));
     913             :         }
     914             :         else
     915             :         {
     916           0 :             n.cross(e1, e2).shr(10);
     917             :         }
     918             :     }
     919             :     else
     920             :     {
     921           0 :         n.cross(e1, e2);
     922             :     }
     923           0 :     return verts[3].getxyz().sub(v0).dot(n);
     924             : }
     925             : 
     926           0 : int faceconvexity(const std::array<ivec, 4> &v, int &vis)
     927             : {
     928           0 :     ivec e1, e2, e3, n;
     929           0 :     n.cross((e1 = v[1]).sub(v[0]), (e2 = v[2]).sub(v[0]));
     930           0 :     int convex = (e3 = v[0]).sub(v[3]).dot(n);
     931           0 :     if(!convex)
     932             :     {
     933           0 :         if(!ivec().cross(e3, e2))
     934             :         {
     935           0 :             if(n)
     936             :             {
     937           0 :                 vis = 1;
     938             :             }
     939             :         }
     940           0 :         else if(!n)
     941             :         {
     942           0 :             vis = 2;
     943             :         }
     944           0 :         return 0;
     945             :     }
     946           0 :     return convex;
     947             : }
     948             : 
     949           0 : int faceconvexity(const cube &c, int orient)
     950             : {
     951           0 :     if(flataxisface(c, orient))
     952             :     {
     953           0 :         return 0;
     954             :     }
     955           0 :     std::array<ivec, 4> v;
     956           0 :     genfaceverts(c, orient, v);
     957           0 :     return faceconvexity(v);
     958             : }
     959             : 
     960           0 : int faceorder(const cube &c, int orient) // gets above 'fv' so that each face is convex
     961             : {
     962           0 :     return faceconvexity(c, orient)<0 ? 1 : 0;
     963             : }
     964             : 
     965          48 : static void faceedges(const cube &c, int orient, std::array<uchar, 4> &edges)
     966             : {
     967         240 :     for(size_t k = 0; k < edges.size(); ++k)
     968             :     {
     969         192 :         edges[k] = c.edges[faceedgesidx[orient][k]];
     970             :     }
     971          48 : }
     972             : 
     973          48 : uint faceedges(const cube &c, int orient)
     974             : {
     975             :     union
     976             :     {
     977             :         std::array<uchar, 4> edges;
     978             :         uint face;
     979             :     } u;
     980          48 :     faceedges(c, orient, u.edges);
     981          48 :     return u.face;
     982             : }
     983             : 
     984             : 
     985           0 : static int genfacevecs(const cube &cu, int orient, const ivec &pos, int size, bool solid, std::array<ivec2, 4> &fvecs, const ivec *v = nullptr)
     986             : {
     987           0 :     int i = 0;
     988           0 :     if(solid)
     989             :     {
     990           0 :         switch(orient)
     991             :         {
     992             :         #define GENFACEORIENT(orient, v0, v1, v2, v3) \
     993             :             case orient: \
     994             :             { \
     995             :                 if(DIM_COORD(orient)) \
     996             :                 { \
     997             :                     v0 v1 v2 v3 \
     998             :                 } \
     999             :                 else \
    1000             :                 { \
    1001             :                     v3 v2 v1 v0 \
    1002             :                 } \
    1003             :                 break; \
    1004             :             }
    1005             :         #define GENFACEVERT(orient, vert, xv,yv,zv, x,y,z) \
    1006             :             { \
    1007             :                 ivec2 &f = fvecs[i]; \
    1008             :                 x ((xv)<<3); \
    1009             :                 y ((yv)<<3); \
    1010             :                 z ((zv)<<3); \
    1011             :                 i++; \
    1012             :             }
    1013           0 :             GENFACEVERTS(pos.x, pos.x+size, pos.y, pos.y+size, pos.z, pos.z+size, f.x = , f.x = , f.y = , f.y = , (void), (void))
    1014             :         #undef GENFACEVERT
    1015             :         }
    1016           0 :         return 4;
    1017             :     }
    1018           0 :     std::array<ivec, 4> buf;
    1019           0 :     if(!v)
    1020             :     {
    1021           0 :         genfaceverts(cu, orient, buf);
    1022           0 :         v = buf.data();
    1023             :     }
    1024           0 :     ivec2 prev(INT_MAX, INT_MAX);
    1025           0 :     switch(orient)
    1026             :     {
    1027             :     #define GENFACEVERT(orient, vert, sx,sy,sz, dx,dy,dz) \
    1028             :         { \
    1029             :             const ivec &e = v[vert]; \
    1030             :             ivec ef; \
    1031             :             ef.dx = e.sx; \
    1032             :             ef.dy = e.sy; \
    1033             :             ef.dz = e.sz; \
    1034             :             if(ef.z == DIM_COORD(orient)*8) \
    1035             :             { \
    1036             :                 ivec2 &f = fvecs[i]; \
    1037             :                 ivec pf; \
    1038             :                 pf.dx = pos.sx; \
    1039             :                 pf.dy = pos.sy; \
    1040             :                 pf.dz = pos.sz; \
    1041             :                 f = ivec2(ef.x*size + (pf.x<<3), ef.y*size + (pf.y<<3)); \
    1042             :                 if(f != prev) \
    1043             :                 { \
    1044             :                     prev = f; \
    1045             :                     i++; \
    1046             :                 } \
    1047             :             } \
    1048             :         }
    1049           0 :         GENFACEVERTS(x, x, y, y, z, z, x, x, y, y, z, z)
    1050             :     #undef GENFACEORIENT
    1051             :     #undef GENFACEVERT
    1052             :     }
    1053           0 :     if(fvecs[0] == prev)
    1054             :     {
    1055           0 :         i--;
    1056             :     }
    1057           0 :     return i;
    1058             : }
    1059             : 
    1060           0 : static int clipfacevecy(const ivec2 &o, const ivec2 &dir, int cx, int cy, int size, ivec2 &r)
    1061             : {
    1062           0 :     if(dir.x >= 0)
    1063             :     {
    1064           0 :         if(cx <= o.x || cx >= o.x+dir.x)
    1065             :         {
    1066           0 :             return 0;
    1067             :         }
    1068             :     }
    1069           0 :     else if(cx <= o.x+dir.x || cx >= o.x)
    1070             :     {
    1071           0 :         return 0;
    1072             :     }
    1073           0 :     int t = (o.y-cy) + (cx-o.x)*dir.y/dir.x;
    1074           0 :     if(t <= 0 || t >= size)
    1075             :     {
    1076           0 :         return 0;
    1077             :     }
    1078           0 :     r.x = cx;
    1079           0 :     r.y = cy + t;
    1080           0 :     return 1;
    1081             : }
    1082             : 
    1083           0 : static int clipfacevecx(const ivec2 &o, const ivec2 &dir, int cx, int cy, int size, ivec2 &r)
    1084             : {
    1085           0 :     if(dir.y >= 0)
    1086             :     {
    1087           0 :         if(cy <= o.y || cy >= o.y+dir.y)
    1088             :         {
    1089           0 :             return 0;
    1090             :         }
    1091             :     }
    1092           0 :     else if(cy <= o.y+dir.y || cy >= o.y)
    1093             :     {
    1094           0 :         return 0;
    1095             :     }
    1096           0 :     int t = (o.x-cx) + (cy-o.y)*dir.x/dir.y;
    1097           0 :     if(t <= 0 || t >= size)
    1098             :     {
    1099           0 :         return 0;
    1100             :     }
    1101           0 :     r.x = cx + t;
    1102           0 :     r.y = cy;
    1103           0 :     return 1;
    1104             : }
    1105             : 
    1106             : //param rvecs: an array of ivec2s
    1107           0 : static int clipfacevec(const ivec2 &o, const ivec2 &dir, int cx, int cy, int size, ivec2 *rvecs)
    1108             : {
    1109           0 :     int r = 0;
    1110           0 :     if(o.x >= cx && o.x <= cx+size &&
    1111           0 :        o.y >= cy && o.y <= cy+size &&
    1112           0 :        ((o.x != cx && o.x != cx+size) || (o.y != cy && o.y != cy+size)))
    1113             :     {
    1114           0 :         rvecs[0] = o;
    1115           0 :         r++;
    1116             :     }
    1117           0 :     r += clipfacevecx(o, dir, cx, cy, size, rvecs[r]);
    1118           0 :     r += clipfacevecx(o, dir, cx, cy+size, size, rvecs[r]);
    1119           0 :     r += clipfacevecy(o, dir, cx, cy, size, rvecs[r]);
    1120           0 :     r += clipfacevecy(o, dir, cx+size, cy, size, rvecs[r]);
    1121           0 :     return r;
    1122             : }
    1123             : 
    1124           0 : static bool insideface(const ivec2 *p, int nump, const ivec2 *o, int numo)
    1125             : {
    1126           0 :     int bounds = 0;
    1127           0 :     ivec2 prev = o[numo-1];
    1128           0 :     for(int i = 0; i < numo; ++i)
    1129             :     {
    1130           0 :         const ivec2 &cur = o[i];
    1131           0 :         ivec2 dir = ivec2(cur).sub(prev);
    1132           0 :         int offset = dir.cross(prev);
    1133           0 :         for(int j = 0; j < nump; ++j)
    1134             :         {
    1135           0 :             if(dir.cross(p[j]) > offset)
    1136             :             {
    1137           0 :                 return false;
    1138             :             }
    1139             :         }
    1140           0 :         bounds++;
    1141           0 :         prev = cur;
    1142             :     }
    1143           0 :     return bounds>=3;
    1144             : }
    1145             : 
    1146           0 : static int clipfacevecs(const ivec2 *o, int numo, int cx, int cy, int size, ivec2 *rvecs)
    1147             : {
    1148           0 :     cx <<= 3;
    1149           0 :     cy <<= 3;
    1150           0 :     size <<= 3;
    1151           0 :     int r = 0;
    1152           0 :     if(numo <= 0)
    1153             :     {
    1154           0 :         logoutf("Invalid clipface index %d\n", numo);
    1155           0 :         return 0; //protection agains numo negative array access
    1156             :     }
    1157           0 :     ivec2 prev = o[numo-1];
    1158           0 :     for(int i = 0; i < numo; ++i)
    1159             :     {
    1160           0 :         const ivec2 &cur = o[i];
    1161           0 :         r += clipfacevec(prev, ivec2(cur).sub(prev), cx, cy, size, &rvecs[r]);
    1162           0 :         prev = cur;
    1163             :     }
    1164           0 :     ivec2 corner[4] = {ivec2(cx, cy), ivec2(cx+size, cy), ivec2(cx+size, cy+size), ivec2(cx, cy+size)};
    1165           0 :     for(int i = 0; i < 4; ++i)
    1166             :     {
    1167           0 :         if(insideface(&corner[i], 1, o, numo))
    1168             :         {
    1169           0 :             rvecs[r++] = corner[i];
    1170             :         }
    1171             :     }
    1172           0 :     return r;
    1173             : }
    1174             : 
    1175           0 : static bool collapsedface(const cube &c, int orient)
    1176             : {
    1177           0 :     int e0 = c.edges[faceedgesidx[orient][0]],
    1178           0 :         e1 = c.edges[faceedgesidx[orient][1]],
    1179           0 :         e2 = c.edges[faceedgesidx[orient][2]],
    1180           0 :         e3 = c.edges[faceedgesidx[orient][3]],
    1181           0 :         face = DIMENSION(orient)*4,
    1182           0 :         f0 = c.edges[face+0],
    1183           0 :         f1 = c.edges[face+1],
    1184           0 :         f2 = c.edges[face+2],
    1185           0 :         f3 = c.edges[face+3];
    1186           0 :     if(DIM_COORD(orient))
    1187             :     {
    1188           0 :         f0 >>= 4;
    1189           0 :         f1 >>= 4;
    1190           0 :         f2 >>= 4;
    1191           0 :         f3 >>= 4;
    1192             :     }
    1193             :     else
    1194             :     {
    1195           0 :         f0 &= 0xF;
    1196           0 :         f1 &= 0xF;
    1197           0 :         f2 &= 0xF;
    1198           0 :         f3 &= 0xF;
    1199             :     }
    1200           0 :     ivec v0(e0&0xF, e2&0xF, f0),
    1201           0 :          v1(e0>>4, e3&0xF, f1),
    1202           0 :          v2(e1>>4, e3>>4, f3),
    1203           0 :          v3(e1&0xF, e2>>4, f2);
    1204           0 :     return !ivec().cross(v1.sub(v0), v2.sub(v0)) &&
    1205           0 :            !ivec().cross(v2, v3.sub(v0));
    1206             : }
    1207             : 
    1208           0 : static bool occludesface(const cube &c, int orient, const ivec &o, int size, const ivec &vo, int vsize, ushort vmat, ushort nmat, ushort matmask, const ivec2 *vf, int numv)
    1209             : {
    1210           0 :     int dim = DIMENSION(orient);
    1211           0 :     if(!c.children)
    1212             :     {
    1213           0 :         if(c.material)
    1214             :         {
    1215           0 :             if(nmat != Mat_Air && (c.material&matmask) == nmat)
    1216             :             {
    1217           0 :                 ivec2 nf[8];
    1218           0 :                 return clipfacevecs(vf, numv, o[C[dim]], o[R[dim]], size, nf) < 3;
    1219             :             }
    1220           0 :             if(vmat != Mat_Air && ((c.material&matmask) == vmat || (IS_LIQUID(vmat) && IS_CLIPPED(c.material&MatFlag_Volume))))
    1221             :             {
    1222           0 :                 return true;
    1223             :             }
    1224             :         }
    1225           0 :         if(c.issolid())
    1226             :         {
    1227           0 :             return true;
    1228             :         }
    1229           0 :         if(touchingface(c, orient) && faceedges(c, orient) == facesolid)
    1230             :         {
    1231           0 :             return true;
    1232             :         }
    1233           0 :         ivec2 cf[8];
    1234           0 :         int numc = clipfacevecs(vf, numv, o[C[dim]], o[R[dim]], size, cf);
    1235           0 :         if(numc < 3)
    1236             :         {
    1237           0 :             return true;
    1238             :         }
    1239           0 :         if(c.isempty() || notouchingface(c, orient))
    1240             :         {
    1241           0 :             return false;
    1242             :         }
    1243           0 :         std::array<ivec2, 4> of;
    1244           0 :         int numo = genfacevecs(c, orient, o, size, false, of);
    1245           0 :         return numo >= 3 && insideface(&cf[0], numc, &of[0], numo);
    1246             :     }
    1247           0 :     size >>= 1;
    1248           0 :     int coord = DIM_COORD(orient);
    1249           0 :     for(int i = 0; i < 8; ++i)
    1250             :     {
    1251           0 :         if(OCTA_COORD(dim, i) == coord)
    1252             :         {
    1253           0 :             if(!occludesface((*c.children)[i], orient, ivec(i, o, size), size, vo, vsize, vmat, nmat, matmask, vf, numv))
    1254             :             {
    1255           0 :                 return false;
    1256             :             }
    1257             :         }
    1258             :     }
    1259           0 :     return true;
    1260             : }
    1261             : 
    1262           0 : bool visibleface(const cube &c, int orient, const ivec &co, int size, ushort mat, ushort nmat, ushort matmask)
    1263             : {
    1264           0 :     if(mat != Mat_Air)
    1265             :     {
    1266           0 :         if(mat != Mat_Clip && faceedges(c, orient) == facesolid && touchingface(c, orient))
    1267             :         {
    1268           0 :             return false;
    1269             :         }
    1270             :     }
    1271             :     else
    1272             :     {
    1273           0 :         if(collapsedface(c, orient))
    1274             :         {
    1275           0 :             return false;
    1276             :         }
    1277           0 :         if(!touchingface(c, orient))
    1278             :         {
    1279           0 :             return true;
    1280             :         }
    1281             :     }
    1282           0 :     ivec no;
    1283             :     int nsize;
    1284           0 :     const cube &o = ::rootworld.neighborcube(orient, co, size, no, nsize);
    1285           0 :     int opp = oppositeorient(orient);
    1286           0 :     if(nsize > size || (nsize == size && !o.children))
    1287             :     {
    1288           0 :         if(o.material)
    1289             :         {
    1290           0 :             if(nmat != Mat_Air && (o.material&matmask) == nmat)
    1291             :             {
    1292           0 :                 return true;
    1293             :             }
    1294           0 :             if(mat != Mat_Air && ((o.material&matmask) == mat || (IS_LIQUID(mat) && IS_CLIPPED(o.material&MatFlag_Volume))))
    1295             :             {
    1296           0 :                 return false;
    1297             :             }
    1298             :         }
    1299           0 :         if(o.issolid())
    1300             :         {
    1301           0 :             return false;
    1302             :         }
    1303           0 :         if(o.isempty() || notouchingface(o, opp))
    1304             :         {
    1305           0 :             return true;
    1306             :         }
    1307           0 :         if(touchingface(o, opp) && faceedges(o, opp) == facesolid)
    1308             :         {
    1309           0 :             return false;
    1310             :         }
    1311           0 :         ivec vo = ivec(co).mask(0xFFF);
    1312           0 :         no.mask(0xFFF);
    1313           0 :         std::array<ivec2, 4> cf,
    1314           0 :                              of;
    1315           0 :         int numc = genfacevecs(c, orient, vo, size, mat != Mat_Air, cf),
    1316           0 :             numo = genfacevecs(o, opp, no, nsize, false, of);
    1317           0 :         return numo < 3 || !insideface(&cf[0], numc, &of[0], numo);
    1318             :     }
    1319           0 :     ivec vo = ivec(co).mask(0xFFF);
    1320           0 :     no.mask(0xFFF);
    1321           0 :     std::array<ivec2, 4> cf;
    1322           0 :     int numc = genfacevecs(c, orient, vo, size, mat != Mat_Air, cf);
    1323           0 :     return !occludesface(o, opp, no, nsize, vo, size, mat, nmat, matmask, cf.data(), numc);
    1324             : }
    1325             : 
    1326           0 : int classifyface(const cube &c, int orient, const ivec &co, int size)
    1327             : {
    1328           0 :     int vismask = 2,
    1329           0 :         forcevis = 0;
    1330           0 :     bool solid = false;
    1331           0 :     switch(c.material&MatFlag_Clip)
    1332             :     {
    1333           0 :         case Mat_NoClip:
    1334             :         {
    1335           0 :             vismask = 0;
    1336           0 :             break;
    1337             :         }
    1338           0 :         case Mat_Clip:
    1339             :         {
    1340           0 :             solid = true;
    1341           0 :             break;
    1342             :         }
    1343             :     }
    1344           0 :     if(c.isempty() || collapsedface(c, orient))
    1345             :     {
    1346           0 :         if(!vismask)
    1347             :         {
    1348           0 :             return 0;
    1349             :         }
    1350             :     }
    1351           0 :     else if(!touchingface(c, orient))
    1352             :     {
    1353           0 :         forcevis = 1;
    1354           0 :         if(!solid)
    1355             :         {
    1356           0 :             if(vismask && collideface(c, orient))
    1357             :             {
    1358           0 :                 forcevis |= 2;
    1359             :             }
    1360           0 :             return forcevis;
    1361             :         }
    1362             :     }
    1363             :     else
    1364             :     {
    1365           0 :         vismask |= 1;
    1366             :     }
    1367           0 :     ivec no;
    1368             :     int nsize;
    1369           0 :     const cube &o = ::rootworld.neighborcube(orient, co, size, no, nsize);
    1370           0 :     if(&o==&c)
    1371             :     {
    1372           0 :         return 0;
    1373             :     }
    1374           0 :     int opp = oppositeorient(orient);
    1375           0 :     if(nsize > size || (nsize == size && !o.children))
    1376             :     {
    1377           0 :         if(o.material)
    1378             :         {
    1379           0 :             if((~c.material & o.material) & Mat_Alpha)
    1380             :             {
    1381           0 :                 forcevis |= vismask&1;
    1382           0 :                 vismask &= ~1;
    1383             :             }
    1384           0 :             switch(o.material&MatFlag_Clip)
    1385             :             {
    1386           0 :                 case Mat_Clip:
    1387             :                 {
    1388           0 :                     vismask &= ~2;
    1389           0 :                     break;
    1390             :                 }
    1391           0 :                 case Mat_NoClip:
    1392             :                 {
    1393           0 :                     forcevis |= vismask&2;
    1394           0 :                     vismask &= ~2;
    1395           0 :                     break;
    1396             :                 }
    1397             :             }
    1398             :         }
    1399           0 :         if(vismask && !(o.issolid()))
    1400             :         {
    1401           0 :             if(o.isempty() || notouchingface(o, opp))
    1402             :             {
    1403           0 :                 forcevis |= vismask;
    1404             :             }
    1405           0 :             else if(!touchingface(o, opp) || faceedges(o, opp) != facesolid)
    1406             :             {
    1407           0 :                 ivec vo = ivec(co).mask(0xFFF);
    1408           0 :                 no.mask(0xFFF);
    1409           0 :                 std::array<ivec2, 4> cf,
    1410           0 :                                      of;
    1411           0 :                 int numo = genfacevecs(o, opp, no, nsize, false, of);
    1412           0 :                 if(numo < 3)
    1413             :                 {
    1414           0 :                     forcevis |= vismask;
    1415             :                 }
    1416             :                 else
    1417             :                 {
    1418           0 :                     int numc = 0;
    1419           0 :                     if(vismask&2 && solid)
    1420             :                     {
    1421           0 :                         numc = genfacevecs(c, orient, vo, size, true, cf);
    1422           0 :                         if(!insideface(&cf[0], numc, &of[0], numo))
    1423             :                         {
    1424           0 :                             forcevis |= 2;
    1425             :                         }
    1426           0 :                         vismask &= ~2;
    1427             :                     }
    1428           0 :                     if(vismask)
    1429             :                     {
    1430           0 :                         numc = genfacevecs(c, orient, vo, size, false, cf);
    1431           0 :                         if(!insideface(&cf[0], numc, &of[0], numo))
    1432             :                         {
    1433           0 :                             forcevis |= vismask;
    1434             :                         }
    1435             :                     }
    1436             :                 }
    1437             :             }
    1438             :         }
    1439           0 :     }
    1440             :     else
    1441             :     {
    1442           0 :         ivec vo = ivec(co).mask(0xFFF);
    1443           0 :         no.mask(0xFFF);
    1444           0 :         std::array<ivec2, 4> cf;
    1445           0 :         int numc = 0;
    1446           0 :         if(vismask&1)
    1447             :         {
    1448           0 :             numc = genfacevecs(c, orient, vo, size, false, cf);
    1449           0 :             if(!occludesface(o, opp, no, nsize, vo, size, Mat_Air, (c.material&Mat_Alpha)^Mat_Alpha, Mat_Alpha, &cf[0], numc))
    1450             :             {
    1451           0 :                 forcevis |= 1;
    1452             :             }
    1453             :         }
    1454           0 :         if(vismask&2)
    1455             :         {
    1456           0 :             if(!numc || solid)
    1457             :             {
    1458           0 :                 numc = genfacevecs(c, orient, vo, size, solid, cf);
    1459             :             }
    1460           0 :             if(!occludesface(o, opp, no, nsize, vo, size, Mat_Clip, Mat_NoClip, MatFlag_Clip, &cf[0], numc))
    1461             :             {
    1462           0 :                 forcevis |= 2;
    1463             :             }
    1464             :         }
    1465             :     }
    1466           0 :     if(forcevis&2 && !solid && !collideface(c, orient))
    1467             :     {
    1468           0 :         forcevis &= ~2;
    1469             :     }
    1470           0 :     return forcevis;
    1471             : }
    1472             : 
    1473             : // more expensive version that checks both triangles of a face independently
    1474          90 : int visibletris(const cube &c, int orient, const ivec &co, int size, ushort vmat, ushort nmat, ushort matmask)
    1475             : {
    1476          90 :     int vis = 3,
    1477          90 :         touching = 0xF;
    1478          90 :     std::array<ivec, 4> v;
    1479          90 :     ivec e1,
    1480          90 :          e2,
    1481          90 :          e3,
    1482          90 :          n;
    1483          90 :     genfaceverts(c, orient, v);
    1484          90 :     n.cross((e1 = v[1]).sub(v[0]), (e2 = v[2]).sub(v[0]));
    1485          90 :     int convex = (e3 = v[0]).sub(v[3]).dot(n);
    1486          90 :     if(!convex)
    1487             :     {
    1488          90 :         if(!ivec().cross(e3, e2) || v[1] == v[3])
    1489             :         {
    1490           0 :             if(!n)
    1491             :             {
    1492           0 :                 return 0;
    1493             :             }
    1494           0 :             vis = 1;
    1495           0 :             touching = 0xF&~(1<<3);
    1496             :         }
    1497          90 :         else if(!n)
    1498             :         {
    1499           0 :             vis = 2;
    1500           0 :             touching = 0xF&~(1<<1);
    1501             :         }
    1502             :     }
    1503          90 :     int dim = DIMENSION(orient), coord = DIM_COORD(orient);
    1504          90 :     if(v[0][dim] != coord*8)
    1505             :     {
    1506           0 :         touching &= ~(1<<0);
    1507             :     }
    1508          90 :     if(v[1][dim] != coord*8)
    1509             :     {
    1510           0 :         touching &= ~(1<<1);
    1511             :     }
    1512          90 :     if(v[2][dim] != coord*8)
    1513             :     {
    1514           0 :         touching &= ~(1<<2);
    1515             :     }
    1516          90 :     if(v[3][dim] != coord*8)
    1517             :     {
    1518           0 :         touching &= ~(1<<3);
    1519             :     }
    1520             :     static const int notouchmasks[2][16] = // mask of triangles not touching
    1521             :     { // order 0: flat or convex
    1522             :        // 0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15
    1523             :         { 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 1, 3, 0 },
    1524             :       // order 1: concave
    1525             :         { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 2, 0 },
    1526             :     };
    1527          90 :     int order = convex < 0 ? 1 : 0,
    1528          90 :         notouch = notouchmasks[order][touching];
    1529          90 :     if((vis&notouch)==vis)
    1530             :     {
    1531           0 :         return vis;
    1532             :     }
    1533          90 :     ivec no;
    1534             :     int nsize;
    1535          90 :     const cube &o = ::rootworld.neighborcube(orient, co, size, no, nsize);
    1536          90 :     if((c.material&matmask) == nmat)
    1537             :     {
    1538           0 :         nmat = Mat_Air;
    1539             :     }
    1540          90 :     ivec vo = ivec(co).mask(0xFFF);
    1541          90 :     no.mask(0xFFF);
    1542          90 :     std::array<ivec2, 4> cf,
    1543          90 :                          of;
    1544          90 :     int opp = oppositeorient(orient),
    1545          90 :         numo = 0,
    1546             :         numc;
    1547          90 :     if(nsize > size || (nsize == size && !o.children))
    1548             :     {
    1549          90 :         if(o.material)
    1550             :         {
    1551           0 :             if(vmat != Mat_Air && (o.material&matmask) == vmat)
    1552             :             {
    1553           0 :                 return vis&notouch;
    1554             :             }
    1555           0 :             if(nmat != Mat_Air && (o.material&matmask) == nmat)
    1556             :             {
    1557           0 :                 return vis;
    1558             :             }
    1559             :         }
    1560          90 :         if(o.isempty() || notouchingface(o, opp))
    1561             :         {
    1562          90 :             return vis;
    1563             :         }
    1564           0 :         if(o.issolid() || (touchingface(o, opp) && faceedges(o, opp) == facesolid))
    1565             :         {
    1566           0 :             return vis&notouch;
    1567             :         }
    1568           0 :         numc = genfacevecs(c, orient, vo, size, false, cf, v.data());
    1569           0 :         numo = genfacevecs(o, opp, no, nsize, false, of);
    1570           0 :         if(numo < 3)
    1571             :         {
    1572           0 :             return vis;
    1573             :         }
    1574           0 :         if(insideface(&cf[0], numc, &of[0], numo))
    1575             :         {
    1576           0 :             return vis&notouch;
    1577             :         }
    1578             :     }
    1579             :     else
    1580             :     {
    1581           0 :         numc = genfacevecs(c, orient, vo, size, false, cf, v.data());
    1582           0 :         if(occludesface(o, opp, no, nsize, vo, size, vmat, nmat, matmask, &cf[0], numc))
    1583             :         {
    1584           0 :             return vis&notouch;
    1585             :         }
    1586             :     }
    1587           0 :     if(vis != 3 || notouch)
    1588             :     {
    1589           0 :         return vis;
    1590             :     }
    1591             :     static const int triverts[2][2][2][3] =
    1592             :     { // order
    1593             :         { // coord
    1594             :             { { 1, 2, 3 }, { 0, 1, 3 } }, // verts
    1595             :             { { 0, 1, 2 }, { 0, 2, 3 } }
    1596             :         },
    1597             :         { // coord
    1598             :             { { 0, 1, 2 }, { 3, 0, 2 } }, // verts
    1599             :             { { 1, 2, 3 }, { 1, 3, 0 } }
    1600             :         }
    1601             :     };
    1602             :     do
    1603             :     {
    1604           0 :         for(int i = 0; i < 2; ++i)
    1605             :         {
    1606           0 :             const int *verts = triverts[order][coord][i];
    1607           0 :             ivec2 tf[3] = { cf[verts[0]], cf[verts[1]], cf[verts[2]] };
    1608           0 :             if(numo > 0)
    1609             :             {
    1610           0 :                 if(!insideface(tf, 3, &of[0], numo))
    1611             :                 {
    1612           0 :                     continue;
    1613             :                 }
    1614             :             }
    1615           0 :             else if(!occludesface(o, opp, no, nsize, vo, size, vmat, nmat, matmask, tf, 3))
    1616             :             {
    1617           0 :                 continue;
    1618             :             }
    1619           0 :             return vis & ~(1<<i);
    1620             :         }
    1621           0 :         vis |= 4;
    1622           0 :     } while(++order <= 1);
    1623           0 :     return 3;
    1624             : }
    1625             : 
    1626           0 : static void calcvert(const cube &c, const ivec &co, int size, vec &v, int i, bool solid = false)
    1627             : {
    1628           0 :     if(solid)
    1629             :     {
    1630           0 :         v = vec(cubecoords[i]);
    1631             :     }
    1632             :     else
    1633             :     {
    1634           0 :         gencubevert(c, i, v);
    1635             :     }
    1636           0 :     v.mul(size/8.0f).add(vec(co));
    1637           0 : }
    1638             : 
    1639             : //sets clipplanes values for a cube c and location co
    1640           0 : void genclipbounds(const cube &c, const ivec &co, int size, clipplanes &p)
    1641             : {
    1642             :     // generate tight bounding box
    1643           0 :     calcvert(c, co, size, p.v[0], 0);
    1644           0 :     vec mx = p.v[0],
    1645           0 :         mn = p.v[0];
    1646           0 :     for(int i = 1; i < 8; i++)
    1647             :     {
    1648           0 :         calcvert(c, co, size, p.v[i], i);
    1649           0 :         mx.max(p.v[i]);
    1650           0 :         mn.min(p.v[i]);
    1651             :     }
    1652           0 :     p.r = mx.sub(mn).mul(0.5f);
    1653           0 :     p.o = mn.add(p.r);
    1654           0 :     p.size = 0;
    1655           0 :     p.visible = 0x80;
    1656           0 : }
    1657             : 
    1658           0 : void genclipplanes(const cube &c, const ivec &co, int size, clipplanes &p, bool collide, bool noclip)
    1659             : {
    1660             :     static const uchar fv[6][4] = // indexes for cubecoords, per each vert of a face orientation
    1661             :     {
    1662             :         { 2, 1, 6, 5 },
    1663             :         { 3, 4, 7, 0 },
    1664             :         { 4, 5, 6, 7 },
    1665             :         { 1, 2, 3, 0 },
    1666             :         { 6, 1, 0, 7 },
    1667             :         { 5, 4, 3, 2 },
    1668             :     };
    1669             : 
    1670           0 :     p.visible &= ~0x80;
    1671           0 :     if(collide || (c.visible&0xC0) == 0x40)
    1672             :     {
    1673           0 :         for(int i = 0; i < 6; ++i)
    1674             :         {
    1675           0 :             if(c.visible&(1<<i))
    1676             :             {
    1677             :                 int vis;
    1678           0 :                 if(flataxisface(c, i))
    1679             :                 {
    1680           0 :                     p.visible |= 1<<i;
    1681             :                 }
    1682           0 :                 else if((vis = visibletris(c, i, co, size, Mat_Clip, Mat_NoClip, MatFlag_Clip)))
    1683             :                 {
    1684           0 :                     int convex = faceconvexity(c, i),
    1685           0 :                         order = vis&4 || convex < 0 ? 1 : 0;
    1686           0 :                     const vec &v0 = p.v[fv[i][order]],
    1687           0 :                               &v1 = p.v[fv[i][order+1]],
    1688           0 :                               &v2 = p.v[fv[i][order+2]],
    1689           0 :                               &v3 = p.v[fv[i][(order+3)&3]];
    1690           0 :                     if(vis&1)
    1691             :                     {
    1692           0 :                         p.side[p.size] = i;
    1693           0 :                         p.p[p.size++].toplane(v0, v1, v2);
    1694             :                     }
    1695           0 :                     if(vis&2 && (!(vis&1) || convex))
    1696             :                     {
    1697           0 :                         p.side[p.size] = i;
    1698           0 :                         p.p[p.size++].toplane(v0, v2, v3);
    1699             :                     }
    1700             :                 }
    1701             :             }
    1702             :         }
    1703           0 :     }
    1704           0 :     else if(c.visible&0x80)
    1705             :     {
    1706           0 :         const ushort nmat = noclip ? Mat_NoClip : Mat_Alpha,
    1707           0 :                      matmask = noclip ? static_cast<ushort>(MatFlag_Clip) : Mat_Alpha; //cast to avoid enum mismatch warning
    1708             :         int vis;
    1709           0 :         for(int i = 0; i < 6; ++i)
    1710             :         {
    1711           0 :             if((vis = visibletris(c, i, co, size, Mat_Air, nmat, matmask)))
    1712             :             {
    1713           0 :                 if(flataxisface(c, i))
    1714             :                 {
    1715           0 :                     p.visible |= 1<<i;
    1716             :                 }
    1717             :                 else
    1718             :                 {
    1719           0 :                     int convex = faceconvexity(c, i),
    1720           0 :                         order = vis&4 || convex < 0 ? 1 : 0;
    1721           0 :                     const vec &v0 = p.v[fv[i][order]],
    1722           0 :                               &v1 = p.v[fv[i][order+1]],
    1723           0 :                               &v2 = p.v[fv[i][order+2]],
    1724           0 :                               &v3 = p.v[fv[i][(order+3)&3]];
    1725           0 :                     if(vis&1)
    1726             :                     {
    1727           0 :                         p.side[p.size] = i;
    1728           0 :                         p.p[p.size++].toplane(v0, v1, v2);
    1729             :                     }
    1730           0 :                     if(vis&2 && (!(vis&1) || convex))
    1731             :                     {
    1732           0 :                         p.side[p.size] = i;
    1733           0 :                         p.p[p.size++].toplane(v0, v2, v3);
    1734             :                     }
    1735             :                 }
    1736             :             }
    1737             :         }
    1738             :     }
    1739           0 : }
    1740             : 
    1741           0 : void cube::mincubeface(const cube &cu, int orient, const ivec &o, int size, const facebounds &orig, facebounds &cf, ushort nmat, ushort matmask) const
    1742             : {
    1743           0 :     int dim = DIMENSION(orient);
    1744           0 :     if(cu.children)
    1745             :     {
    1746           0 :         size >>= 1;
    1747           0 :         int coord = DIM_COORD(orient);
    1748           0 :         for(int i = 0; i < 8; ++i)
    1749             :         {
    1750           0 :             if(OCTA_COORD(dim, i) == coord)
    1751             :             {
    1752           0 :                 mincubeface((*cu.children)[i], orient, ivec(i, o, size), size, orig, cf, nmat, matmask);
    1753             :             }
    1754             :         }
    1755           0 :         return;
    1756             :     }
    1757           0 :     int c = C[dim],
    1758           0 :         r = R[dim];
    1759           0 :     ushort uco = (o[c]&0xFFF)<<3,
    1760           0 :            vco = (o[r]&0xFFF)<<3;
    1761           0 :     ushort uc1 = uco,
    1762           0 :            vc1 = vco,
    1763           0 :            uc2 = static_cast<ushort>(size<<3)+uco,
    1764           0 :            vc2 = static_cast<ushort>(size<<3)+vco;
    1765           0 :     uc1 = std::max(uc1, orig.u1);
    1766           0 :     uc2 = std::min(uc2, orig.u2);
    1767           0 :     vc1 = std::max(vc1, orig.v1);
    1768           0 :     vc2 = std::min(vc2, orig.v2);
    1769           0 :     if(!(cu.isempty()) && touchingface(cu, orient) && !(nmat!=Mat_Air && (cu.material&matmask)==nmat))
    1770             :     {
    1771           0 :         uchar r1 = cu.edges[faceedgesidx[orient][0]],
    1772           0 :               r2 = cu.edges[faceedgesidx[orient][1]],
    1773           0 :               c1 = cu.edges[faceedgesidx[orient][2]],
    1774           0 :               c2 = cu.edges[faceedgesidx[orient][3]];
    1775           0 :         ushort u1 = std::max(c1&0xF, c2&0xF)*size+uco,
    1776           0 :                u2 = std::min(c1>>4, c2>>4)*size+uco,
    1777           0 :                v1 = std::max(r1&0xF, r2&0xF)*size+vco,
    1778           0 :                v2 = std::min(r1>>4, r2>>4)*size+vco;
    1779           0 :         u1 = std::max(u1, orig.u1);
    1780           0 :         u2 = std::min(u2, orig.u2);
    1781           0 :         v1 = std::max(v1, orig.v1);
    1782           0 :         v2 = std::min(v2, orig.v2);
    1783           0 :         if(v2-v1==vc2-vc1)
    1784             :         {
    1785           0 :             if(u2-u1==uc2-uc1)
    1786             :             {
    1787           0 :                 return;
    1788             :             }
    1789           0 :             if(u1==uc1)
    1790             :             {
    1791           0 :                 uc1 = u2;
    1792             :             }
    1793           0 :             if(u2==uc2)
    1794             :             {
    1795           0 :                 uc2 = u1;
    1796             :             }
    1797             :         }
    1798           0 :         else if(u2-u1==uc2-uc1)
    1799             :         {
    1800           0 :             if(v1==vc1)
    1801             :             {
    1802           0 :                 vc1 = v2;
    1803             :             }
    1804           0 :             if(v2==vc2)
    1805             :             {
    1806           0 :                 vc2 = v1;
    1807             :             }
    1808             :         }
    1809             :     }
    1810           0 :     if(uc1==uc2 || vc1==vc2)
    1811             :     {
    1812           0 :         return;
    1813             :     }
    1814           0 :     cf.u1 = std::min(cf.u1, uc1);
    1815           0 :     cf.u2 = std::max(cf.u2, uc2);
    1816           0 :     cf.v1 = std::min(cf.v1, vc1);
    1817           0 :     cf.v2 = std::max(cf.v2, vc2);
    1818             : }
    1819             : 
    1820           0 : int calcmergedsize(int orient, const ivec &co, int size, const vertinfo *verts, int numverts)
    1821             : {
    1822           0 :     ushort x1 = verts[0].x,
    1823           0 :            y1 = verts[0].y,
    1824           0 :            z1 = verts[0].z,
    1825           0 :            x2 = x1,
    1826           0 :            y2 = y1,
    1827           0 :            z2 = z1;
    1828           0 :     for(int i = 1; i < numverts; i++)
    1829             :     {
    1830           0 :         const vertinfo &v = verts[i];
    1831           0 :         x1 = std::min(x1, v.x);
    1832           0 :         x2 = std::max(x2, v.x);
    1833           0 :         y1 = std::min(y1, v.y);
    1834           0 :         y2 = std::max(y2, v.y);
    1835           0 :         z1 = std::min(z1, v.z);
    1836           0 :         z2 = std::max(z2, v.z);
    1837             :     }
    1838           0 :     int bits = 0;
    1839           0 :     while(1<<bits < size)
    1840             :     {
    1841           0 :         ++bits;
    1842             :     }
    1843           0 :     bits += 3;
    1844           0 :     ivec mo(co);
    1845           0 :     mo.mask(0xFFF);
    1846           0 :     mo.shl(3);
    1847           0 :     while(bits<15)
    1848             :     {
    1849           0 :         mo.mask(~((1<<bits)-1));
    1850           0 :         if(mo.x <= x1 && mo.x + (1<<bits) >= x2 &&
    1851           0 :            mo.y <= y1 && mo.y + (1<<bits) >= y2 &&
    1852           0 :            mo.z <= z1 && mo.z + (1<<bits) >= z2)
    1853             :         {
    1854           0 :             break;
    1855             :         }
    1856           0 :         bits++;
    1857             :     }
    1858           0 :     return bits-3;
    1859             : }
    1860             : 
    1861           0 : void invalidatemerges(cube &c)
    1862             : {
    1863           0 :     if(c.merged)
    1864             :     {
    1865           0 :         brightencube(c);
    1866           0 :         c.merged = 0;
    1867             :     }
    1868           0 :     if(c.ext)
    1869             :     {
    1870           0 :         if(c.ext->va)
    1871             :         {
    1872           0 :             if(!(c.ext->va->hasmerges&(Merge_Part | Merge_Origin)))
    1873             :             {
    1874           0 :                 return;
    1875             :             }
    1876           0 :             destroyva(c.ext->va);
    1877           0 :             c.ext->va = nullptr;
    1878             :         }
    1879           0 :         if(c.ext->tjoints >= 0)
    1880             :         {
    1881           0 :             c.ext->tjoints = -1;
    1882             :         }
    1883             :     }
    1884           0 :     if(c.children)
    1885             :     {
    1886           0 :         for(int i = 0; i < 8; ++i)
    1887             :         {
    1888           0 :             invalidatemerges((*c.children)[i]);
    1889             :         }
    1890             :     }
    1891             : }
    1892             : 
    1893          11 : uchar octaboxoverlap(const ivec &o, int size, const ivec &bbmin, const ivec &bbmax)
    1894             : {
    1895          11 :     uchar p = 0xFF; // bitmask of possible collisions with octants. 0 bit = 0 octant, etc
    1896          11 :     ivec mid = ivec(o).add(size);
    1897          11 :     if(mid.z <= bbmin.z)
    1898             :     {
    1899           4 :         p &= 0xF0; // not in a -ve Z octant
    1900             :     }
    1901           7 :     else if(mid.z >= bbmax.z)
    1902             :     {
    1903           6 :         p &= 0x0F; // not in a +ve Z octant
    1904             :     }
    1905          11 :     if(mid.y <= bbmin.y)
    1906             :     {
    1907           4 :         p &= 0xCC; // not in a -ve Y octant
    1908             :     }
    1909           7 :     else if(mid.y >= bbmax.y)
    1910             :     {
    1911           5 :         p &= 0x33; // etc..
    1912             :     }
    1913          11 :     if(mid.x <= bbmin.x)
    1914             :     {
    1915           4 :         p &= 0xAA;
    1916             :     }
    1917           7 :     else if(mid.x >= bbmax.x)
    1918             :     {
    1919           4 :         p &= 0x55;
    1920             :     }
    1921          11 :     return p;
    1922             : }
    1923             : 
    1924           2 : void initoctaworldcmds()
    1925             : {
    1926           2 :     addcommand("printcube", reinterpret_cast<identfun>(printcube), "", Id_Command);
    1927           2 : }

Generated by: LCOV version 1.14