2020-04-18 02:06:25 +00:00
|
|
|
#include "blend.hh"
|
|
|
|
|
2020-04-16 18:28:40 +00:00
|
|
|
#include "engine.hh"
|
2020-04-15 16:39:17 +00:00
|
|
|
|
|
|
|
extern int outline;
|
|
|
|
|
|
|
|
bool boxoutline = false;
|
|
|
|
|
|
|
|
void boxs(int orient, vec o, const vec &s, float size)
|
|
|
|
{
|
|
|
|
int d = dimension(orient), dc = dimcoord(orient);
|
|
|
|
float f = boxoutline ? (dc>0 ? 0.2f : -0.2f) : 0;
|
|
|
|
o[D[d]] += dc * s[D[d]] + f;
|
|
|
|
|
|
|
|
vec r(0, 0, 0), c(0, 0, 0);
|
|
|
|
r[R[d]] = s[R[d]];
|
|
|
|
c[C[d]] = s[C[d]];
|
|
|
|
|
|
|
|
vec v1 = o, v2 = vec(o).add(r), v3 = vec(o).add(r).add(c), v4 = vec(o).add(c);
|
|
|
|
|
|
|
|
r[R[d]] = 0.5f*size;
|
|
|
|
c[C[d]] = 0.5f*size;
|
|
|
|
|
|
|
|
gle::defvertex();
|
|
|
|
gle::begin(GL_TRIANGLE_STRIP);
|
|
|
|
gle::attrib(vec(v1).sub(r).sub(c));
|
|
|
|
gle::attrib(vec(v1).add(r).add(c));
|
|
|
|
gle::attrib(vec(v2).add(r).sub(c));
|
|
|
|
gle::attrib(vec(v2).sub(r).add(c));
|
|
|
|
gle::attrib(vec(v3).add(r).add(c));
|
|
|
|
gle::attrib(vec(v3).sub(r).sub(c));
|
|
|
|
gle::attrib(vec(v4).sub(r).add(c));
|
|
|
|
gle::attrib(vec(v4).add(r).sub(c));
|
|
|
|
gle::attrib(vec(v1).sub(r).sub(c));
|
|
|
|
gle::attrib(vec(v1).add(r).add(c));
|
|
|
|
xtraverts += gle::end();
|
|
|
|
}
|
|
|
|
|
|
|
|
void boxs(int orient, vec o, const vec &s)
|
|
|
|
{
|
|
|
|
int d = dimension(orient), dc = dimcoord(orient);
|
|
|
|
float f = boxoutline ? (dc>0 ? 0.2f : -0.2f) : 0;
|
|
|
|
o[D[d]] += dc * s[D[d]] + f;
|
|
|
|
|
|
|
|
gle::defvertex();
|
|
|
|
gle::begin(GL_LINE_LOOP);
|
|
|
|
|
|
|
|
gle::attrib(o); o[R[d]] += s[R[d]];
|
|
|
|
gle::attrib(o); o[C[d]] += s[C[d]];
|
|
|
|
gle::attrib(o); o[R[d]] -= s[R[d]];
|
|
|
|
gle::attrib(o);
|
|
|
|
|
|
|
|
xtraverts += gle::end();
|
|
|
|
}
|
|
|
|
|
|
|
|
void boxs3D(const vec &o, vec s, int g)
|
|
|
|
{
|
|
|
|
s.mul(g);
|
|
|
|
loopi(6)
|
|
|
|
boxs(i, o, s);
|
|
|
|
}
|
|
|
|
|
|
|
|
void boxsgrid(int orient, vec o, vec s, int g)
|
|
|
|
{
|
|
|
|
int d = dimension(orient), dc = dimcoord(orient);
|
|
|
|
float ox = o[R[d]],
|
|
|
|
oy = o[C[d]],
|
|
|
|
xs = s[R[d]],
|
|
|
|
ys = s[C[d]],
|
|
|
|
f = boxoutline ? (dc>0 ? 0.2f : -0.2f) : 0;
|
|
|
|
|
|
|
|
o[D[d]] += dc * s[D[d]]*g + f;
|
|
|
|
|
|
|
|
gle::defvertex();
|
|
|
|
gle::begin(GL_LINES);
|
|
|
|
loop(x, xs)
|
|
|
|
{
|
|
|
|
o[R[d]] += g;
|
|
|
|
gle::attrib(o);
|
|
|
|
o[C[d]] += ys*g;
|
|
|
|
gle::attrib(o);
|
|
|
|
o[C[d]] = oy;
|
|
|
|
}
|
|
|
|
loop(y, ys)
|
|
|
|
{
|
|
|
|
o[C[d]] += g;
|
|
|
|
o[R[d]] = ox;
|
|
|
|
gle::attrib(o);
|
|
|
|
o[R[d]] += xs*g;
|
|
|
|
gle::attrib(o);
|
|
|
|
}
|
|
|
|
xtraverts += gle::end();
|
|
|
|
}
|
|
|
|
|
|
|
|
selinfo sel, lastsel, savedsel;
|
|
|
|
|
|
|
|
int orient = 0;
|
|
|
|
int gridsize = 8;
|
|
|
|
ivec cor, lastcor;
|
|
|
|
ivec cur, lastcur;
|
|
|
|
|
|
|
|
extern int entediting;
|
|
|
|
bool editmode = false;
|
|
|
|
bool havesel = false;
|
|
|
|
bool hmapsel = false;
|
|
|
|
int horient = 0;
|
|
|
|
|
|
|
|
extern int entmoving;
|
|
|
|
|
|
|
|
VARF(dragging, 0, 0, 1,
|
|
|
|
if(!dragging || cor[0]<0) return;
|
|
|
|
lastcur = cur;
|
|
|
|
lastcor = cor;
|
|
|
|
sel.grid = gridsize;
|
|
|
|
sel.orient = orient;
|
|
|
|
);
|
|
|
|
|
|
|
|
int moving = 0;
|
|
|
|
ICOMMAND(moving, "b", (int *n),
|
|
|
|
{
|
|
|
|
if(*n >= 0)
|
|
|
|
{
|
|
|
|
if(!*n || (moving<=1 && !pointinsel(sel, vec(cur).add(1)))) moving = 0;
|
|
|
|
else if(!moving) moving = 1;
|
|
|
|
}
|
|
|
|
intret(moving);
|
|
|
|
});
|
|
|
|
|
|
|
|
VARF(gridpower, 0, 3, 12,
|
|
|
|
{
|
|
|
|
if(dragging) return;
|
|
|
|
gridsize = 1<<gridpower;
|
|
|
|
if(gridsize>=worldsize) gridsize = worldsize/2;
|
|
|
|
cancelsel();
|
|
|
|
});
|
|
|
|
|
|
|
|
VAR(passthroughsel, 0, 0, 1);
|
|
|
|
VAR(editing, 1, 0, 0);
|
|
|
|
VAR(selectcorners, 0, 0, 1);
|
|
|
|
VARF(hmapedit, 0, 0, 1, horient = sel.orient);
|
|
|
|
|
|
|
|
void forcenextundo() { lastsel.orient = -1; }
|
|
|
|
|
|
|
|
namespace hmap { void cancel(); }
|
|
|
|
|
|
|
|
void cubecancel()
|
|
|
|
{
|
|
|
|
havesel = false;
|
|
|
|
moving = dragging = hmapedit = passthroughsel = 0;
|
|
|
|
forcenextundo();
|
|
|
|
hmap::cancel();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cancelsel()
|
|
|
|
{
|
|
|
|
cubecancel();
|
|
|
|
entcancel();
|
|
|
|
}
|
|
|
|
|
|
|
|
void toggleedit(bool force)
|
|
|
|
{
|
|
|
|
if(!force)
|
|
|
|
{
|
|
|
|
if(!isconnected()) return;
|
|
|
|
if(player->state!=CS_ALIVE && player->state!=CS_DEAD && player->state!=CS_EDITING) return; // do not allow dead players to edit to avoid state confusion
|
|
|
|
if(!game::allowedittoggle()) return; // not in most multiplayer modes
|
|
|
|
}
|
|
|
|
if(!(editmode = !editmode))
|
|
|
|
{
|
|
|
|
player->state = player->editstate;
|
|
|
|
player->o.z -= player->eyeheight; // entinmap wants feet pos
|
|
|
|
entinmap(player); // find spawn closest to current floating pos
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game::resetgamestate();
|
|
|
|
player->editstate = player->state;
|
|
|
|
player->state = CS_EDITING;
|
|
|
|
}
|
|
|
|
cancelsel();
|
|
|
|
stoppaintblendmap();
|
|
|
|
keyrepeat(editmode, KR_EDITMODE);
|
|
|
|
editing = entediting = editmode;
|
|
|
|
if(!force) game::edittoggled(editmode);
|
|
|
|
execident("edittoggled");
|
|
|
|
}
|
|
|
|
|
|
|
|
VARP(editinview, 0, 1, 1);
|
|
|
|
|
|
|
|
bool noedit(bool view, bool msg)
|
|
|
|
{
|
|
|
|
if(!editmode) { if(msg) conoutf(CON_ERROR, "operation only allowed in edit mode"); return true; }
|
|
|
|
if(view || haveselent()) return false;
|
|
|
|
vec o(sel.o), s(sel.s);
|
|
|
|
s.mul(sel.grid / 2.0f);
|
|
|
|
o.add(s);
|
|
|
|
float r = max(s.x, s.y, s.z);
|
|
|
|
bool viewable = (isvisiblesphere(r, o) != VFC_NOT_VISIBLE);
|
|
|
|
if(viewable || !editinview) return false;
|
|
|
|
if(msg) conoutf(CON_ERROR, "selection not in view");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void reorient()
|
|
|
|
{
|
|
|
|
sel.cx = 0;
|
|
|
|
sel.cy = 0;
|
|
|
|
sel.cxs = sel.s[R[dimension(orient)]]*2;
|
|
|
|
sel.cys = sel.s[C[dimension(orient)]]*2;
|
|
|
|
sel.orient = orient;
|
|
|
|
}
|
|
|
|
|
|
|
|
void selextend()
|
|
|
|
{
|
|
|
|
if(noedit(true)) return;
|
|
|
|
loopi(3)
|
|
|
|
{
|
|
|
|
if(cur[i]<sel.o[i])
|
|
|
|
{
|
|
|
|
sel.s[i] += (sel.o[i]-cur[i])/sel.grid;
|
|
|
|
sel.o[i] = cur[i];
|
|
|
|
}
|
|
|
|
else if(cur[i]>=sel.o[i]+sel.s[i]*sel.grid)
|
|
|
|
{
|
|
|
|
sel.s[i] = (cur[i]-sel.o[i])/sel.grid+1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ICOMMAND(edittoggle, "", (), toggleedit(false));
|
|
|
|
COMMAND(entcancel, "");
|
|
|
|
COMMAND(cubecancel, "");
|
|
|
|
COMMAND(cancelsel, "");
|
|
|
|
COMMAND(reorient, "");
|
|
|
|
COMMAND(selextend, "");
|
|
|
|
|
|
|
|
ICOMMAND(selmoved, "", (), { if(noedit(true)) return; intret(sel.o != savedsel.o ? 1 : 0); });
|
|
|
|
ICOMMAND(selsave, "", (), { if(noedit(true)) return; savedsel = sel; });
|
|
|
|
ICOMMAND(selrestore, "", (), { if(noedit(true)) return; sel = savedsel; });
|
|
|
|
ICOMMAND(selswap, "", (), { if(noedit(true)) return; swap(sel, savedsel); });
|
|
|
|
|
|
|
|
ICOMMAND(getselpos, "", (),
|
|
|
|
{
|
|
|
|
if(noedit(true)) return;
|
|
|
|
defformatstring(pos, "%s %s %s", floatstr(sel.o.x), floatstr(sel.o.y), floatstr(sel.o.z));
|
|
|
|
result(pos);
|
|
|
|
});
|
|
|
|
|
|
|
|
void setselpos(int *x, int *y, int *z)
|
|
|
|
{
|
|
|
|
if(noedit(moving!=0)) return;
|
|
|
|
havesel = true;
|
|
|
|
sel.o = ivec(*x, *y, *z).mask(~(gridsize-1));
|
|
|
|
}
|
|
|
|
COMMAND(setselpos, "iii");
|
|
|
|
|
|
|
|
void movesel(int *dir, int *dim)
|
|
|
|
{
|
|
|
|
if(noedit(moving!=0)) return;
|
|
|
|
if(*dim < 0 || *dim > 2) return;
|
|
|
|
sel.o[*dim] += *dir * sel.grid;
|
|
|
|
}
|
|
|
|
COMMAND(movesel, "ii");
|
|
|
|
|
|
|
|
///////// selection support /////////////
|
|
|
|
|
|
|
|
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
|
|
|
|
{
|
|
|
|
int dim = dimension(b.orient), dc = dimcoord(b.orient);
|
|
|
|
ivec s(dim, x*b.grid, y*b.grid, dc*(b.s[dim]-1)*b.grid);
|
|
|
|
s.add(b.o);
|
|
|
|
if(dc) s[dim] -= z*b.grid; else s[dim] += z*b.grid;
|
|
|
|
return lookupcube(s, rgrid);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define loopxy(b) loop(y,(b).s[C[dimension((b).orient)]]) loop(x,(b).s[R[dimension((b).orient)]])
|
|
|
|
#define loopxyz(b, r, f) { loop(z,(b).s[D[dimension((b).orient)]]) loopxy((b)) { cube &c = blockcube(x,y,z,b,r); f; } }
|
|
|
|
#define loopselxyz(f) { if(local) makeundo(); loopxyz(sel, sel.grid, f); changed(sel); }
|
|
|
|
#define selcube(x, y, z) blockcube(x, y, z, sel, sel.grid)
|
|
|
|
|
|
|
|
////////////// cursor ///////////////
|
|
|
|
|
|
|
|
int selchildcount = 0, selchildmat = -1;
|
|
|
|
|
|
|
|
ICOMMAND(havesel, "", (), intret(havesel ? selchildcount : 0));
|
|
|
|
ICOMMAND(selchildcount, "", (), { if(selchildcount < 0) result(tempformatstring("1/%d", -selchildcount)); else intret(selchildcount); });
|
|
|
|
ICOMMAND(selchildmat, "s", (char *prefix), { if(selchildmat > 0) result(getmaterialdesc(selchildmat, prefix)); });
|
|
|
|
|
|
|
|
void countselchild(cube *c, const ivec &cor, int size)
|
|
|
|
{
|
|
|
|
ivec ss = ivec(sel.s).mul(sel.grid);
|
|
|
|
loopoctaboxsize(cor, size, sel.o, ss)
|
|
|
|
{
|
|
|
|
ivec o(i, cor, size);
|
|
|
|
if(c[i].children) countselchild(c[i].children, o, size/2);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
selchildcount++;
|
|
|
|
if(c[i].material != MAT_AIR && selchildmat != MAT_AIR)
|
|
|
|
{
|
|
|
|
if(selchildmat < 0) selchildmat = c[i].material;
|
|
|
|
else if(selchildmat != c[i].material) selchildmat = MAT_AIR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void normalizelookupcube(const ivec &o)
|
|
|
|
{
|
|
|
|
if(lusize>gridsize)
|
|
|
|
{
|
|
|
|
lu.x += (o.x-lu.x)/gridsize*gridsize;
|
|
|
|
lu.y += (o.y-lu.y)/gridsize*gridsize;
|
|
|
|
lu.z += (o.z-lu.z)/gridsize*gridsize;
|
|
|
|
}
|
|
|
|
else if(gridsize>lusize)
|
|
|
|
{
|
|
|
|
lu.x &= ~(gridsize-1);
|
|
|
|
lu.y &= ~(gridsize-1);
|
|
|
|
lu.z &= ~(gridsize-1);
|
|
|
|
}
|
|
|
|
lusize = gridsize;
|
|
|
|
}
|
|
|
|
|
|
|
|
void updateselection()
|
|
|
|
{
|
|
|
|
sel.o.x = min(lastcur.x, cur.x);
|
|
|
|
sel.o.y = min(lastcur.y, cur.y);
|
|
|
|
sel.o.z = min(lastcur.z, cur.z);
|
|
|
|
sel.s.x = abs(lastcur.x-cur.x)/sel.grid+1;
|
|
|
|
sel.s.y = abs(lastcur.y-cur.y)/sel.grid+1;
|
|
|
|
sel.s.z = abs(lastcur.z-cur.z)/sel.grid+1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool editmoveplane(const vec &o, const vec &ray, int d, float off, vec &handle, vec &dest, bool first)
|
|
|
|
{
|
|
|
|
plane pl(d, off);
|
|
|
|
float dist = 0.0f;
|
|
|
|
if(!pl.rayintersect(player->o, ray, dist))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
dest = vec(ray).mul(dist).add(player->o);
|
|
|
|
if(first) handle = vec(dest).sub(o);
|
|
|
|
dest.sub(handle);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace hmap { inline bool isheightmap(int orient, int d, bool empty, cube *c); }
|
|
|
|
extern void entdrag(const vec &ray);
|
|
|
|
extern bool hoveringonent(int ent, int orient);
|
|
|
|
extern void renderentselection(const vec &o, const vec &ray, bool entmoving);
|
|
|
|
extern float rayent(const vec &o, const vec &ray, float radius, int mode, int size, int &orient, int &ent);
|
|
|
|
|
|
|
|
VAR(gridlookup, 0, 0, 1);
|
|
|
|
VAR(passthroughcube, 0, 1, 1);
|
|
|
|
|
|
|
|
void rendereditcursor()
|
|
|
|
{
|
|
|
|
int d = dimension(sel.orient),
|
|
|
|
od = dimension(orient),
|
|
|
|
odc = dimcoord(orient);
|
|
|
|
|
|
|
|
bool hidecursor = UI::hascursor() || blendpaintmode, hovering = false;
|
|
|
|
hmapsel = false;
|
|
|
|
|
|
|
|
if(moving)
|
|
|
|
{
|
|
|
|
static vec dest, handle;
|
|
|
|
if(editmoveplane(vec(sel.o), camdir, od, sel.o[D[od]]+odc*sel.grid*sel.s[D[od]], handle, dest, moving==1))
|
|
|
|
{
|
|
|
|
if(moving==1)
|
|
|
|
{
|
|
|
|
dest.add(handle);
|
|
|
|
handle = vec(ivec(handle).mask(~(sel.grid-1)));
|
|
|
|
dest.sub(handle);
|
|
|
|
moving = 2;
|
|
|
|
}
|
|
|
|
ivec o = ivec(dest).mask(~(sel.grid-1));
|
|
|
|
sel.o[R[od]] = o[R[od]];
|
|
|
|
sel.o[C[od]] = o[C[od]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(entmoving)
|
|
|
|
{
|
|
|
|
entdrag(camdir);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ivec w;
|
|
|
|
float sdist = 0, wdist = 0, t;
|
|
|
|
int entorient = 0, ent = -1;
|
|
|
|
|
|
|
|
wdist = rayent(player->o, camdir, 1e16f,
|
|
|
|
(editmode && showmat ? RAY_EDITMAT : 0) // select cubes first
|
|
|
|
| (!dragging && entediting ? RAY_ENTS : 0)
|
|
|
|
| RAY_SKIPFIRST
|
|
|
|
| (passthroughcube==1 ? RAY_PASS : 0), gridsize, entorient, ent);
|
|
|
|
|
|
|
|
if((havesel || dragging) && !passthroughsel && !hmapedit) // now try selecting the selection
|
|
|
|
if(rayboxintersect(vec(sel.o), vec(sel.s).mul(sel.grid), player->o, camdir, sdist, orient))
|
|
|
|
{ // and choose the nearest of the two
|
|
|
|
if(sdist < wdist)
|
|
|
|
{
|
|
|
|
wdist = sdist;
|
|
|
|
ent = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if((hovering = hoveringonent(hidecursor ? -1 : ent, entorient)))
|
|
|
|
{
|
|
|
|
if(!havesel)
|
|
|
|
{
|
|
|
|
selchildcount = 0;
|
|
|
|
selchildmat = -1;
|
|
|
|
sel.s = ivec(0, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vec w = vec(camdir).mul(wdist+0.05f).add(player->o);
|
|
|
|
if(!insideworld(w))
|
|
|
|
{
|
|
|
|
loopi(3) wdist = min(wdist, ((camdir[i] > 0 ? worldsize : 0) - player->o[i]) / camdir[i]);
|
|
|
|
w = vec(camdir).mul(wdist-0.05f).add(player->o);
|
|
|
|
if(!insideworld(w))
|
|
|
|
{
|
|
|
|
wdist = 0;
|
|
|
|
loopi(3) w[i] = clamp(player->o[i], 0.0f, float(worldsize));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cube *c = &lookupcube(ivec(w));
|
|
|
|
if(gridlookup && !dragging && !moving && !havesel && hmapedit!=1) gridsize = lusize;
|
|
|
|
int mag = lusize / gridsize;
|
|
|
|
normalizelookupcube(ivec(w));
|
|
|
|
if(sdist == 0 || sdist > wdist) rayboxintersect(vec(lu), vec(gridsize), player->o, camdir, t=0, orient); // just getting orient
|
|
|
|
cur = lu;
|
|
|
|
cor = ivec(vec(w).mul(2).div(gridsize));
|
|
|
|
od = dimension(orient);
|
|
|
|
d = dimension(sel.orient);
|
|
|
|
|
|
|
|
if(hmapedit==1 && dimcoord(horient) == (camdir[dimension(horient)]<0))
|
|
|
|
{
|
|
|
|
hmapsel = hmap::isheightmap(horient, dimension(horient), false, c);
|
|
|
|
if(hmapsel)
|
|
|
|
od = dimension(orient = horient);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(dragging)
|
|
|
|
{
|
|
|
|
updateselection();
|
|
|
|
sel.cx = min(cor[R[d]], lastcor[R[d]]);
|
|
|
|
sel.cy = min(cor[C[d]], lastcor[C[d]]);
|
|
|
|
sel.cxs = max(cor[R[d]], lastcor[R[d]]);
|
|
|
|
sel.cys = max(cor[C[d]], lastcor[C[d]]);
|
|
|
|
|
|
|
|
if(!selectcorners)
|
|
|
|
{
|
|
|
|
sel.cx &= ~1;
|
|
|
|
sel.cy &= ~1;
|
|
|
|
sel.cxs &= ~1;
|
|
|
|
sel.cys &= ~1;
|
|
|
|
sel.cxs -= sel.cx-2;
|
|
|
|
sel.cys -= sel.cy-2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sel.cxs -= sel.cx-1;
|
|
|
|
sel.cys -= sel.cy-1;
|
|
|
|
}
|
|
|
|
|
|
|
|
sel.cx &= 1;
|
|
|
|
sel.cy &= 1;
|
|
|
|
havesel = true;
|
|
|
|
}
|
|
|
|
else if(!havesel)
|
|
|
|
{
|
|
|
|
sel.o = lu;
|
|
|
|
sel.s.x = sel.s.y = sel.s.z = 1;
|
|
|
|
sel.cx = sel.cy = 0;
|
|
|
|
sel.cxs = sel.cys = 2;
|
|
|
|
sel.grid = gridsize;
|
|
|
|
sel.orient = orient;
|
|
|
|
d = od;
|
|
|
|
}
|
|
|
|
|
|
|
|
sel.corner = (cor[R[d]]-(lu[R[d]]*2)/gridsize)+(cor[C[d]]-(lu[C[d]]*2)/gridsize)*2;
|
|
|
|
selchildcount = 0;
|
|
|
|
selchildmat = -1;
|
|
|
|
countselchild(worldroot, ivec(0, 0, 0), worldsize/2);
|
|
|
|
if(mag>=1 && selchildcount==1)
|
|
|
|
{
|
|
|
|
selchildmat = c->material;
|
|
|
|
if(mag>1) selchildcount = -mag;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
|
|
|
|
|
|
|
// cursors
|
|
|
|
|
|
|
|
ldrnotextureshader->set();
|
|
|
|
|
|
|
|
renderentselection(player->o, camdir, entmoving!=0);
|
|
|
|
|
|
|
|
boxoutline = outline!=0;
|
|
|
|
|
|
|
|
enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
|
|
|
|
|
|
|
|
if(!moving && !hovering && !hidecursor)
|
|
|
|
{
|
|
|
|
if(hmapedit==1)
|
|
|
|
gle::colorub(0, hmapsel ? 255 : 40, 0);
|
|
|
|
else
|
|
|
|
gle::colorub(120,120,120);
|
|
|
|
boxs(orient, vec(lu), vec(lusize));
|
|
|
|
}
|
|
|
|
|
|
|
|
// selections
|
|
|
|
if(havesel || moving)
|
|
|
|
{
|
|
|
|
d = dimension(sel.orient);
|
|
|
|
gle::colorub(50,50,50); // grid
|
|
|
|
boxsgrid(sel.orient, vec(sel.o), vec(sel.s), sel.grid);
|
|
|
|
gle::colorub(200,0,0); // 0 reference
|
|
|
|
boxs3D(vec(sel.o).sub(0.5f*min(gridsize*0.25f, 2.0f)), vec(min(gridsize*0.25f, 2.0f)), 1);
|
|
|
|
gle::colorub(200,200,200);// 2D selection box
|
|
|
|
vec co(sel.o.v), cs(sel.s.v);
|
|
|
|
co[R[d]] += 0.5f*(sel.cx*gridsize);
|
|
|
|
co[C[d]] += 0.5f*(sel.cy*gridsize);
|
|
|
|
cs[R[d]] = 0.5f*(sel.cxs*gridsize);
|
|
|
|
cs[C[d]] = 0.5f*(sel.cys*gridsize);
|
|
|
|
cs[D[d]] *= gridsize;
|
|
|
|
boxs(sel.orient, co, cs);
|
|
|
|
if(hmapedit==1) // 3D selection box
|
|
|
|
gle::colorub(0,120,0);
|
|
|
|
else
|
|
|
|
gle::colorub(0,0,120);
|
|
|
|
boxs3D(vec(sel.o), vec(sel.s), sel.grid);
|
|
|
|
}
|
|
|
|
|
|
|
|
disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
|
|
|
|
|
|
|
|
boxoutline = false;
|
|
|
|
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tryedit()
|
|
|
|
{
|
|
|
|
extern int hidehud;
|
|
|
|
if(!editmode || hidehud || mainmenu) return;
|
|
|
|
if(blendpaintmode) trypaintblendmap();
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////// ready changes to vertex arrays ////////////
|
|
|
|
|
|
|
|
static bool haschanged = false;
|
|
|
|
|
|
|
|
void readychanges(const ivec &bbmin, const ivec &bbmax, cube *c, const ivec &cor, int size)
|
|
|
|
{
|
|
|
|
loopoctabox(cor, size, bbmin, bbmax)
|
|
|
|
{
|
|
|
|
ivec o(i, cor, size);
|
|
|
|
if(c[i].ext)
|
|
|
|
{
|
|
|
|
if(c[i].ext->va) // removes va s so that octarender will recreate
|
|
|
|
{
|
|
|
|
int hasmerges = c[i].ext->va->hasmerges;
|
|
|
|
destroyva(c[i].ext->va);
|
|
|
|
c[i].ext->va = NULL;
|
|
|
|
if(hasmerges) invalidatemerges(c[i], o, size, true);
|
|
|
|
}
|
|
|
|
freeoctaentities(c[i]);
|
|
|
|
c[i].ext->tjoints = -1;
|
|
|
|
}
|
|
|
|
if(c[i].children)
|
|
|
|
{
|
|
|
|
if(size<=1)
|
|
|
|
{
|
|
|
|
solidfaces(c[i]);
|
|
|
|
discardchildren(c[i], true);
|
|
|
|
brightencube(c[i]);
|
|
|
|
}
|
|
|
|
else readychanges(bbmin, bbmax, c[i].children, o, size/2);
|
|
|
|
}
|
|
|
|
else brightencube(c[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void commitchanges(bool force)
|
|
|
|
{
|
|
|
|
if(!force && !haschanged) return;
|
|
|
|
haschanged = false;
|
|
|
|
|
|
|
|
int oldlen = valist.length();
|
|
|
|
resetclipplanes();
|
|
|
|
entitiesinoctanodes();
|
|
|
|
inbetweenframes = false;
|
|
|
|
octarender();
|
|
|
|
inbetweenframes = true;
|
|
|
|
setupmaterials(oldlen);
|
|
|
|
clearshadowcache();
|
|
|
|
updatevabbs();
|
|
|
|
}
|
|
|
|
|
|
|
|
void changed(const ivec &bbmin, const ivec &bbmax, bool commit)
|
|
|
|
{
|
|
|
|
readychanges(bbmin, bbmax, worldroot, ivec(0, 0, 0), worldsize/2);
|
|
|
|
haschanged = true;
|
|
|
|
|
|
|
|
if(commit) commitchanges();
|
|
|
|
}
|
|
|
|
|
|
|
|
void changed(const block3 &sel, bool commit)
|
|
|
|
{
|
|
|
|
if(sel.s.iszero()) return;
|
|
|
|
readychanges(ivec(sel.o).sub(1), ivec(sel.s).mul(sel.grid).add(sel.o).add(1), worldroot, ivec(0, 0, 0), worldsize/2);
|
|
|
|
haschanged = true;
|
|
|
|
|
|
|
|
if(commit) commitchanges();
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////// copy and undo /////////////
|
|
|
|
static inline void copycube(const cube &src, cube &dst)
|
|
|
|
{
|
|
|
|
dst = src;
|
|
|
|
dst.visible = 0;
|
|
|
|
dst.merged = 0;
|
|
|
|
dst.ext = NULL; // src cube is responsible for va destruction
|
|
|
|
if(src.children)
|
|
|
|
{
|
|
|
|
dst.children = newcubes(F_EMPTY);
|
|
|
|
loopi(8) copycube(src.children[i], dst.children[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void pastecube(const cube &src, cube &dst)
|
|
|
|
{
|
|
|
|
discardchildren(dst);
|
|
|
|
copycube(src, dst);
|
|
|
|
}
|
|
|
|
|
|
|
|
void blockcopy(const block3 &s, int rgrid, block3 *b)
|
|
|
|
{
|
|
|
|
*b = s;
|
|
|
|
cube *q = b->c();
|
|
|
|
loopxyz(s, rgrid, copycube(c, *q++));
|
|
|
|
}
|
|
|
|
|
|
|
|
block3 *blockcopy(const block3 &s, int rgrid)
|
|
|
|
{
|
|
|
|
int bsize = sizeof(block3)+sizeof(cube)*s.size();
|
|
|
|
if(bsize <= 0 || bsize > (100<<20)) return NULL;
|
|
|
|
block3 *b = (block3 *)new (false) uchar[bsize];
|
|
|
|
if(b) blockcopy(s, rgrid, b);
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
void freeblock(block3 *b, bool alloced = true)
|
|
|
|
{
|
|
|
|
cube *q = b->c();
|
|
|
|
loopi(b->size()) discardchildren(*q++);
|
|
|
|
if(alloced) delete[] b;
|
|
|
|
}
|
|
|
|
|
|
|
|
void selgridmap(const selinfo &sel, uchar *g) // generates a map of the cube sizes at each grid point
|
|
|
|
{
|
|
|
|
loopxyz(sel, -sel.grid, (*g++ = bitscan(lusize), (void)c));
|
|
|
|
}
|
|
|
|
|
|
|
|
void freeundo(undoblock *u)
|
|
|
|
{
|
|
|
|
if(!u->numents) freeblock(u->block(), false);
|
|
|
|
delete[] (uchar *)u;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pasteundoblock(block3 *b, uchar *g)
|
|
|
|
{
|
|
|
|
cube *s = b->c();
|
|
|
|
loopxyz(*b, 1<<min(int(*g++), worldscale-1), pastecube(*s++, c));
|
|
|
|
}
|
|
|
|
|
|
|
|
void pasteundo(undoblock *u)
|
|
|
|
{
|
|
|
|
if(u->numents) pasteundoents(u);
|
|
|
|
else pasteundoblock(u->block(), u->gridmap());
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int undosize(undoblock *u)
|
|
|
|
{
|
|
|
|
if(u->numents) return u->numents*sizeof(undoent);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
block3 *b = u->block();
|
|
|
|
cube *q = b->c();
|
|
|
|
int size = b->size(), total = size;
|
|
|
|
loopj(size) total += familysize(*q++)*sizeof(cube);
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct undolist
|
|
|
|
{
|
|
|
|
undoblock *first, *last;
|
|
|
|
|
|
|
|
undolist() : first(NULL), last(NULL) {}
|
|
|
|
|
|
|
|
bool empty() { return !first; }
|
|
|
|
|
|
|
|
void add(undoblock *u)
|
|
|
|
{
|
|
|
|
u->next = NULL;
|
|
|
|
u->prev = last;
|
|
|
|
if(!first) first = last = u;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
last->next = u;
|
|
|
|
last = u;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
undoblock *popfirst()
|
|
|
|
{
|
|
|
|
undoblock *u = first;
|
|
|
|
first = first->next;
|
|
|
|
if(first) first->prev = NULL;
|
|
|
|
else last = NULL;
|
|
|
|
return u;
|
|
|
|
}
|
|
|
|
|
|
|
|
undoblock *poplast()
|
|
|
|
{
|
|
|
|
undoblock *u = last;
|
|
|
|
last = last->prev;
|
|
|
|
if(last) last->next = NULL;
|
|
|
|
else first = NULL;
|
|
|
|
return u;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
undolist undos, redos;
|
|
|
|
VARP(undomegs, 0, 5, 100); // bounded by n megs
|
|
|
|
int totalundos = 0;
|
|
|
|
|
|
|
|
void pruneundos(int maxremain) // bound memory
|
|
|
|
{
|
|
|
|
while(totalundos > maxremain && !undos.empty())
|
|
|
|
{
|
|
|
|
undoblock *u = undos.popfirst();
|
|
|
|
totalundos -= u->size;
|
|
|
|
freeundo(u);
|
|
|
|
}
|
|
|
|
//conoutf(CON_DEBUG, "undo: %d of %d(%%%d)", totalundos, undomegs<<20, totalundos*100/(undomegs<<20));
|
|
|
|
while(!redos.empty())
|
|
|
|
{
|
|
|
|
undoblock *u = redos.popfirst();
|
|
|
|
totalundos -= u->size;
|
|
|
|
freeundo(u);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void clearundos() { pruneundos(0); }
|
|
|
|
|
|
|
|
COMMAND(clearundos, "");
|
|
|
|
|
|
|
|
undoblock *newundocube(const selinfo &s)
|
|
|
|
{
|
|
|
|
int ssize = s.size(),
|
|
|
|
selgridsize = ssize,
|
|
|
|
blocksize = sizeof(block3)+ssize*sizeof(cube);
|
|
|
|
if(blocksize <= 0 || blocksize > (undomegs<<20)) return NULL;
|
|
|
|
undoblock *u = (undoblock *)new (false) uchar[sizeof(undoblock) + blocksize + selgridsize];
|
|
|
|
if(!u) return NULL;
|
|
|
|
u->numents = 0;
|
|
|
|
block3 *b = u->block();
|
|
|
|
blockcopy(s, -s.grid, b);
|
|
|
|
uchar *g = u->gridmap();
|
|
|
|
selgridmap(s, g);
|
|
|
|
return u;
|
|
|
|
}
|
|
|
|
|
|
|
|
void addundo(undoblock *u)
|
|
|
|
{
|
|
|
|
u->size = undosize(u);
|
|
|
|
u->timestamp = totalmillis;
|
|
|
|
undos.add(u);
|
|
|
|
totalundos += u->size;
|
|
|
|
pruneundos(undomegs<<20);
|
|
|
|
}
|
|
|
|
|
|
|
|
VARP(nompedit, 0, 1, 1);
|
|
|
|
|
|
|
|
void makeundo(selinfo &s)
|
|
|
|
{
|
|
|
|
undoblock *u = newundocube(s);
|
|
|
|
if(u) addundo(u);
|
|
|
|
}
|
|
|
|
|
|
|
|
void makeundo() // stores state of selected cubes before editing
|
|
|
|
{
|
|
|
|
if(lastsel==sel || sel.s.iszero()) return;
|
|
|
|
lastsel=sel;
|
|
|
|
makeundo(sel);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int countblock(cube *c, int n = 8)
|
|
|
|
{
|
|
|
|
int r = 0;
|
|
|
|
loopi(n) if(c[i].children) r += countblock(c[i].children); else ++r;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int countblock(block3 *b) { return countblock(b->c(), b->size()); }
|
|
|
|
|
|
|
|
void swapundo(undolist &a, undolist &b, int op)
|
|
|
|
{
|
|
|
|
if(noedit()) return;
|
|
|
|
if(a.empty()) { conoutf(CON_WARN, "nothing more to %s", op == EDIT_REDO ? "redo" : "undo"); return; }
|
|
|
|
int ts = a.last->timestamp;
|
|
|
|
if(multiplayer(false))
|
|
|
|
{
|
|
|
|
int n = 0, ops = 0;
|
|
|
|
for(undoblock *u = a.last; u && ts==u->timestamp; u = u->prev)
|
|
|
|
{
|
|
|
|
++ops;
|
|
|
|
n += u->numents ? u->numents : countblock(u->block());
|
|
|
|
if(ops > 10 || n > 2500)
|
|
|
|
{
|
|
|
|
conoutf(CON_WARN, "undo too big for multiplayer");
|
|
|
|
if(nompedit) { multiplayer(); return; }
|
|
|
|
op = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
selinfo l = sel;
|
|
|
|
while(!a.empty() && ts==a.last->timestamp)
|
|
|
|
{
|
|
|
|
if(op >= 0) game::edittrigger(sel, op);
|
|
|
|
undoblock *u = a.poplast(), *r;
|
|
|
|
if(u->numents) r = copyundoents(u);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
block3 *ub = u->block();
|
|
|
|
l.o = ub->o;
|
|
|
|
l.s = ub->s;
|
|
|
|
l.grid = ub->grid;
|
|
|
|
l.orient = ub->orient;
|
|
|
|
r = newundocube(l);
|
|
|
|
}
|
|
|
|
if(r)
|
|
|
|
{
|
|
|
|
r->size = u->size;
|
|
|
|
r->timestamp = totalmillis;
|
|
|
|
b.add(r);
|
|
|
|
}
|
|
|
|
pasteundo(u);
|
|
|
|
if(!u->numents) changed(*u->block(), false);
|
|
|
|
freeundo(u);
|
|
|
|
}
|
|
|
|
commitchanges();
|
|
|
|
if(!hmapsel)
|
|
|
|
{
|
|
|
|
sel = l;
|
|
|
|
reorient();
|
|
|
|
}
|
|
|
|
forcenextundo();
|
|
|
|
}
|
|
|
|
|
|
|
|
void editundo() { swapundo(undos, redos, EDIT_UNDO); }
|
|
|
|
void editredo() { swapundo(redos, undos, EDIT_REDO); }
|
|
|
|
|
|
|
|
// guard against subdivision
|
|
|
|
#define protectsel(f) { undoblock *_u = newundocube(sel); f; if(_u) { pasteundo(_u); freeundo(_u); } }
|
|
|
|
|
|
|
|
vector<editinfo *> editinfos;
|
|
|
|
editinfo *localedit = NULL;
|
|
|
|
|
|
|
|
template<class B>
|
|
|
|
static void packcube(cube &c, B &buf)
|
|
|
|
{
|
|
|
|
if(c.children)
|
|
|
|
{
|
|
|
|
buf.put(0xFF);
|
|
|
|
loopi(8) packcube(c.children[i], buf);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cube data = c;
|
|
|
|
lilswap(data.texture, 6);
|
|
|
|
buf.put(c.material&0xFF);
|
|
|
|
buf.put(c.material>>8);
|
|
|
|
buf.put(data.edges, sizeof(data.edges));
|
|
|
|
buf.put((uchar *)data.texture, sizeof(data.texture));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<class B>
|
|
|
|
static bool packblock(block3 &b, B &buf)
|
|
|
|
{
|
|
|
|
if(b.size() <= 0 || b.size() > (1<<20)) return false;
|
|
|
|
block3 hdr = b;
|
|
|
|
lilswap(hdr.o.v, 3);
|
|
|
|
lilswap(hdr.s.v, 3);
|
|
|
|
lilswap(&hdr.grid, 1);
|
|
|
|
lilswap(&hdr.orient, 1);
|
|
|
|
buf.put((const uchar *)&hdr, sizeof(hdr));
|
|
|
|
cube *c = b.c();
|
|
|
|
loopi(b.size()) packcube(c[i], buf);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct vslothdr
|
|
|
|
{
|
|
|
|
ushort index;
|
|
|
|
ushort slot;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void packvslots(cube &c, vector<uchar> &buf, vector<ushort> &used)
|
|
|
|
{
|
|
|
|
if(c.children)
|
|
|
|
{
|
|
|
|
loopi(8) packvslots(c.children[i], buf, used);
|
|
|
|
}
|
|
|
|
else loopi(6)
|
|
|
|
{
|
|
|
|
ushort index = c.texture[i];
|
|
|
|
if(vslots.inrange(index) && vslots[index]->changed && used.find(index) < 0)
|
|
|
|
{
|
|
|
|
used.add(index);
|
|
|
|
VSlot &vs = *vslots[index];
|
|
|
|
vslothdr &hdr = *(vslothdr *)buf.pad(sizeof(vslothdr));
|
|
|
|
hdr.index = index;
|
|
|
|
hdr.slot = vs.slot->index;
|
|
|
|
lilswap(&hdr.index, 2);
|
|
|
|
packvslot(buf, vs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void packvslots(block3 &b, vector<uchar> &buf)
|
|
|
|
{
|
|
|
|
vector<ushort> used;
|
|
|
|
cube *c = b.c();
|
|
|
|
loopi(b.size()) packvslots(c[i], buf, used);
|
|
|
|
memset(buf.pad(sizeof(vslothdr)), 0, sizeof(vslothdr));
|
|
|
|
}
|
|
|
|
|
|
|
|
template<class B>
|
|
|
|
static void unpackcube(cube &c, B &buf)
|
|
|
|
{
|
|
|
|
int mat = buf.get();
|
|
|
|
if(mat == 0xFF)
|
|
|
|
{
|
|
|
|
c.children = newcubes(F_EMPTY);
|
|
|
|
loopi(8) unpackcube(c.children[i], buf);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
c.material = mat | (buf.get()<<8);
|
|
|
|
buf.get(c.edges, sizeof(c.edges));
|
|
|
|
buf.get((uchar *)c.texture, sizeof(c.texture));
|
|
|
|
lilswap(c.texture, 6);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<class B>
|
|
|
|
static bool unpackblock(block3 *&b, B &buf)
|
|
|
|
{
|
|
|
|
if(b) { freeblock(b); b = NULL; }
|
|
|
|
block3 hdr;
|
|
|
|
if(buf.get((uchar *)&hdr, sizeof(hdr)) < int(sizeof(hdr))) return false;
|
|
|
|
lilswap(hdr.o.v, 3);
|
|
|
|
lilswap(hdr.s.v, 3);
|
|
|
|
lilswap(&hdr.grid, 1);
|
|
|
|
lilswap(&hdr.orient, 1);
|
|
|
|
if(hdr.size() > (1<<20) || hdr.grid <= 0 || hdr.grid > (1<<12)) return false;
|
|
|
|
b = (block3 *)new (false) uchar[sizeof(block3)+hdr.size()*sizeof(cube)];
|
|
|
|
if(!b) return false;
|
|
|
|
*b = hdr;
|
|
|
|
cube *c = b->c();
|
|
|
|
memset(c, 0, b->size()*sizeof(cube));
|
|
|
|
loopi(b->size()) unpackcube(c[i], buf);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct vslotmap
|
|
|
|
{
|
|
|
|
int index;
|
|
|
|
VSlot *vslot;
|
|
|
|
|
|
|
|
vslotmap() {}
|
|
|
|
vslotmap(int index, VSlot *vslot) : index(index), vslot(vslot) {}
|
|
|
|
};
|
|
|
|
static vector<vslotmap> unpackingvslots;
|
|
|
|
|
|
|
|
static void unpackvslots(cube &c, ucharbuf &buf)
|
|
|
|
{
|
|
|
|
if(c.children)
|
|
|
|
{
|
|
|
|
loopi(8) unpackvslots(c.children[i], buf);
|
|
|
|
}
|
|
|
|
else loopi(6)
|
|
|
|
{
|
|
|
|
ushort tex = c.texture[i];
|
|
|
|
loopvj(unpackingvslots) if(unpackingvslots[j].index == tex) { c.texture[i] = unpackingvslots[j].vslot->index; break; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void unpackvslots(block3 &b, ucharbuf &buf)
|
|
|
|
{
|
|
|
|
while(buf.remaining() >= int(sizeof(vslothdr)))
|
|
|
|
{
|
|
|
|
vslothdr &hdr = *(vslothdr *)buf.pad(sizeof(vslothdr));
|
|
|
|
lilswap(&hdr.index, 2);
|
|
|
|
if(!hdr.index) break;
|
|
|
|
VSlot &vs = *lookupslot(hdr.slot, false).variants;
|
|
|
|
VSlot ds;
|
|
|
|
if(!unpackvslot(buf, ds, false)) break;
|
|
|
|
if(vs.index < 0 || vs.index == DEFAULT_SKY) continue;
|
|
|
|
VSlot *edit = editvslot(vs, ds);
|
|
|
|
unpackingvslots.add(vslotmap(hdr.index, edit ? edit : &vs));
|
|
|
|
}
|
|
|
|
|
|
|
|
cube *c = b.c();
|
|
|
|
loopi(b.size()) unpackvslots(c[i], buf);
|
|
|
|
|
|
|
|
unpackingvslots.setsize(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool compresseditinfo(const uchar *inbuf, int inlen, uchar *&outbuf, int &outlen)
|
|
|
|
{
|
|
|
|
uLongf len = compressBound(inlen);
|
|
|
|
if(len > (1<<20)) return false;
|
|
|
|
outbuf = new (false) uchar[len];
|
|
|
|
if(!outbuf || compress2((Bytef *)outbuf, &len, (const Bytef *)inbuf, inlen, Z_BEST_COMPRESSION) != Z_OK || len > (1<<16))
|
|
|
|
{
|
|
|
|
delete[] outbuf;
|
|
|
|
outbuf = NULL;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
outlen = len;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool uncompresseditinfo(const uchar *inbuf, int inlen, uchar *&outbuf, int &outlen)
|
|
|
|
{
|
|
|
|
if(compressBound(outlen) > (1<<20)) return false;
|
|
|
|
uLongf len = outlen;
|
|
|
|
outbuf = new (false) uchar[len];
|
|
|
|
if(!outbuf || uncompress((Bytef *)outbuf, &len, (const Bytef *)inbuf, inlen) != Z_OK)
|
|
|
|
{
|
|
|
|
delete[] outbuf;
|
|
|
|
outbuf = NULL;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
outlen = len;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool packeditinfo(editinfo *e, int &inlen, uchar *&outbuf, int &outlen)
|
|
|
|
{
|
|
|
|
vector<uchar> buf;
|
|
|
|
|