LCOV - code coverage report
Current view: top level - engine/world - octaedit.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 107 1114 9.6 %
Date: 2024-11-22 05:07:59 Functions: 32 115 27.8 %

          Line data    Source code
       1             : /* octaedit.cpp: world modification core functionality
       2             :  *
       3             :  * modifying the octree grid can be done by changing the states of cube nodes within
       4             :  * the world, which is made easier with octaedit.cpp's notions of selections (a
       5             :  * rectangular selection of cubes with which to modify together). Selections can
       6             :  * be modified all at once, copied, and pasted throughout the world instead of individual
       7             :  * cubes being modified.
       8             :  *
       9             :  * additionally, this file contains core functionality for rendering of selections
      10             :  * and other constructs generally useful for modifying the level, such as entity
      11             :  * locations and radii.
      12             :  */
      13             : #include "../libprimis-headers/cube.h"
      14             : #include "../../shared/geomexts.h"
      15             : #include "../../shared/glemu.h"
      16             : #include "../../shared/glexts.h"
      17             : #include "../../shared/stream.h"
      18             : 
      19             : #include "light.h"
      20             : #include "octaedit.h"
      21             : #include "octaworld.h"
      22             : #include "raycube.h"
      23             : 
      24             : #include "interface/console.h"
      25             : #include "interface/control.h"
      26             : #include "interface/input.h"
      27             : 
      28             : #include "render/hud.h"
      29             : #include "render/octarender.h"
      30             : #include "render/rendergl.h"
      31             : #include "render/renderlights.h"
      32             : #include "render/renderva.h"
      33             : #include "render/shader.h"
      34             : #include "render/shaderparam.h"
      35             : #include "render/texture.h"
      36             : 
      37             : #include "heightmap.h"
      38             : #include "material.h"
      39             : #include "world.h"
      40             : 
      41             : struct prefabheader
      42             : {
      43             :     char magic[4];
      44             :     int version;
      45             : };
      46             : 
      47             : //used in iengine.h
      48           0 : void boxs(int orient, vec o, const vec &s, float size, bool boxoutline)
      49             : {
      50           0 :     int d  = DIMENSION(orient),
      51           0 :         dc = DIM_COORD(orient);
      52           0 :     float f = boxoutline ? (dc>0 ? 0.2f : -0.2f) : 0;
      53           0 :     o[D[d]] += dc * s[D[d]] + f;
      54             : 
      55           0 :     vec r(0, 0, 0),
      56           0 :         c(0, 0, 0);
      57           0 :     r[R[d]] = s[R[d]];
      58           0 :     c[C[d]] = s[C[d]];
      59             : 
      60           0 :     vec v1 = o,
      61           0 :         v2 = vec(o).add(r),
      62           0 :         v3 = vec(o).add(r).add(c),
      63           0 :         v4 = vec(o).add(c);
      64             : 
      65           0 :     r[R[d]] = 0.5f*size;
      66           0 :     c[C[d]] = 0.5f*size;
      67             : 
      68           0 :     gle::defvertex();
      69           0 :     gle::begin(GL_TRIANGLE_STRIP);
      70           0 :     gle::attrib(vec(v1).sub(r).sub(c));
      71           0 :     gle::attrib(vec(v1).add(r).add(c));
      72             : 
      73           0 :     gle::attrib(vec(v2).add(r).sub(c));
      74           0 :     gle::attrib(vec(v2).sub(r).add(c));
      75             : 
      76           0 :     gle::attrib(vec(v3).add(r).add(c));
      77           0 :     gle::attrib(vec(v3).sub(r).sub(c));
      78             : 
      79           0 :     gle::attrib(vec(v4).sub(r).add(c));
      80           0 :     gle::attrib(vec(v4).add(r).sub(c));
      81             : 
      82           0 :     gle::attrib(vec(v1).sub(r).sub(c));
      83           0 :     gle::attrib(vec(v1).add(r).add(c));
      84           0 :     xtraverts += gle::end();
      85           0 : }
      86             : 
      87             : //used in iengine.h
      88           0 : void boxs(int orient, vec origin, const vec &s, bool boxoutline)
      89             : {
      90           0 :     int d  = DIMENSION(orient),
      91           0 :         dc = DIM_COORD(orient);
      92           0 :     float f = boxoutline ? (dc>0 ? 0.2f : -0.2f) : 0;
      93           0 :     origin[D[d]] += dc * s[D[d]] + f;
      94             : 
      95           0 :     gle::defvertex();
      96           0 :     gle::begin(GL_LINE_LOOP);
      97             :     //draw four surfaces
      98           0 :     gle::attrib(origin); origin[R[d]] += s[R[d]];
      99           0 :     gle::attrib(origin); origin[C[d]] += s[C[d]];
     100           0 :     gle::attrib(origin); origin[R[d]] -= s[R[d]];
     101           0 :     gle::attrib(origin);
     102             : 
     103           0 :     xtraverts += gle::end();
     104           0 : }
     105             : 
     106             : //used in iengine.h
     107           0 : void boxs3D(const vec &origin, vec s, int g, bool boxoutline)
     108             : {
     109           0 :     s.mul(g); //multiply displacement by g(ridpower)
     110           0 :     for(int i = 0; i < 6; ++i) //for each face
     111             :     {
     112           0 :         boxs(i, origin, s, boxoutline);
     113             :     }
     114           0 : }
     115             : 
     116             : //used in iengine.h
     117           0 : void boxsgrid(int orient, vec origin, vec s, int g, bool boxoutline)
     118             : {
     119           0 :     int d  = DIMENSION(orient),
     120           0 :         dc = DIM_COORD(orient);
     121           0 :     float ox = origin[R[d]],
     122           0 :           oy = origin[C[d]],
     123           0 :           xs = s[R[d]],
     124           0 :           ys = s[C[d]],
     125           0 :           f = boxoutline ? (dc>0 ? 0.2f : -0.2f) : 0;
     126             : 
     127           0 :     origin[D[d]] += dc * s[D[d]]*g + f;
     128             : 
     129           0 :     gle::defvertex();
     130           0 :     gle::begin(GL_LINES);
     131           0 :     for(int x = 0; x < xs; ++x)
     132             :     {
     133           0 :         origin[R[d]] += g;
     134           0 :         gle::attrib(origin);
     135           0 :         origin[C[d]] += ys*g;
     136           0 :         gle::attrib(origin);
     137           0 :         origin[C[d]] = oy;
     138             :     }
     139           0 :     for(int y = 0; y < ys; ++y)
     140             :     {
     141           0 :         origin[C[d]] += g;
     142           0 :         origin[R[d]] = ox;
     143           0 :         gle::attrib(origin);
     144           0 :         origin[R[d]] += xs*g;
     145           0 :         gle::attrib(origin);
     146             :     }
     147           0 :     xtraverts += gle::end();
     148           0 : }
     149             : 
     150             : selinfo sel, lastsel; //lastsel is used only in iengine
     151             : static selinfo savedsel;
     152             : 
     153           0 : bool selinfo::validate()
     154             : {
     155           0 :     if(grid <= 0 || grid >= rootworld.mapsize())
     156             :     {
     157           0 :         return false;
     158             :     }
     159           0 :     if(o.x >= rootworld.mapsize() || o.y >= rootworld.mapsize() || o.z >= rootworld.mapsize())
     160             :     {
     161           0 :         return false;
     162             :     }
     163           0 :     if(o.x < 0)
     164             :     {
     165           0 :         s.x -= (grid - 1 - o.x)/grid;
     166           0 :         o.x = 0;
     167             :     }
     168           0 :     if(o.y < 0)
     169             :     {
     170           0 :         s.y -= (grid - 1 - o.y)/grid;
     171           0 :         o.y = 0;
     172             :     }
     173           0 :     if(o.z < 0)
     174             :     {
     175           0 :         s.z -= (grid - 1 - o.z)/grid;
     176           0 :         o.z = 0;
     177             :     }
     178           0 :     s.x = std::clamp(s.x, 0, (rootworld.mapsize() - o.x)/grid);
     179           0 :     s.y = std::clamp(s.y, 0, (rootworld.mapsize() - o.y)/grid);
     180           0 :     s.z = std::clamp(s.z, 0, (rootworld.mapsize() - o.z)/grid);
     181           0 :     return s.x > 0 && s.y > 0 && s.z > 0;
     182             : }
     183             : 
     184             : int orient = 0,
     185             :     gridsize = 8;
     186             : ivec cor, lastcor,
     187             :      cur, lastcur;
     188             : 
     189             : bool editmode     = false,
     190             :      multiplayer  = false,
     191             :      allowediting = false,
     192             :      havesel      = false;
     193             : int horient  = 0,
     194             :     entmoving = 0;
     195             : 
     196             : 
     197           0 : VARF(entediting, 0, 0, 1,
     198             : {
     199             :     if(!entediting)
     200             :     {
     201             :         entcancel();
     202             :     }
     203             : });
     204             : 
     205             : 
     206           3 : void multiplayerwarn()
     207             : {
     208           3 :     conoutf(Console_Error, "operation not available in multiplayer");
     209           3 : }
     210             : 
     211           0 : bool pointinsel(const selinfo &sel, const vec &origin)
     212             : {
     213           0 :     return(origin.x <= sel.o.x+sel.s.x*sel.grid
     214           0 :         && origin.x >= sel.o.x
     215           0 :         && origin.y <= sel.o.y+sel.s.y*sel.grid
     216           0 :         && origin.y >= sel.o.y
     217           0 :         && origin.z <= sel.o.z+sel.s.z*sel.grid
     218           0 :         && origin.z >= sel.o.z);
     219             : }
     220             : 
     221           0 : VARF(dragging, 0, 0, 1,
     222             :     if(!dragging || cor[0]<0)
     223             :     {
     224             :         return;
     225             :     }
     226             :     lastcur = cur;
     227             :     lastcor = cor;
     228             :     sel.grid = gridsize;
     229             :     sel.orient = orient;
     230             : );
     231             : 
     232             : int moving = 0;
     233             : 
     234           0 : VARF(gridpower, 0, 3, 12,
     235             : {
     236             :     if(dragging)
     237             :     {
     238             :         return;
     239             :     }
     240             :     gridsize = 1<<gridpower;
     241             :     if(gridsize>=rootworld.mapsize())
     242             :     {
     243             :         gridsize = rootworld.mapsize()/2;
     244             :     }
     245             :     cancelsel();
     246             : });
     247             : 
     248             : VAR(passthroughsel, 0, 0, 1);
     249             : VAR(selectcorners, 0, 0, 1);
     250           0 : VARF(hmapedit, 0, 0, 1, horient = sel.orient);
     251             : 
     252           2 : void forcenextundo()
     253             : {
     254           2 :     lastsel.orient = -1;
     255           2 : }
     256             : 
     257           2 : void cubecancel()
     258             : {
     259           2 :     havesel = false;
     260           2 :     moving = dragging = hmapedit = passthroughsel = 0;
     261           2 :     forcenextundo();
     262           2 :     hmapcancel();
     263           2 : }
     264             : 
     265           1 : void cancelsel()
     266             : {
     267           1 :     cubecancel();
     268           1 :     entcancel();
     269           1 : }
     270             : 
     271             : //used in iengine
     272           0 : bool haveselent()
     273             : {
     274           0 :     return entgroup.size() > 0;
     275             : }
     276             : 
     277           5 : bool noedit(bool inview, bool msg)
     278             : {
     279           5 :     if(!editmode)
     280             :     {
     281           5 :         if(msg)
     282             :         {
     283           5 :             conoutf(Console_Error, "operation only allowed in edit mode");
     284             :         }
     285           5 :         return true;
     286             :     }
     287           0 :     if(inview || haveselent())
     288             :     {
     289           0 :         return false;
     290             :     }
     291           0 :     vec o(sel.o), s(sel.s);
     292           0 :     s.mul(sel.grid / 2.0f);
     293           0 :     o.add(s);
     294           0 :     float r = std::max(std::max(s.x, s.y), s.z);
     295           0 :     bool viewable = view.isvisiblesphere(r, o) != ViewFrustumCull_NotVisible;
     296           0 :     if(!viewable && msg)
     297             :     {
     298           0 :         conoutf(Console_Error, "selection not in view");
     299             :     }
     300           0 :     return !viewable;
     301             : }
     302             : 
     303           1 : void reorient()
     304             : {
     305           1 :     sel.cx = 0;
     306           1 :     sel.cy = 0;
     307           1 :     sel.cxs = sel.s[R[DIMENSION(orient)]]*2;
     308           1 :     sel.cys = sel.s[C[DIMENSION(orient)]]*2;
     309           1 :     sel.orient = orient;
     310           1 : }
     311             : 
     312             : ///////// selection support /////////////
     313             : 
     314           0 : cube &blockcube(int x, int y, int z, const block3 &b, int rgrid) // looks up a world cube, based on coordinates mapped by the block
     315             : {
     316           0 :     int dim = DIMENSION(b.orient),
     317           0 :         dc = DIM_COORD(b.orient);
     318           0 :     ivec s(dim, x*b.grid, y*b.grid, dc*(b.s[dim]-1)*b.grid);
     319           0 :     s.add(b.o);
     320           0 :     if(dc)
     321             :     {
     322           0 :         s[dim] -= z*b.grid;
     323             :     }
     324             :     else
     325             :     {
     326           0 :         s[dim] += z*b.grid;
     327             :     }
     328           0 :     return rootworld.lookupcube(s, rgrid);
     329             : }
     330             : 
     331             : ////////////// cursor ///////////////
     332             : 
     333             : int selchildcount = 0,
     334             :     selchildmat = -1;
     335             : 
     336             : //used in iengine.h
     337           0 : void countselchild(const std::array<cube, 8> &c, const ivec &cor, int size)
     338             : {
     339           0 :     ivec ss = ivec(sel.s).mul(sel.grid);
     340           0 :     uchar possible = octaboxoverlap(cor, size, sel.o, ivec(sel.o).add(ss));
     341           0 :     for(int i = 0; i < 8; ++i)
     342             :     {
     343           0 :         if(possible&(1<<i))
     344             :         {
     345           0 :             ivec o(i, cor, size);
     346           0 :             if(c[i].children)
     347             :             {
     348           0 :                 countselchild(*(c[i].children), o, size/2);
     349             :             }
     350             :             else
     351             :             {
     352           0 :                 selchildcount++;
     353           0 :                 if(c[i].material != Mat_Air && selchildmat != Mat_Air)
     354             :                 {
     355           0 :                     if(selchildmat < 0)
     356             :                     {
     357           0 :                         selchildmat = c[i].material;
     358             :                     }
     359           0 :                     else if(selchildmat != c[i].material)
     360             :                     {
     361           0 :                         selchildmat = Mat_Air;
     362             :                     }
     363             :                 }
     364             :             }
     365             :         }
     366             :     }
     367           0 : }
     368             : 
     369             : //used in iengine.h
     370           0 : void normalizelookupcube(const ivec &o)
     371             : {
     372           0 :     if(lusize>gridsize)
     373             :     {
     374           0 :         lu.x += (o.x-lu.x)/gridsize*gridsize;
     375           0 :         lu.y += (o.y-lu.y)/gridsize*gridsize;
     376           0 :         lu.z += (o.z-lu.z)/gridsize*gridsize;
     377             :     }
     378           0 :     else if(gridsize>lusize)
     379             :     {
     380           0 :         lu.x &= ~(gridsize-1);
     381           0 :         lu.y &= ~(gridsize-1);
     382           0 :         lu.z &= ~(gridsize-1);
     383             :     }
     384           0 :     lusize = gridsize;
     385           0 : }
     386             : 
     387             : //used in iengine.h
     388           0 : void updateselection()
     389             : {
     390           0 :     sel.o.x = std::min(lastcur.x, cur.x);
     391           0 :     sel.o.y = std::min(lastcur.y, cur.y);
     392           0 :     sel.o.z = std::min(lastcur.z, cur.z);
     393           0 :     sel.s.x = std::abs(lastcur.x-cur.x)/sel.grid+1;
     394           0 :     sel.s.y = std::abs(lastcur.y-cur.y)/sel.grid+1;
     395           0 :     sel.s.z = std::abs(lastcur.z-cur.z)/sel.grid+1;
     396           0 : }
     397             : 
     398           0 : bool editmoveplane(const vec &o, const vec &ray, int d, float off, vec &handle, vec &dest, bool first)
     399             : {
     400           0 :     plane pl(d, off);
     401           0 :     float dist = 0.0f;
     402           0 :     if(!pl.rayintersect(player->o, ray, dist))
     403             :     {
     404           0 :         return false;
     405             :     }
     406           0 :     dest = vec(ray).mul(dist).add(player->o);
     407           0 :     if(first)
     408             :     {
     409           0 :         handle = vec(dest).sub(o);
     410             :     }
     411           0 :     dest.sub(handle);
     412           0 :     return true;
     413             : }
     414             : 
     415             : //////////// ready changes to vertex arrays ////////////
     416             : 
     417           0 : static void readychanges(const ivec &bbmin, const ivec &bbmax, std::array<cube, 8> &c, const ivec &cor, int size)
     418             : {
     419           0 :     LOOP_OCTA_BOX(cor, size, bbmin, bbmax)
     420             :     {
     421           0 :         ivec o(i, cor, size);
     422           0 :         if(c[i].ext)
     423             :         {
     424           0 :             if(c[i].ext->va)             // removes va s so that octarender will recreate
     425             :             {
     426           0 :                 int hasmerges = c[i].ext->va->hasmerges;
     427           0 :                 destroyva(c[i].ext->va);
     428           0 :                 c[i].ext->va = nullptr;
     429           0 :                 if(hasmerges)
     430             :                 {
     431           0 :                     invalidatemerges(c[i]);
     432             :                 }
     433             :             }
     434           0 :             freeoctaentities(c[i]);
     435           0 :             c[i].ext->tjoints = -1;
     436             :         }
     437           0 :         if(c[i].children)
     438             :         {
     439           0 :             if(size<=1)
     440             :             {
     441           0 :                 setcubefaces(c[i], facesolid);
     442           0 :                 c[i].discardchildren(true);
     443           0 :                 brightencube(c[i]);
     444             :             }
     445             :             else
     446             :             {
     447           0 :                 readychanges(bbmin, bbmax, *(c[i].children), o, size/2);
     448             :             }
     449             :         }
     450             :         else
     451             :         {
     452           0 :             brightencube(c[i]);
     453             :         }
     454             :     }
     455           0 : }
     456             : 
     457           0 : void cubeworld::commitchanges(bool force)
     458             : {
     459           0 :     if(!force && !haschanged)
     460             :     {
     461           0 :         return;
     462             :     }
     463           0 :     haschanged = false;
     464           0 :     int oldlen = valist.size();
     465           0 :     resetclipplanes();
     466           0 :     entitiesinoctanodes();
     467           0 :     inbetweenframes = false;
     468           0 :     octarender();
     469           0 :     inbetweenframes = true;
     470           0 :     setupmaterials(oldlen);
     471           0 :     clearshadowcache();
     472           0 :     updatevabbs();
     473             : }
     474             : 
     475           0 : void cubeworld::changed(const ivec &bbmin, const ivec &bbmax, bool commit)
     476             : {
     477           0 :     readychanges(bbmin, bbmax, *worldroot, ivec(0, 0, 0), mapsize()/2);
     478           0 :     haschanged = true;
     479             : 
     480           0 :     if(commit)
     481             :     {
     482           0 :         commitchanges();
     483             :     }
     484           0 : }
     485             : 
     486           0 : void cubeworld::changed(const block3 &sel, bool commit)
     487             : {
     488           0 :     if(!sel.s)
     489             :     {
     490           0 :         return;
     491             :     }
     492           0 :     readychanges(ivec(sel.o).sub(1), ivec(sel.s).mul(sel.grid).add(sel.o).add(1), *worldroot, ivec(0, 0, 0), mapsize()/2);
     493           0 :     haschanged = true;
     494           0 :     if(commit)
     495             :     {
     496           0 :         commitchanges();
     497             :     }
     498             : }
     499             : 
     500             : //////////// copy and undo /////////////
     501           0 : static void copycube(const cube &src, cube &dst)
     502             : {
     503           0 :     dst = src;
     504           0 :     dst.visible = 0;
     505           0 :     dst.merged = 0;
     506           0 :     dst.ext = nullptr; // src cube is responsible for va destruction
     507             :     //recursively apply to children
     508           0 :     if(src.children)
     509             :     {
     510           0 :         dst.children = newcubes(faceempty);
     511           0 :         for(int i = 0; i < 8; ++i)
     512             :         {
     513           0 :             copycube((*src.children)[i], (*dst.children)[i]);
     514             :         }
     515             :     }
     516           0 : }
     517             : 
     518           0 : void pastecube(const cube &src, cube &dst)
     519             : {
     520           0 :     dst.discardchildren();
     521           0 :     copycube(src, dst);
     522           0 : }
     523             : 
     524             : //used in iengine.h
     525           0 : void blockcopy(const block3 &s, int rgrid, block3 *b)
     526             : {
     527           0 :     *b = s;
     528           0 :     cube *q = b->c();
     529           0 :     uint i = 0;
     530           0 :     LOOP_XYZ(s, rgrid, copycube(c, q[i]); i++);
     531           0 : }
     532             : 
     533           0 : block3 *blockcopy(const block3 &s, int rgrid)
     534             : {
     535           0 :     int bsize = sizeof(block3)+sizeof(cube)*s.size();
     536           0 :     if(bsize <= 0 || bsize > (100<<20))
     537             :     {
     538           0 :         return nullptr;
     539             :     }
     540           0 :     block3 *b = reinterpret_cast<block3 *>(new uchar[bsize]); //create a new block3 pointing to an appropriate sized memory area
     541           0 :     if(b) //should always be true
     542             :     {
     543           0 :         blockcopy(s, rgrid, b); //copy the block3 s to b
     544             :     }
     545           0 :     return b;
     546             : }
     547             : 
     548           0 : void freeblock(block3 *b, bool alloced = true)
     549             : {
     550           0 :     cube *q = b->c();
     551           0 :     uint j = 0;
     552           0 :     for(int i = 0; i < b->size(); ++i)
     553             :     {
     554           0 :         (q[j]).discardchildren();
     555           0 :         j++;
     556             :     }
     557           0 :     if(alloced)
     558             :     {
     559           0 :         delete[] b;
     560             :     }
     561           0 : }
     562             : 
     563           0 : void selgridmap(const selinfo &sel, uchar *g)
     564             : {
     565           0 :     for(int z = 0; z < sel.s[D[DIMENSION(sel.orient)]]; ++z)
     566             :     {
     567           0 :         for(int y = 0; y < sel.s[C[DIMENSION(sel.orient)]]; ++y)
     568             :         {
     569           0 :             for(int x = 0; x < sel.s[R[DIMENSION(sel.orient)]]; ++x)
     570             :             {
     571           0 :                 blockcube(x,y,z,sel,-sel.grid);
     572           0 :                 *g++ = BITSCAN(lusize);
     573             :             }
     574             :         }
     575             :     }
     576           0 : }
     577             : 
     578           0 : void freeundo(undoblock *u)
     579             : {
     580           0 :     if(!u->numents)
     581             :     {
     582           0 :         freeblock(u->block(), false);
     583             :     }
     584           0 :     delete[] reinterpret_cast<uchar *>(u);  //re-cast to uchar array so it can be destructed properly
     585           0 : }
     586             : 
     587           0 : static int undosize(undoblock *u)
     588             : {
     589           0 :     if(u->numents)
     590             :     {
     591           0 :         return u->numents*sizeof(undoent);
     592             :     }
     593             :     else
     594             :     {
     595           0 :         block3 *b = u->block();
     596           0 :         cube *q = b->c();
     597           0 :         int size = b->size(),
     598           0 :             total = size;
     599           0 :         uint i = 0;
     600           0 :         for(int j = 0; j < size; ++j)
     601             :         {
     602           0 :             total += familysize(q[i])*sizeof(cube);
     603           0 :             i++;
     604             :         }
     605           0 :         return total;
     606             :     }
     607             : }
     608             : 
     609             : std::deque<undoblock *> undos, redos;
     610             : VARP(undomegs, 0, 5, 100);                              // bounded by n megs, zero means no undo history
     611             : int totalundos = 0;
     612             : 
     613           1 : void pruneundos(int maxremain)                          // bound memory
     614             : {
     615           1 :     while(totalundos > maxremain && !undos.empty())
     616             :     {
     617           0 :         undoblock *u = undos.front();
     618           0 :         undos.pop_front();
     619           0 :         totalundos -= u->size;
     620           0 :         freeundo(u);
     621             :     }
     622             :     //conoutf(CON_DEBUG, "undo: %d of %d(%%%d)", totalundos, undomegs<<20, totalundos*100/(undomegs<<20));
     623           1 :     while(!redos.empty())
     624             :     {
     625           0 :         undoblock *u = redos.front();
     626           0 :         redos.pop_front();
     627           0 :         totalundos -= u->size;
     628           0 :         freeundo(u);
     629             :     }
     630           1 : }
     631             : 
     632           0 : undoblock *newundocube(const selinfo &s)
     633             : {
     634           0 :     int ssize = s.size(),
     635           0 :         selgridsize = ssize,
     636           0 :         blocksize = sizeof(block3)+ssize*sizeof(cube);
     637           0 :     if(blocksize <= 0 || blocksize > (undomegs<<20))
     638             :     {
     639           0 :         return nullptr;
     640             :     }
     641           0 :     undoblock *u = reinterpret_cast<undoblock *>(new uchar[sizeof(undoblock) + blocksize + selgridsize]);
     642           0 :     if(!u)
     643             :     {
     644           0 :         return nullptr;
     645             :     }
     646           0 :     u->numents = 0;
     647           0 :     block3 *b = u->block();
     648           0 :     blockcopy(s, -s.grid, b);
     649           0 :     uchar *g = u->gridmap();
     650           0 :     selgridmap(s, g);
     651           0 :     return u;
     652             : }
     653             : 
     654           0 : void addundo(undoblock *u)
     655             : {
     656           0 :     u->size = undosize(u);
     657           0 :     u->timestamp = totalmillis;
     658           0 :     undos.push_back(u);
     659           0 :     totalundos += u->size;
     660           0 :     pruneundos(undomegs<<20);
     661           0 : }
     662             : 
     663             : VARP(nompedit, 0, 1, 1);
     664             : 
     665           0 : static int countblock(const cube * const c, int n = 8)
     666             : {
     667           0 :     int r = 0;
     668           0 :     for(int i = 0; i < n; ++i)
     669             :     {
     670           0 :         if(c[i].children)
     671             :         {
     672           0 :             r += countblock(c[i].children->data());
     673             :         }
     674             :         else
     675             :         {
     676           0 :             ++r;
     677             :         }
     678             :     }
     679           0 :     return r;
     680             : }
     681             : 
     682           0 : int countblock(block3 *b)
     683             : {
     684           0 :     return countblock(b->getcube(), b->size());
     685             : }
     686             : 
     687             : std::vector<editinfo *> editinfos;
     688             : 
     689             : template<class B>
     690           0 : static void packcube(const cube &c, B &buf)
     691             : {
     692             :     //recursvely apply to children
     693           0 :     if(c.children)
     694             :     {
     695           0 :         buf.push_back(0xFF);
     696           0 :         for(int i = 0; i < 8; ++i)
     697             :         {
     698           0 :             packcube((*c.children)[i], buf);
     699             :         }
     700             :     }
     701             :     else
     702             :     {
     703           0 :         cube data = c;
     704           0 :         buf.push_back(c.material&0xFF);
     705           0 :         buf.push_back(c.material>>8);
     706           0 :         for(uint i = 0; i < sizeof(data.edges); ++i)
     707             :         {
     708           0 :             buf.push_back(data.edges[i]);
     709             :         }
     710           0 :         for(uint i = 0; i < sizeof(data.texture); ++i)
     711             :         {
     712           0 :             buf.push_back(reinterpret_cast<uchar *>(data.texture)[i]);
     713             :         }
     714             :     }
     715           0 : }
     716             : 
     717             : template<class B>
     718           0 : static bool packblock(const block3 &b, B &buf)
     719             : {
     720           0 :     if(b.size() <= 0 || b.size() > (1<<20))
     721             :     {
     722           0 :         return false;
     723             :     }
     724           0 :     block3 hdr = b;
     725           0 :     for(uint i = 0; i < sizeof(hdr); ++i)
     726             :     {
     727           0 :         buf.push_back(reinterpret_cast<const uchar *>(&hdr)[i]);
     728             :     }
     729           0 :     const cube *c = b.getcube();
     730           0 :     for(uint i = 0; i < static_cast<uint>(b.size()); ++i)
     731             :     {
     732           0 :         packcube(c[i], buf);
     733             :     }
     734           0 :     return true;
     735             : }
     736             : 
     737             : struct vslothdr
     738             : {
     739             :     ushort index;
     740             :     ushort slot;
     741             : };
     742             : 
     743           0 : static void packvslots(const cube &c, std::vector<uchar> &buf, std::vector<ushort> &used)
     744             : {
     745             :     //recursively apply to children
     746           0 :     if(c.children)
     747             :     {
     748           0 :         for(int i = 0; i < 8; ++i)
     749             :         {
     750           0 :             packvslots((*c.children)[i], buf, used);
     751             :         }
     752             :     }
     753             :     else
     754             :     {
     755           0 :         for(int i = 0; i < 6; ++i) //for each face
     756             :         {
     757           0 :             ushort index = c.texture[i];
     758           0 :             if((vslots.size() > index) && vslots[index]->changed && std::find(used.begin(), used.end(), index) != used.end())
     759             :             {
     760           0 :                 used.push_back(index);
     761           0 :                 VSlot &vs = *vslots[index];
     762           0 :                 for(uint i = 0; i < sizeof(vslothdr); ++i)
     763             :                 {
     764           0 :                     buf.emplace_back();
     765             :                 }
     766           0 :                 vslothdr &hdr = *reinterpret_cast<vslothdr *>(&(*(buf.end())) - sizeof(vslothdr));
     767           0 :                 hdr.index = index;
     768           0 :                 hdr.slot = vs.slot->index;
     769           0 :                 packvslot(buf, vs);
     770             :             }
     771             :         }
     772             :     }
     773           0 : }
     774             : 
     775           0 : static void packvslots(const block3 &b, std::vector<uchar> &buf)
     776             : {
     777           0 :     std::vector<ushort> used;
     778           0 :     const cube *c = b.getcube();
     779           0 :     for(int i = 0; i < b.size(); ++i)
     780             :     {
     781           0 :         packvslots(c[i], buf, used);
     782             :     }
     783           0 :     for(uint i = 0; i < sizeof(vslothdr); ++i)
     784             :     {
     785           0 :         buf.push_back(0);
     786             :     }
     787           0 : }
     788             : 
     789             : template<class B>
     790           0 : static void unpackcube(cube &c, B &buf)
     791             : {
     792           0 :     int mat = buf.get();
     793           0 :     if(mat == 0xFF)
     794             :     {
     795           0 :         c.children = newcubes(faceempty);
     796             :         //recursively apply to children
     797           0 :         for(int i = 0; i < 8; ++i)
     798             :         {
     799           0 :             unpackcube((*c.children)[i], buf);
     800             :         }
     801             :     }
     802             :     else
     803             :     {
     804           0 :         c.material = mat | (buf.get()<<8);
     805           0 :         buf.get(c.edges, sizeof(c.edges));
     806           0 :         buf.get(reinterpret_cast<uchar *>(c.texture), sizeof(c.texture));
     807             :     }
     808           0 : }
     809             : 
     810             : template<class B>
     811           0 : static bool unpackblock(block3 *&b, B &buf)
     812             : {
     813           0 :     if(b)
     814             :     {
     815           0 :         freeblock(b);
     816           0 :         b = nullptr;
     817             :     }
     818           0 :     block3 hdr;
     819           0 :     if(buf.get(reinterpret_cast<uchar *>(&hdr), sizeof(hdr)) < static_cast<int>(sizeof(hdr)))
     820             :     {
     821           0 :         return false;
     822             :     }
     823           0 :     if(hdr.size() > (1<<20) || hdr.grid <= 0 || hdr.grid > (1<<12))
     824             :     {
     825           0 :         return false;
     826             :     }
     827           0 :     b = reinterpret_cast<block3 *>(new uchar[sizeof(block3)+hdr.size()*sizeof(cube)]);
     828           0 :     if(!b)
     829             :     {
     830           0 :         return false;
     831             :     }
     832           0 :     *b = hdr;
     833           0 :     cube *c = b->c();
     834           0 :     std::memset(c, 0, b->size()*sizeof(cube));
     835           0 :     for(int i = 0; i < b->size(); ++i)
     836             :     {
     837           0 :         unpackcube(c[i], buf);
     838             :     }
     839           0 :     return true;
     840             : }
     841             : 
     842             : struct vslotmap
     843             : {
     844             :     int index;
     845             :     VSlot *vslot;
     846             : 
     847             :     vslotmap() {}
     848           0 :     vslotmap(int index, VSlot *vslot) : index(index), vslot(vslot) {}
     849             : };
     850             : 
     851             : static std::vector<vslotmap> remappedvslots;
     852             : 
     853             : //used in iengine.h so remappedvslots does not need to be exposed
     854           0 : void clearremappedvslots()
     855             : {
     856           0 :     remappedvslots.clear();
     857           0 : }
     858             : static std::vector<vslotmap> unpackingvslots;
     859             : 
     860           0 : static void unpackvslots(cube &c, ucharbuf &buf)
     861             : {
     862             :     //recursively apply to children
     863           0 :     if(c.children)
     864             :     {
     865           0 :         for(int i = 0; i < 8; ++i)
     866             :         {
     867           0 :             unpackvslots((*c.children)[i], buf);
     868             :         }
     869             :     }
     870             :     else
     871             :     {
     872           0 :         for(int i = 0; i < 6; ++i) //one for each face
     873             :         {
     874           0 :             ushort tex = c.texture[i];
     875           0 :             for(uint j = 0; j < unpackingvslots.size(); j++)
     876             :             {
     877           0 :                 if(unpackingvslots[j].index == tex)
     878             :                 {
     879           0 :                     c.texture[i] = unpackingvslots[j].vslot->index;
     880           0 :                     break;
     881             :                 }
     882             :             }
     883             :         }
     884             :     }
     885           0 : }
     886             : 
     887           0 : static void unpackvslots(block3 &b, ucharbuf &buf)
     888             : {
     889           0 :     while(buf.remaining() >= static_cast<int>(sizeof(vslothdr)))
     890             :     {
     891           0 :         vslothdr &hdr = *reinterpret_cast<vslothdr *>(buf.pad(sizeof(vslothdr)));
     892           0 :         if(!hdr.index)
     893             :         {
     894           0 :             break;
     895             :         }
     896           0 :         VSlot &vs = *lookupslot(hdr.slot, false).variants;
     897           0 :         VSlot ds;
     898           0 :         if(!unpackvslot(buf, ds, false))
     899             :         {
     900           0 :             break;
     901             :         }
     902           0 :         if(vs.index < 0 || vs.index == Default_Sky)
     903             :         {
     904           0 :             continue;
     905             :         }
     906           0 :         VSlot *edit = editvslot(vs, ds);
     907           0 :         unpackingvslots.emplace_back(vslotmap(hdr.index, edit ? edit : &vs));
     908           0 :     }
     909             : 
     910           0 :     cube *c = b.c();
     911           0 :     for(int i = 0; i < b.size(); ++i)
     912             :     {
     913           0 :         unpackvslots(c[i], buf);
     914             :     }
     915             : 
     916           0 :     unpackingvslots.clear();
     917           0 : }
     918             : 
     919           0 : static bool compresseditinfo(const uchar *inbuf, int inlen, uchar *&outbuf, int &outlen)
     920             : {
     921           0 :     uLongf len = compressBound(inlen);
     922           0 :     if(len > (1<<20))
     923             :     {
     924           0 :         return false;
     925             :     }
     926           0 :     outbuf = new uchar[len];
     927           0 :     if(!outbuf || compress2(static_cast<Bytef *>(outbuf), &len, static_cast<const Bytef *>(inbuf), inlen, Z_BEST_COMPRESSION) != Z_OK || len > (1<<16))
     928             :     {
     929           0 :         delete[] outbuf;
     930           0 :         outbuf = nullptr;
     931           0 :         return false;
     932             :     }
     933           0 :     outlen = len;
     934           0 :     return true;
     935             : }
     936             : 
     937             : //used in iengine.h
     938           0 : bool uncompresseditinfo(const uchar *inbuf, int inlen, uchar *&outbuf, int &outlen)
     939             : {
     940           0 :     if(compressBound(outlen) > (1<<20))
     941             :     {
     942           0 :         return false;
     943             :     }
     944           0 :     uLongf len = outlen;
     945           0 :     outbuf = new uchar[len];
     946           0 :     if(!outbuf || uncompress(static_cast<Bytef *>(outbuf), &len, static_cast<const Bytef *>(inbuf), inlen) != Z_OK)
     947             :     {
     948           0 :         delete[] outbuf;
     949           0 :         outbuf = nullptr;
     950           0 :         return false;
     951             :     }
     952           0 :     outlen = len;
     953           0 :     return true;
     954             : }
     955             : 
     956             : //used in iengine.h
     957           0 : bool packeditinfo(const editinfo *e, int &inlen, uchar *&outbuf, int &outlen)
     958             : {
     959           0 :     std::vector<uchar> buf;
     960           0 :     if(!e || !e->copy || !packblock(*e->copy, buf))
     961             :     {
     962           0 :         return false;
     963             :     }
     964           0 :     packvslots(*e->copy, buf);
     965           0 :     inlen = buf.size();
     966           0 :     return compresseditinfo(buf.data(), buf.size(), outbuf, outlen);
     967           0 : }
     968             : 
     969             : //used in iengine.h
     970           0 : bool unpackeditinfo(editinfo *&e, const uchar *inbuf, int inlen, int outlen)
     971             : {
     972           0 :     if(e && e->copy)
     973             :     {
     974           0 :         freeblock(e->copy);
     975           0 :         e->copy = nullptr;
     976             :     }
     977           0 :     uchar *outbuf = nullptr;
     978           0 :     if(!uncompresseditinfo(inbuf, inlen, outbuf, outlen))
     979             :     {
     980           0 :         return false;
     981             :     }
     982           0 :     ucharbuf buf(outbuf, outlen);
     983           0 :     if(!e)
     984             :     {
     985             :         editinfo *e;
     986           0 :         editinfos.push_back(e);
     987             :     }
     988           0 :     if(!unpackblock(e->copy, buf))
     989             :     {
     990           0 :         delete[] outbuf;
     991           0 :         return false;
     992             :     }
     993           0 :     unpackvslots(*e->copy, buf);
     994           0 :     delete[] outbuf;
     995           0 :     return true;
     996             : }
     997             : 
     998             : //used in iengine.h
     999           0 : void freeeditinfo(editinfo *&e)
    1000             : {
    1001           0 :     if(!e)
    1002             :     {
    1003           0 :         return;
    1004             :     }
    1005           0 :     editinfos.erase(std::find(editinfos.begin(), editinfos.end(), e));
    1006           0 :     if(e->copy)
    1007             :     {
    1008           0 :         freeblock(e->copy);
    1009             :     }
    1010           0 :     delete e;
    1011           0 :     e = nullptr;
    1012             : }
    1013             : 
    1014             : //used in iengine.h
    1015           0 : bool packundo(undoblock *u, int &inlen, uchar *&outbuf, int &outlen)
    1016             : {
    1017           0 :     std::vector<uchar> buf;
    1018           0 :     buf.reserve(512);
    1019           0 :     for(uint i = 0; i < sizeof(ushort); ++i)
    1020             :     {
    1021           0 :         buf.emplace_back();
    1022             :     }
    1023           0 :     *reinterpret_cast<ushort *>(buf.data()) = static_cast<ushort>(u->numents);
    1024           0 :     if(u->numents)
    1025             :     {
    1026           0 :         const undoent *ue = u->ents();
    1027           0 :         for(int i = 0; i < u->numents; ++i)
    1028             :         {
    1029           0 :             for(uint i = 0; i < sizeof(ushort); ++i)
    1030             :             {
    1031           0 :                 buf.emplace_back();
    1032             :             }
    1033           0 :             *reinterpret_cast<ushort *>(&(*buf.end()) - sizeof(ushort)) = static_cast<ushort>(ue[i].i);
    1034           0 :             for(uint i = 0; i < sizeof(entity); ++i)
    1035             :             {
    1036           0 :                 buf.emplace_back();
    1037             :             }
    1038           0 :             entity &e = *reinterpret_cast<entity *>(&(*buf.end()) - sizeof(entity));
    1039           0 :             e = ue[i].e;
    1040             :         }
    1041             :     }
    1042             :     else
    1043             :     {
    1044           0 :         const block3 &b = *u->block();
    1045           0 :         if(!packblock(b, buf))
    1046             :         {
    1047           0 :             return false;
    1048             :         }
    1049           0 :         for(int i = 0; i < b.size(); ++i)
    1050             :         {
    1051           0 :             buf.push_back(u->gridmap()[i]);
    1052             :         }
    1053           0 :         packvslots(b, buf);
    1054             :     }
    1055           0 :     inlen = buf.size();
    1056           0 :     return compresseditinfo(buf.data(), buf.size(), outbuf, outlen);
    1057           0 : }
    1058             : 
    1059             : //used in iengine.h
    1060           0 : bool packundo(bool undo, int &inlen, uchar *&outbuf, int &outlen)
    1061             : {
    1062           0 :     if(undo)
    1063             :     {
    1064           0 :         return !undos.empty() && packundo(undos.back(), inlen, outbuf, outlen);
    1065             :     }
    1066             :     else
    1067             :     {
    1068           0 :         return !redos.empty() && packundo(redos.back(), inlen, outbuf, outlen);
    1069             :     }
    1070             : }
    1071             : 
    1072             : struct prefab : editinfo
    1073             : {
    1074             :     std::string name;
    1075             :     GLuint ebo, vbo;
    1076             :     int numtris, numverts;
    1077             : 
    1078           0 :     prefab() : name(""), ebo(0), vbo(0), numtris(0), numverts(0) {}
    1079           0 :     ~prefab()
    1080             :     {
    1081           0 :         if(copy)
    1082             :         {
    1083           0 :             freeblock(copy);
    1084             :         }
    1085           0 :     }
    1086             : 
    1087           0 :     void cleanup()
    1088             :     {
    1089           0 :         if(ebo)
    1090             :         {
    1091           0 :             glDeleteBuffers(1, &ebo);
    1092           0 :             ebo = 0;
    1093             :         }
    1094           0 :         if(vbo)
    1095             :         {
    1096           0 :             glDeleteBuffers(1, &vbo);
    1097           0 :             vbo = 0;
    1098             :         }
    1099           0 :         numtris = numverts = 0;
    1100           0 :     }
    1101             : };
    1102             : 
    1103             : static std::unordered_map<std::string, prefab> prefabs;
    1104             : 
    1105           0 : void cleanupprefabs()
    1106             : {
    1107           0 :     for(auto &[k, i] : prefabs)
    1108             :     {
    1109           0 :         i.cleanup();
    1110             :     }
    1111           0 : }
    1112             : 
    1113           0 : void pasteundoblock(block3 *b, const uchar *g)
    1114             : {
    1115           0 :     cube *s = b->c();
    1116           0 :     uint i = 0;
    1117           0 :     LOOP_XYZ(*b, 1<<std::min(static_cast<int>(*g++), rootworld.mapscale()-1), pastecube(s[i], c); i++; );
    1118           0 : }
    1119             : 
    1120             : //used in client prefab unpacking, handles the octree unpacking (not the entities,
    1121             : // which are game-dependent)
    1122           0 : void unpackundocube(ucharbuf &buf, uchar *outbuf)
    1123             : {
    1124           0 :     block3 *b = nullptr;
    1125           0 :     if(!unpackblock(b, buf) || b->grid >= rootworld.mapsize() || buf.remaining() < b->size())
    1126             :     {
    1127           0 :         freeblock(b);
    1128           0 :         delete[] outbuf;
    1129           0 :         return;
    1130             :     }
    1131           0 :     uchar *g = buf.pad(b->size());
    1132           0 :     unpackvslots(*b, buf);
    1133           0 :     pasteundoblock(b, g);
    1134           0 :     rootworld.changed(*b, false);
    1135           0 :     freeblock(b);
    1136             : }
    1137             : 
    1138           0 : void makeundo(selinfo &s)
    1139             : {
    1140           0 :     undoblock *u = newundocube(s);
    1141           0 :     if(u)
    1142             :     {
    1143           0 :         addundo(u);
    1144             :     }
    1145           0 : }
    1146             : 
    1147           0 : void makeundo()                        // stores state of selected cubes before editing
    1148             : {
    1149           0 :     if(lastsel==sel || !sel.s)
    1150             :     {
    1151           0 :         return;
    1152             :     }
    1153           0 :     lastsel=sel;
    1154           0 :     makeundo(sel);
    1155             : }
    1156             : 
    1157           0 : void pasteblock(const block3 &b, selinfo &sel, bool local)
    1158             : {
    1159           0 :     sel.s = b.s;
    1160           0 :     int o = sel.orient;
    1161           0 :     sel.orient = b.orient;
    1162           0 :     const cube *s = b.getcube();
    1163           0 :     uint i = 0;
    1164           0 :     LOOP_SEL_XYZ(if(!(s[i].isempty()) || s[i].children || s[i].material != Mat_Air) pastecube(s[i], c); i++); // 'transparent'. old opaque by 'delcube; paste'
    1165           0 :     sel.orient = o;
    1166           0 : }
    1167             : 
    1168           0 : prefab *loadprefab(const char *name, bool msg = true)
    1169             : {
    1170           0 :     auto itr = prefabs.find(name);
    1171           0 :     if(itr != prefabs.end())
    1172             :     {
    1173           0 :         return &(*itr).second;
    1174             :     }
    1175           0 :     DEF_FORMAT_STRING(filename, "media/prefab/%s.obr", name);
    1176           0 :     path(filename);
    1177           0 :     stream *f = opengzfile(filename, "rb");
    1178           0 :     if(!f)
    1179             :     {
    1180           0 :         if(msg)
    1181             :         {
    1182           0 :             conoutf(Console_Error, "could not read prefab %s", filename);
    1183             :         }
    1184           0 :         return nullptr;
    1185             :     }
    1186             :     prefabheader hdr;
    1187           0 :     if(f->read(&hdr, sizeof(hdr)) != sizeof(prefabheader) || std::memcmp(hdr.magic, "OEBR", 4))
    1188             :     {
    1189           0 :         delete f;
    1190           0 :         if(msg)
    1191             :         {
    1192           0 :             conoutf(Console_Error, "prefab %s has malformatted header", filename);
    1193           0 :             return nullptr;
    1194             :         }
    1195             :     }
    1196           0 :     if(hdr.version != 0)
    1197             :     {
    1198           0 :         delete f;
    1199           0 :         if(msg)
    1200             :         {
    1201           0 :            conoutf(Console_Error, "prefab %s uses unsupported version", filename);
    1202           0 :            return nullptr;
    1203             :         }
    1204             :     }
    1205           0 :     streambuf<uchar> s(f);
    1206           0 :     block3 *copy = nullptr;
    1207           0 :     if(!unpackblock(copy, s))
    1208             :     {
    1209           0 :         delete f;
    1210           0 :         if(msg)
    1211             :         {
    1212           0 :             conoutf(Console_Error, "could not unpack prefab %s", filename);
    1213           0 :             return nullptr;
    1214             :         }
    1215             :     }
    1216           0 :     delete f;
    1217             : 
    1218           0 :     prefab *b = &(*prefabs.insert_or_assign(name, prefab()).first).second;
    1219           0 :     b->name = name ? name : "";
    1220           0 :     b->copy = copy;
    1221             : 
    1222           0 :     return b;
    1223             : }
    1224             : 
    1225             : class prefabmesh
    1226             : {
    1227             :     public:
    1228             :         struct vertex
    1229             :         {
    1230             :             vec pos;
    1231             :             vec4<uchar> norm;
    1232             :         };
    1233             : 
    1234             :         std::vector<vertex> verts;
    1235             :         std::vector<int> chain;
    1236             :         std::vector<ushort> tris;
    1237             : 
    1238           0 :         prefabmesh()
    1239           0 :         {
    1240           0 :             table.fill(-1);
    1241           0 :         }
    1242             : 
    1243           0 :         int addvert(const vec &pos, const bvec &norm)
    1244             :         {
    1245           0 :             vertex vtx;
    1246           0 :             vtx.pos = pos;
    1247           0 :             vtx.norm = norm;
    1248           0 :             return addvert(vtx);
    1249             :         }
    1250             : 
    1251           0 :         void setup(prefab &p)
    1252             :         {
    1253           0 :             if(tris.empty())
    1254             :             {
    1255           0 :                 return;
    1256             :             }
    1257           0 :             p.cleanup();
    1258             : 
    1259           0 :             for(uint i = 0; i < verts.size(); i++)
    1260             :             {
    1261           0 :                 verts[i].norm.flip();
    1262             :             }
    1263           0 :             if(!p.vbo)
    1264             :             {
    1265           0 :                 glGenBuffers(1, &p.vbo);
    1266             :             }
    1267           0 :             gle::bindvbo(p.vbo);
    1268           0 :             glBufferData(GL_ARRAY_BUFFER, verts.size()*sizeof(vertex), verts.data(), GL_STATIC_DRAW);
    1269           0 :             gle::clearvbo();
    1270           0 :             p.numverts = verts.size();
    1271             : 
    1272           0 :             if(!p.ebo)
    1273             :             {
    1274           0 :                 glGenBuffers(1, &p.ebo);
    1275             :             }
    1276           0 :             gle::bindebo(p.ebo);
    1277           0 :             glBufferData(GL_ELEMENT_ARRAY_BUFFER, tris.size()*sizeof(ushort), tris.data(), GL_STATIC_DRAW);
    1278           0 :             gle::clearebo();
    1279           0 :             p.numtris = tris.size()/3;
    1280             :         }
    1281             :     private:
    1282             :         static constexpr int prefabmeshsize = 1<<9;
    1283             :         std::array<int, prefabmeshsize> table;
    1284           0 :         int addvert(const vertex &v)
    1285             :         {
    1286             :             auto vechash = std::hash<vec>();
    1287           0 :             uint h = vechash(v.pos)&(prefabmeshsize-1);
    1288           0 :             for(int i = table[h]; i>=0; i = chain[i])
    1289             :             {
    1290           0 :                 const vertex &c = verts[i];
    1291           0 :                 if(c.pos==v.pos && c.norm==v.norm)
    1292             :                 {
    1293           0 :                     return i;
    1294             :                 }
    1295             :             }
    1296           0 :             if(verts.size() >= USHRT_MAX)
    1297             :             {
    1298           0 :                 return -1;
    1299             :             }
    1300           0 :             verts.emplace_back(v);
    1301           0 :             chain.emplace_back(table[h]);
    1302           0 :             return table[h] = verts.size()-1;
    1303             :         }
    1304             : 
    1305             : };
    1306             : 
    1307           0 : static void genprefabmesh(prefabmesh &r, const cube &c, const ivec &co, int size)
    1308             : {
    1309             :     //recursively apply to children
    1310           0 :     if(c.children)
    1311             :     {
    1312           0 :         neighborstack[++neighbordepth] = &(*c.children)[0];
    1313           0 :         for(int i = 0; i < 8; ++i)
    1314             :         {
    1315           0 :             ivec o(i, co, size/2);
    1316           0 :             genprefabmesh(r, (*c.children)[i], o, size/2);
    1317             :         }
    1318           0 :         --neighbordepth;
    1319             :     }
    1320           0 :     else if(!(c.isempty()))
    1321             :     {
    1322             :         int vis;
    1323           0 :         for(int i = 0; i < 6; ++i) //for each face
    1324             :         {
    1325           0 :             if((vis = visibletris(c, i, co, size)))
    1326             :             {
    1327           0 :                 std::array<ivec, 4> v;
    1328           0 :                 genfaceverts(c, i, v);
    1329           0 :                 int convex = 0;
    1330           0 :                 if(!flataxisface(c, i))
    1331             :                 {
    1332           0 :                     convex = faceconvexity(v);
    1333             :                 }
    1334           0 :                 int order = vis&4 || convex < 0 ? 1 : 0, numverts = 0;
    1335           0 :                 vec vo(co), pos[4], norm[4];
    1336           0 :                 pos[numverts++] = vec(v[order]).mul(size/8.0f).add(vo);
    1337           0 :                 if(vis&1)
    1338             :                 {
    1339           0 :                     pos[numverts++] = vec(v[order+1]).mul(size/8.0f).add(vo);
    1340             :                 }
    1341           0 :                 pos[numverts++] = vec(v[order+2]).mul(size/8.0f).add(vo);
    1342           0 :                 if(vis&2)
    1343             :                 {
    1344           0 :                     pos[numverts++] = vec(v[(order+3)&3]).mul(size/8.0f).add(vo);
    1345             :                 }
    1346           0 :                 guessnormals(pos, numverts, norm);
    1347             :                 int index[4];
    1348           0 :                 for(int j = 0; j < numverts; ++j)
    1349             :                 {
    1350           0 :                     index[j] = r.addvert(pos[j], bvec(norm[j]));
    1351             :                 }
    1352           0 :                 for(int j = 0; j < numverts-2; ++j)
    1353             :                 {
    1354           0 :                     if(index[0]!=index[j+1] && index[j+1]!=index[j+2] && index[j+2]!=index[0])
    1355             :                     {
    1356           0 :                         r.tris.emplace_back(index[0]);
    1357           0 :                         r.tris.emplace_back(index[j+1]);
    1358           0 :                         r.tris.emplace_back(index[j+2]);
    1359             :                     }
    1360             :                 }
    1361             :             }
    1362             :         }
    1363             :     }
    1364           0 : }
    1365             : 
    1366           0 : void cubeworld::genprefabmesh(prefab &p)
    1367             : {
    1368           0 :     block3 b = *p.copy;
    1369           0 :     b.o = ivec(0, 0, 0);
    1370             : 
    1371           0 :     std::array<cube, 8> *oldworldroot = worldroot;
    1372           0 :     int oldworldscale = worldscale;
    1373             : 
    1374           0 :     worldroot = newcubes();
    1375           0 :     worldscale = 1;
    1376           0 :     while(mapscale() < std::max(std::max(b.s.x, b.s.y), b.s.z)*b.grid)
    1377             :     {
    1378           0 :         worldscale++;
    1379             :     }
    1380             : 
    1381           0 :     cube *s = p.copy->c();
    1382           0 :     uint i = 0;
    1383           0 :     LOOP_XYZ(b, b.grid, if(!(s[i].isempty()) || s[i].children) pastecube(s[i], c); i++);
    1384             : 
    1385           0 :     prefabmesh r;
    1386           0 :     neighborstack[++neighbordepth] = &(*worldroot)[0];
    1387             :     //recursively apply to children
    1388           0 :     for(int i = 0; i < 8; ++i)
    1389             :     {
    1390           0 :         ::genprefabmesh(r, (*worldroot)[i], ivec(i, ivec(0, 0, 0), mapsize()/2), mapsize()/2);
    1391             :     }
    1392           0 :     --neighbordepth;
    1393           0 :     r.setup(p);
    1394             : 
    1395           0 :     freeocta(worldroot);
    1396             : 
    1397           0 :     worldroot = oldworldroot;
    1398           0 :     worldscale = oldworldscale;
    1399             : 
    1400           0 :     useshaderbyname("prefab");
    1401           0 : }
    1402             : 
    1403           0 : static void renderprefab(prefab &p, const vec &o, float yaw, float pitch, float roll, float size, const vec &color)
    1404             : {
    1405           0 :     if(!p.numtris)
    1406             :     {
    1407           0 :         rootworld.genprefabmesh(p);
    1408           0 :         if(!p.numtris)
    1409             :         {
    1410           0 :             return;
    1411             :         }
    1412             :     }
    1413             : 
    1414           0 :     block3 &b = *p.copy;
    1415             : 
    1416           0 :     matrix4 m;
    1417           0 :     m.identity();
    1418           0 :     m.settranslation(o);
    1419           0 :     if(yaw)
    1420             :     {
    1421           0 :         m.rotate_around_z(yaw/RAD);
    1422             :     }
    1423           0 :     if(pitch)
    1424             :     {
    1425           0 :         m.rotate_around_x(pitch/RAD);
    1426             :     }
    1427           0 :     if(roll)
    1428             :     {
    1429           0 :         m.rotate_around_y(-roll/RAD);
    1430             :     }
    1431           0 :     matrix3 w(m);
    1432           0 :     if(size > 0 && size != 1)
    1433             :     {
    1434           0 :         m.scale(size);
    1435             :     }
    1436           0 :     m.translate(vec(b.s).mul(-b.grid*0.5f));
    1437             : 
    1438           0 :     gle::bindvbo(p.vbo);
    1439           0 :     gle::bindebo(p.ebo);
    1440           0 :     gle::enablevertex();
    1441           0 :     gle::enablenormal();
    1442           0 :     prefabmesh::vertex *v = (prefabmesh::vertex *)0;
    1443           0 :     gle::vertexpointer(sizeof(prefabmesh::vertex), v->pos.v);
    1444           0 :     gle::normalpointer(sizeof(prefabmesh::vertex), v->norm.v, GL_BYTE);
    1445             : 
    1446           0 :     matrix4 pm;
    1447           0 :     pm.mul(camprojmatrix, m);
    1448           0 :     GLOBALPARAM(prefabmatrix, pm);
    1449           0 :     GLOBALPARAM(prefabworld, w);
    1450           0 :     SETSHADER(prefab);
    1451           0 :     gle::color(vec(color).mul(ldrscale));
    1452           0 :     glDrawRangeElements(GL_TRIANGLES, 0, p.numverts-1, p.numtris*3, GL_UNSIGNED_SHORT, (ushort *)0);
    1453             : 
    1454           0 :     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    1455           0 :     enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
    1456             : 
    1457           0 :     pm.mul(camprojmatrix, m);
    1458           0 :     GLOBALPARAM(prefabmatrix, pm);
    1459           0 :     SETSHADER(prefab);
    1460           0 :     gle::color((outlinecolor).tocolor().mul(ldrscale));
    1461           0 :     glDrawRangeElements(GL_TRIANGLES, 0, p.numverts-1, p.numtris*3, GL_UNSIGNED_SHORT, (ushort *)0);
    1462             : 
    1463           0 :     disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
    1464           0 :     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    1465             : 
    1466           0 :     gle::disablevertex();
    1467           0 :     gle::disablenormal();
    1468           0 :     gle::clearebo();
    1469           0 :     gle::clearvbo();
    1470             : }
    1471             : 
    1472           0 : void renderprefab(const char *name, const vec &o, float yaw, float pitch, float roll, float size, const vec &color)
    1473             : {
    1474           0 :     prefab *p = loadprefab(name, false);
    1475           0 :     if(p)
    1476             :     {
    1477           0 :         renderprefab(*p, o, yaw, pitch, roll, size, color);
    1478             :     }
    1479           0 : }
    1480             : 
    1481           0 : void previewprefab(const char *name, const vec &color)
    1482             : {
    1483           0 :     prefab *p = loadprefab(name, false);
    1484           0 :     if(p)
    1485             :     {
    1486           0 :         block3 &b = *p->copy;
    1487             :         float yaw;
    1488           0 :         vec o = calcmodelpreviewpos(vec(b.s).mul(b.grid*0.5f), yaw);
    1489           0 :         renderprefab(*p, o, yaw, 0, 0, 1, color);
    1490             :     }
    1491           0 : }
    1492             : 
    1493             : std::vector<int *> editingvslots;
    1494             : 
    1495           0 : void compacteditvslots()
    1496             : {
    1497           0 :     for(uint i = 0; i < editingvslots.size(); i++)
    1498             :     {
    1499           0 :         if(*editingvslots[i])
    1500             :         {
    1501           0 :             compactvslot(*editingvslots[i]);
    1502             :         }
    1503             :     }
    1504           0 :     for(uint i = 0; i < unpackingvslots.size(); i++)
    1505             :     {
    1506           0 :         compactvslot(*unpackingvslots[i].vslot);
    1507             :     }
    1508           0 :     for(uint i = 0; i < editinfos.size(); i++)
    1509             :     {
    1510           0 :         editinfo *e = editinfos[i];
    1511           0 :         compactvslots(e->copy->c(), e->copy->size());
    1512             :     }
    1513           0 :     for(undoblock *u : undos)
    1514             :     {
    1515           0 :         if(!u->numents)
    1516             :         {
    1517           0 :             compactvslots(u->block()->c(), u->block()->size());
    1518             :         }
    1519             :     }
    1520           0 :     for(undoblock *u : redos)
    1521             :     {
    1522           0 :         if(!u->numents)
    1523             :         {
    1524           0 :             compactvslots(u->block()->c(), u->block()->size());
    1525             :         }
    1526             :     }
    1527           0 : }
    1528             : 
    1529             : ///////////// height maps ////////////////
    1530             : 
    1531           0 : ushort getmaterial(cube &c)
    1532             : {
    1533           0 :     if(c.children)
    1534             :     {
    1535           0 :         ushort mat = getmaterial((*c.children)[7]);
    1536           0 :         for(int i = 0; i < 7; ++i)
    1537             :         {
    1538           0 :             if(mat != getmaterial((*c.children)[i]))
    1539             :             {
    1540           0 :                 return Mat_Air;
    1541             :             }
    1542             :         }
    1543           0 :         return mat;
    1544             :     }
    1545           0 :     return c.material;
    1546             : }
    1547             : 
    1548             : /////////// texture editing //////////////////
    1549             : 
    1550             : int curtexindex = -1;
    1551             : std::vector<ushort> texmru;
    1552             : 
    1553             : int reptex = -1;
    1554             : 
    1555           0 : static VSlot *remapvslot(int index, bool delta, const VSlot &ds)
    1556             : {
    1557           0 :     for(uint i = 0; i < remappedvslots.size(); i++)
    1558             :     {
    1559           0 :         if(remappedvslots[i].index == index)
    1560             :         {
    1561           0 :             return remappedvslots[i].vslot;
    1562             :         }
    1563             :     }
    1564           0 :     VSlot &vs = lookupvslot(index, false);
    1565           0 :     if(vs.index < 0 || vs.index == Default_Sky)
    1566             :     {
    1567           0 :         return nullptr;
    1568             :     }
    1569           0 :     VSlot *edit = nullptr;
    1570           0 :     if(delta)
    1571             :     {
    1572           0 :         VSlot ms;
    1573           0 :         mergevslot(ms, vs, ds);
    1574           0 :         edit = ms.changed ? editvslot(vs, ms) : vs.slot->variants;
    1575           0 :     }
    1576             :     else
    1577             :     {
    1578           0 :         edit = ds.changed ? editvslot(vs, ds) : vs.slot->variants;
    1579             :     }
    1580           0 :     if(!edit)
    1581             :     {
    1582           0 :         edit = &vs;
    1583             :     }
    1584           0 :     remappedvslots.emplace_back(vslotmap(vs.index, edit));
    1585           0 :     return edit;
    1586             : }
    1587             : 
    1588           0 : void remapvslots(cube &c, bool delta, const VSlot &ds, int orient, bool &findrep, VSlot *&findedit)
    1589             : {
    1590             :     //recursively apply to children
    1591           0 :     if(c.children)
    1592             :     {
    1593           0 :         for(int i = 0; i < 8; ++i)
    1594             :         {
    1595           0 :             remapvslots((*c.children)[i], delta, ds, orient, findrep, findedit);
    1596             :         }
    1597           0 :         return;
    1598             :     }
    1599           0 :     static VSlot ms;
    1600           0 :     if(orient<0)
    1601             :     {
    1602           0 :         for(int i = 0; i < 6; ++i) //for each face
    1603             :         {
    1604           0 :             VSlot *edit = remapvslot(c.texture[i], delta, ds);
    1605           0 :             if(edit)
    1606             :             {
    1607           0 :                 c.texture[i] = edit->index;
    1608           0 :                 if(!findedit)
    1609             :                 {
    1610           0 :                     findedit = edit;
    1611             :                 }
    1612             :             }
    1613             :         }
    1614             :     }
    1615             :     else
    1616             :     {
    1617           0 :         int i = visibleorient(c, orient);
    1618           0 :         VSlot *edit = remapvslot(c.texture[i], delta, ds);
    1619           0 :         if(edit)
    1620             :         {
    1621           0 :             if(findrep)
    1622             :             {
    1623           0 :                 if(reptex < 0)
    1624             :                 {
    1625           0 :                     reptex = c.texture[i];
    1626             :                 }
    1627           0 :                 else if(reptex != c.texture[i])
    1628             :                 {
    1629           0 :                     findrep = false;
    1630             :                 }
    1631             :             }
    1632           0 :             c.texture[i] = edit->index;
    1633           0 :             if(!findedit)
    1634             :             {
    1635           0 :                 findedit = edit;
    1636             :             }
    1637             :         }
    1638             :     }
    1639             : }
    1640             : 
    1641           0 : void compactmruvslots()
    1642             : {
    1643             :     static int lasttex = 0;
    1644           0 :     remappedvslots.clear();
    1645           0 :     for(int i = static_cast<int>(texmru.size()); --i >=0;) //note reverse iteration
    1646             :     {
    1647           0 :         if(vslots.size() > texmru[i])
    1648             :         {
    1649           0 :             VSlot &vs = *vslots[texmru[i]];
    1650           0 :             if(vs.index >= 0)
    1651             :             {
    1652           0 :                 texmru[i] = vs.index;
    1653           0 :                 continue;
    1654             :             }
    1655             :         }
    1656           0 :         if(curtexindex > i)
    1657             :         {
    1658           0 :             curtexindex--;
    1659             :         }
    1660           0 :         else if(curtexindex == i)
    1661             :         {
    1662           0 :             curtexindex = -1;
    1663             :         }
    1664           0 :         texmru.erase(texmru.begin() + i);
    1665             :     }
    1666           0 :     if(vslots.size() > static_cast<uint>(lasttex))
    1667             :     {
    1668           0 :         VSlot &vs = *vslots[lasttex];
    1669           0 :         lasttex = vs.index >= 0 ? vs.index : 0;
    1670             :     }
    1671             :     else
    1672             :     {
    1673           0 :         lasttex = 0;
    1674             :     }
    1675           0 :     reptex = (vslots.size() > static_cast<uint>(reptex)) ? vslots[reptex]->index : -1;
    1676           0 : }
    1677             : 
    1678           0 : void edittexcube(cube &c, int tex, int orient, bool &findrep)
    1679             : {
    1680           0 :     if(orient<0)
    1681             :     {
    1682           0 :         for(int i = 0; i < 6; ++i) //for each face
    1683             :         {
    1684           0 :             c.texture[i] = tex;
    1685             :         }
    1686             :     }
    1687             :     else
    1688             :     {
    1689           0 :         int i = visibleorient(c, orient);
    1690           0 :         if(findrep)
    1691             :         {
    1692           0 :             if(reptex < 0)
    1693             :             {
    1694           0 :                 reptex = c.texture[i];
    1695             :             }
    1696           0 :             else if(reptex != c.texture[i])
    1697             :             {
    1698           0 :                 findrep = false;
    1699             :             }
    1700             :         }
    1701           0 :         c.texture[i] = tex;
    1702             :     }
    1703             :     //recursively apply to children
    1704           0 :     if(c.children)
    1705             :     {
    1706           0 :         for(int i = 0; i < 8; ++i)
    1707             :         {
    1708           0 :             edittexcube((*c.children)[i], tex, orient, findrep);
    1709             :         }
    1710             :     }
    1711           0 : }
    1712             : 
    1713             : /*setmat: sets a cube's materials, given a material & filter to use
    1714             :  * Arguments:
    1715             :  *  c: the cube object to use
    1716             :  *  mat: material index to apply
    1717             :  *  matmask: material mask
    1718             :  *  filtermat: if nonzero, determines what existing mats to apply to
    1719             :  *  filtermask: filter material mask
    1720             :  *  filtergeom: type of geometry inside the cube (empty, solid, partially solid)
    1721             :  */
    1722           0 : void cube::setmat(ushort mat, ushort matmask, ushort filtermat, ushort filtermask, int filtergeom)
    1723             : {
    1724             :     //recursively sets material for all child nodes
    1725           0 :     if(children)
    1726             :     {
    1727           0 :         for(int i = 0; i < 8; ++i)
    1728             :         {
    1729           0 :             (*children)[i].setmat( mat, matmask, filtermat, filtermask, filtergeom);
    1730             :         }
    1731             :     }
    1732           0 :     else if((material&filtermask) == filtermat)
    1733             :     {
    1734           0 :         switch(filtergeom)
    1735             :         {
    1736           0 :             case EditMatFlag_Empty:
    1737             :             {
    1738           0 :                 if(isempty())
    1739             :                 {
    1740           0 :                     break;
    1741             :                 }
    1742           0 :                 return;
    1743             :             }
    1744           0 :             case EditMatFlag_NotEmpty:
    1745             :             {
    1746           0 :                 if(!(isempty()))
    1747             :                 {
    1748           0 :                     break;
    1749             :                 }
    1750           0 :                 return;
    1751             :             }
    1752           0 :             case EditMatFlag_Solid:
    1753             :             {
    1754           0 :                 if(issolid())
    1755             :                 {
    1756           0 :                     break;
    1757             :                 }
    1758           0 :                 return;
    1759             :             }
    1760           0 :             case EditMatFlag_NotSolid:
    1761             :             {
    1762           0 :                 if(!(issolid()))
    1763             :                 {
    1764           0 :                     break;
    1765             :                 }
    1766           0 :                 return;
    1767             :             }
    1768             :         }
    1769           0 :         if(mat!=Mat_Air)
    1770             :         {
    1771           0 :             material &= matmask;
    1772           0 :             material |= mat;
    1773             :         }
    1774             :         else
    1775             :         {
    1776           0 :             material = Mat_Air;
    1777             :         }
    1778             :     }
    1779             : }
    1780             : 
    1781           0 : void rendertexturepanel(int w, int h)
    1782             : {
    1783             :     static int texpaneltimer = 0;
    1784           0 :     if((texpaneltimer -= curtime)>0 && editmode)
    1785             :     {
    1786           0 :         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    1787             : 
    1788           0 :         pushhudmatrix();
    1789           0 :         hudmatrix.scale(h/1800.0f, h/1800.0f, 1);
    1790           0 :         flushhudmatrix(false);
    1791           0 :         SETSHADER(hudrgb);
    1792           0 :         int y = 50,
    1793           0 :         gap = 10;
    1794           0 :         gle::defvertex(2);
    1795           0 :         gle::deftexcoord0();
    1796             : 
    1797           0 :         for(int i = 0; i < 7; ++i)
    1798             :         {
    1799           0 :             int s = (i == 3 ? 285 : 220),
    1800           0 :                 ti = curtexindex+i-3;
    1801           0 :             if(static_cast<int>(texmru.size()) > ti)
    1802             :             {
    1803           0 :                 VSlot &vslot = lookupvslot(texmru[ti]);
    1804           0 :                 Slot &slot = *vslot.slot;
    1805           0 :                 Texture *tex = slot.sts.empty() ? notexture : slot.sts[0].t,
    1806           0 :                         *glowtex = nullptr;
    1807           0 :                 if(slot.texmask&(1 << Tex_Glow))
    1808             :                 {
    1809           0 :                     for(const Slot::Tex &t : slot.sts)
    1810             :                     {
    1811           0 :                         if(t.type == Tex_Glow)
    1812             :                         {
    1813           0 :                             glowtex = t.t;
    1814           0 :                             break;
    1815             :                         }
    1816             :                     }
    1817             :                 }
    1818           0 :                 float sx = std::min(1.0f, tex->xs/static_cast<float>(tex->ys)),
    1819           0 :                       sy = std::min(1.0f, tex->ys/static_cast<float>(tex->xs));
    1820           0 :                 int x = w*1800/h-s-50,
    1821           0 :                     r = s;
    1822           0 :                 vec2 tc[4] = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1) };
    1823           0 :                 float xoff = vslot.offset.x,
    1824           0 :                       yoff = vslot.offset.y;
    1825           0 :                 if(vslot.rotation)
    1826             :                 {
    1827           0 :                     const texrotation &r = texrotations[vslot.rotation];
    1828           0 :                     if(r.swapxy)
    1829             :                     {
    1830           0 :                         std::swap(xoff, yoff);
    1831           0 :                         for(int k = 0; k < 4; ++k)
    1832             :                         {
    1833           0 :                             std::swap(tc[k].x, tc[k].y);
    1834             :                         }
    1835             :                     }
    1836           0 :                     if(r.flipx)
    1837             :                     {
    1838           0 :                         xoff *= -1;
    1839           0 :                         for(int k = 0; k < 4; ++k)
    1840             :                         {
    1841           0 :                             tc[k].x *= -1;
    1842             :                         }
    1843             :                     }
    1844           0 :                     if(r.flipy)
    1845             :                     {
    1846           0 :                         yoff *= -1;
    1847           0 :                         for(int k = 0; k < 4; ++k)
    1848             :                         {
    1849           0 :                             tc[k].y *= -1;
    1850             :                         }
    1851             :                     }
    1852             :                 }
    1853           0 :                 for(int k = 0; k < 4; ++k)
    1854             :                 {
    1855           0 :                     tc[k].x = tc[k].x/sx - xoff/tex->xs;
    1856           0 :                     tc[k].y = tc[k].y/sy - yoff/tex->ys;
    1857             :                 }
    1858           0 :                 glBindTexture(GL_TEXTURE_2D, tex->id);
    1859           0 :                 for(int j = 0; j < (glowtex ? 3 : 2); ++j)
    1860             :                 {
    1861           0 :                     if(j < 2)
    1862             :                     {
    1863           0 :                         gle::color(vec(vslot.colorscale).mul(j), texpaneltimer/1000.0f);
    1864             :                     }
    1865             :                     else
    1866             :                     {
    1867           0 :                         glBindTexture(GL_TEXTURE_2D, glowtex->id);
    1868           0 :                         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    1869           0 :                         gle::color(vslot.glowcolor, texpaneltimer/1000.0f);
    1870             :                     }
    1871           0 :                     gle::begin(GL_TRIANGLE_STRIP);
    1872           0 :                     gle::attribf(x,   y);   gle::attrib(tc[0]);
    1873           0 :                     gle::attribf(x+r, y);   gle::attrib(tc[1]);
    1874           0 :                     gle::attribf(x,   y+r); gle::attrib(tc[3]);
    1875           0 :                     gle::attribf(x+r, y+r); gle::attrib(tc[2]);
    1876           0 :                     xtraverts += gle::end();
    1877           0 :                     if(!j)
    1878             :                     {
    1879           0 :                         r -= 10;
    1880           0 :                         x += 5;
    1881           0 :                         y += 5;
    1882             :                     }
    1883           0 :                     else if(j == 2)
    1884             :                     {
    1885           0 :                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    1886             :                     }
    1887             :                 }
    1888             :             }
    1889           0 :             y += s+gap;
    1890             :         }
    1891             : 
    1892           0 :         pophudmatrix(true, false);
    1893           0 :         resethudshader();
    1894             :     }
    1895           0 : }
    1896             : 
    1897           0 : static int bounded(int n)
    1898             : {
    1899           0 :     return n<0 ? 0 : (n>8 ? 8 : n);
    1900             : }
    1901             : 
    1902           0 : static void pushedge(uchar &edge, int dir, int dc)
    1903             : {
    1904           0 :     int ne = bounded(EDGE_GET(edge, dc)+dir);
    1905           0 :     EDGE_SET(edge, dc, ne);
    1906           0 :     int oe = EDGE_GET(edge, 1-dc);
    1907           0 :     if((dir<0 && dc && oe>ne) || (dir>0 && dc==0 && oe<ne))
    1908             :     {
    1909           0 :         EDGE_SET(edge, 1-dc, ne);
    1910             :     }
    1911           0 : }
    1912             : 
    1913             : //used in iengine
    1914           0 : void linkedpush(cube &c, int d, int x, int y, int dc, int dir)
    1915             : {
    1916           0 :     ivec v, p;
    1917           0 :     getcubevector(c, d, x, y, dc, v);
    1918             : 
    1919           0 :     for(int i = 0; i < 2; ++i)
    1920             :     {
    1921           0 :         for(int j = 0; j < 2; ++j)
    1922             :         {
    1923           0 :             getcubevector(c, d, i, j, dc, p);
    1924           0 :             if(v==p)
    1925             :             {
    1926           0 :                 pushedge(CUBE_EDGE(c, d, i, j), dir, dc);
    1927             :             }
    1928             :         }
    1929             :     }
    1930           0 : }
    1931             : 
    1932           1 : void initoctaeditcmds()
    1933             : {
    1934             :     //some of these commands use code only needed for the command itself, so
    1935             :     //they are declared as lambdas inside the local scope
    1936             : 
    1937             :     //others use functions in the global namespace, which are implemented elsewhere
    1938             :     //in this file
    1939             : 
    1940             :     //static to make sure that these lambdas have constant location in memory for identmap to look up
    1941           1 :     static auto movingcmd = [] (int *n)
    1942             :     {
    1943           1 :         if(*n >= 0)
    1944             :         {
    1945           0 :             if(!*n || (moving<=1 && !pointinsel(sel, vec(cur).add(1))))
    1946             :             {
    1947           0 :                 moving = 0;
    1948             :             }
    1949           0 :             else if(!moving)
    1950             :             {
    1951           0 :                 moving = 1;
    1952             :             }
    1953             :         }
    1954           1 :         intret(moving);
    1955           1 :     };
    1956             :     //unary + operator converts to function pointer
    1957           1 :     addcommand("moving",        reinterpret_cast<identfun>(+movingcmd), "b", Id_Command);
    1958             : 
    1959           1 :     addcommand("entcancel",     reinterpret_cast<identfun>(entcancel), "", Id_Command); ///
    1960           1 :     addcommand("cubecancel",    reinterpret_cast<identfun>(cubecancel), "", Id_Command); ///
    1961           1 :     addcommand("cancelsel",     reinterpret_cast<identfun>(cancelsel), "", Id_Command); ///
    1962           1 :     addcommand("reorient",      reinterpret_cast<identfun>(reorient), "", Id_Command); ///
    1963             : 
    1964           1 :     static auto selextend = [] ()
    1965             :     {
    1966           1 :         if(noedit(true))
    1967             :         {
    1968           1 :             return;
    1969             :         }
    1970           0 :         for(int i = 0; i < 3; ++i)
    1971             :         {
    1972           0 :             if(cur[i]<sel.o[i])
    1973             :             {
    1974           0 :                 sel.s[i] += (sel.o[i]-cur[i])/sel.grid;
    1975           0 :                 sel.o[i] = cur[i];
    1976             :             }
    1977           0 :             else if(cur[i]>=sel.o[i]+sel.s[i]*sel.grid)
    1978             :             {
    1979           0 :                 sel.s[i] = (cur[i]-sel.o[i])/sel.grid+1;
    1980             :             }
    1981             :         }
    1982             :     };
    1983           1 :     addcommand("selextend",     reinterpret_cast<identfun>(+selextend), "", Id_Command);
    1984             : 
    1985           1 :     static auto selmoved = [] ()
    1986             :     {
    1987           1 :         if(noedit(true))
    1988             :         {
    1989           1 :             return;
    1990             :         }
    1991           0 :         intret(sel.o != savedsel.o ? 1 : 0);
    1992             :     };
    1993             : 
    1994           1 :     static auto selsave = [] ()
    1995             :     {
    1996           1 :         if(noedit(true))
    1997             :         {
    1998           1 :             return;
    1999             :         }
    2000           0 :         savedsel = sel;
    2001             :     };
    2002             : 
    2003           1 :     static auto selrestore = [] ()
    2004             :     {
    2005           1 :         if(noedit(true))
    2006             :         {
    2007           1 :             return;
    2008             :         }
    2009           0 :         sel = savedsel;
    2010             :     };
    2011             : 
    2012           1 :     static auto selswap = [] ()
    2013             :     {
    2014           1 :         if(noedit(true))
    2015             :         {
    2016           1 :             return;
    2017             :         }
    2018           0 :         std::swap(sel, savedsel);
    2019             :     };
    2020             : 
    2021           1 :     addcommand("selmoved",      reinterpret_cast<identfun>(+selmoved), "", Id_Command);
    2022           1 :     addcommand("selsave",       reinterpret_cast<identfun>(+selsave), "", Id_Command);
    2023           1 :     addcommand("selrestore",    reinterpret_cast<identfun>(+selrestore), "", Id_Command);
    2024           1 :     addcommand("selswap",       reinterpret_cast<identfun>(+selswap), "", Id_Command);
    2025             : 
    2026           1 :     static auto haveselcmd = [] ()
    2027             :     {
    2028           1 :         intret(havesel ? selchildcount : 0);
    2029           1 :     };
    2030             : 
    2031           1 :     addcommand("havesel",       reinterpret_cast<identfun>(+haveselcmd), "", Id_Command);
    2032             : 
    2033             : 
    2034           1 :     static auto selchildcountcmd = [] ()
    2035             :     {
    2036           1 :         if(selchildcount < 0)
    2037             :         {
    2038           0 :             result(tempformatstring("1/%d", -selchildcount));
    2039             :         }
    2040             :         else
    2041             :         {
    2042           1 :             intret(selchildcount);
    2043             :         }
    2044           1 :     };
    2045           1 :     addcommand("selchildnum", reinterpret_cast<identfun>(+selchildcountcmd), "", Id_Command);
    2046             : 
    2047             : 
    2048           1 :     static auto selchildmatcmd = [] (char *prefix)
    2049             :     {
    2050           1 :         if(selchildmat > 0)
    2051             :         {
    2052           0 :             result(getmaterialdesc(selchildmat, prefix));
    2053             :         }
    2054           1 :     };
    2055           1 :     addcommand("selchildmat",   reinterpret_cast<identfun>(+selchildmatcmd), "s", Id_Command);
    2056             : 
    2057           1 :     static auto clearundos = [] ()
    2058             :     {
    2059           1 :         pruneundos(0);
    2060           1 :     };
    2061           1 :     addcommand("clearundos",    reinterpret_cast<identfun>(+clearundos), "", Id_Command); //run pruneundos but with a cache size of zero
    2062             : 
    2063           1 :     static auto delprefab = [] (char *name)
    2064             :     {
    2065           1 :         auto itr = prefabs.find(name);
    2066           1 :         if(itr != prefabs.end())
    2067             :         {
    2068           0 :             (*itr).second.cleanup();
    2069           0 :             prefabs.erase(name);
    2070           0 :             conoutf("deleted prefab %s", name);
    2071             :         }
    2072             :         else
    2073             :         {
    2074           1 :             conoutf("no such prefab %s", name);
    2075             :         }
    2076           1 :     };
    2077           1 :     addcommand("delprefab",     reinterpret_cast<identfun>(+delprefab), "s", Id_Command);
    2078             : 
    2079             :     /* saveprefab: saves the current selection to a prefab file
    2080             :      *
    2081             :      * Parameters:
    2082             :      *  char * name: a string containing the name of the prefab to save (sans file type)
    2083             :      * Returns:
    2084             :      *  void
    2085             :      * Effects:
    2086             :      * Using the global variables for selection information, writes the current selection
    2087             :      * to a prefab file with the given name. Does not save slot information, so pasting
    2088             :      * into a map with a different texture slot list will result in meaningless textures.
    2089             :      *
    2090             :      */
    2091           1 :     static auto saveprefab = [] (char *name)
    2092             :     {
    2093           1 :         if(!name[0] || noedit(true) || (nompedit && multiplayer))
    2094             :         {
    2095           1 :             multiplayerwarn();
    2096           1 :             return;
    2097             :         }
    2098           0 :         auto itr = prefabs.find(name);
    2099           0 :         prefab *b = nullptr;
    2100           0 :         if(itr == prefabs.end())
    2101             :         {
    2102           0 :             b = &(*prefabs.insert( { std::string(name), prefab() } ).first).second;
    2103           0 :             b->name = newstring(name);
    2104           0 :             b->name = name ? name : "";
    2105             :         }
    2106             :         else
    2107             :         {
    2108           0 :             b = &(*itr).second;
    2109             :         }
    2110           0 :         if(b->copy)
    2111             :         {
    2112           0 :             freeblock(b->copy);
    2113             :         }
    2114           0 :         PROTECT_SEL(b->copy = blockcopy(block3(sel), sel.grid));
    2115           0 :         rootworld.changed(sel);
    2116           0 :         DEF_FORMAT_STRING(filename, "media/prefab/%s.obr", name);
    2117           0 :         path(filename);
    2118           0 :         stream *f = opengzfile(filename, "wb");
    2119           0 :         if(!f)
    2120             :         {
    2121           0 :             conoutf(Console_Error, "could not write prefab to %s", filename);
    2122           0 :             return;
    2123             :         }
    2124             :         prefabheader hdr;
    2125           0 :         std::string headermagic = "OEBR";
    2126           0 :         std::copy(headermagic.begin(), headermagic.end(), hdr.magic);
    2127           0 :         hdr.version = 0;
    2128           0 :         f->write(&hdr, sizeof(hdr));
    2129           0 :         streambuf<uchar> s(f);
    2130           0 :         if(!packblock(*b->copy, s))
    2131             :         {
    2132           0 :             delete f;
    2133           0 :             conoutf(Console_Error, "could not pack prefab %s", filename);
    2134           0 :             return;
    2135             :         }
    2136           0 :         delete f;
    2137           0 :         conoutf("wrote prefab file %s", filename);
    2138           0 :     };
    2139           1 :     addcommand("saveprefab",    reinterpret_cast<identfun>(+saveprefab), "s", Id_Command);
    2140             : 
    2141           1 :     static auto pasteprefab = [] (char *name)
    2142             :     {
    2143           1 :         if(!name[0] || noedit() || (nompedit && multiplayer))
    2144             :         {
    2145           1 :             multiplayerwarn();
    2146           1 :             return;
    2147             :         }
    2148           0 :         prefab *b = loadprefab(name, true);
    2149           0 :         if(b)
    2150             :         {
    2151           0 :             pasteblock(*b->copy, sel, true);
    2152             :         }
    2153             :     };
    2154           1 :     addcommand("pasteprefab",   reinterpret_cast<identfun>(+pasteprefab), "s", Id_Command);
    2155             : 
    2156             :     //defines editing readonly variables, useful for the HUD
    2157             :     #define EDITSTAT(name, val) \
    2158             :         static auto name = [] () \
    2159             :         { \
    2160             :             static int laststat = 0; \
    2161             :             static int prevstat = 0; \
    2162             :             static int curstat = 0; \
    2163             :             if(totalmillis - laststat >= statrate) \
    2164             :             { \
    2165             :                 prevstat = curstat; \
    2166             :                 laststat = totalmillis - (totalmillis%statrate); \
    2167             :             } \
    2168             :             if(prevstat == curstat) curstat = (val); \
    2169             :             intret(curstat); \
    2170             :         }; \
    2171             :         addcommand(#name, reinterpret_cast<identfun>(+name), "", Id_Command);
    2172             : 
    2173           2 :     EDITSTAT(wtr, wtris);
    2174           2 :     EDITSTAT(vtr, (vtris*100)/std::max(wtris, 1));
    2175           2 :     EDITSTAT(wvt, wverts);
    2176           2 :     EDITSTAT(vvt, (vverts*100)/std::max(wverts, 1));
    2177           2 :     EDITSTAT(evt, xtraverts);
    2178           2 :     EDITSTAT(eva, xtravertsva);
    2179           2 :     EDITSTAT(octa, allocnodes*8);
    2180           2 :     EDITSTAT(va, allocva);
    2181           2 :     EDITSTAT(gldes, glde);
    2182           2 :     EDITSTAT(geombatch, gbatches);
    2183           2 :     EDITSTAT(oq, occlusionengine.getnumqueries());
    2184             : 
    2185             :     #undef EDITSTAT
    2186           1 : }

Generated by: LCOV version 1.14