editbind X [ editflip ]
editbind C [ editcopy ]
editbind V [ editpaste ]
-editbind Z [ undo; passthroughsel 0]
-editbind U [ undo; passthroughsel 0]
-editbind I [ redo ]
editbind O [ domodifier 15 ] // vSlot: offset H
editbind P [ domodifier 16 ] // vSlot: offset V
}
]
-lazyshader 0 "prefab" [
- attribute vec4 vvertex, vcolor;
- attribute vec3 vnormal;
- uniform mat4 prefabmatrix;
- uniform mat3 prefabworld;
- varying vec4 color;
-
- void main(void)
- {
- gl_Position = prefabmatrix * vvertex;
- color = vcolor;
- color.rgb *= dot(prefabworld * vnormal, vec3(0.0, -0.447213595, 0.894427191));
- }
-] [
- varying vec4 color;
- void main(void)
- {
- gl_FragColor = color;
- }
-]
-
lazyshader 0 "moviergb" [
attribute vec4 vvertex;
@(screentexcoord 0)
}
return layout(size+SHADOW, size+SHADOW);
}
- int prefabpreview(const char *prefab, const vec &color, float sizescale, const char *overlaid, bool throttle) {
- autotab();
- if(sizescale==0) sizescale = 1;
- int size = (int)(sizescale*2*FONTH)-SHADOW;
- if(prefab[0] && visible() && (!throttle || throttlepreview(prefabloaded(prefab)))) {
- bool hit = ishit(size+SHADOW, size+SHADOW);
- float xs = size, ys = size, xi = curx, yi = cury;
- if(overlaid && hit && actionon) {
- hudnotextureshader->set();
- gle::colorf(0, 0, 0, 0.75f);
- rect_(xi+SHADOW, yi+SHADOW, xs, ys);
- hudshader->set();
- }
- int x1 = int(floor(screenw*(xi*scale.x+origin.x))), y1 = int(floor(screenh*(1 - ((yi+ys)*scale.y+origin.y)))),
- x2 = int(ceil(screenw*((xi+xs)*scale.x+origin.x))), y2 = int(ceil(screenh*(1 - (yi*scale.y+origin.y))));
- glDisable(GL_BLEND);
- modelpreview::start(x1, y1, x2-x1, y2-y1, overlaid!=NULL);
- previewprefab(prefab, color);
- modelpreview::end();
- hudshader->set();
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_BLEND);
- if(overlaid) {
- if(hit) {
- hudnotextureshader->set();
- glBlendFunc(GL_ZERO, GL_SRC_COLOR);
- gle::colorf(1, 0.5f, 0.5f);
- rect_(xi, yi, xs, ys);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- hudshader->set();
- }
- if(overlaid[0]) text_(overlaid, xi + FONTH/2, yi + FONTH/2, hit ? 0xFF0000 : 0xFFFFFF, hit, hit);
- if(!overlaytex) overlaytex = textureload("data/guioverlay.png", 3);
- gle::color(light);
- glBindTexture(GL_TEXTURE_2D, overlaytex->id);
- rect_(xi, yi, xs, ys, 0);
- }
- }
- return layout(size+SHADOW, size+SHADOW);
- }
void slider(int &val, int vmin, int vmax, int color, const char *label) {
autotab();
int x = curx;
// ents
extern char *entname(entity &e);
extern bool haveselent();
-extern undoblock *copyundoents(undoblock *u);
-extern void pasteundoent(int idx, const entity &ue);
-extern void pasteundoents(undoblock *u);
// octaedit
extern void cancelsel();
extern void rendertexturepanel(int w, int h);
-extern void addundo(undoblock *u);
extern void commitchanges(bool force = false);
extern void rendereditcursor();
extern void tryedit();
-extern bool prefabloaded(const char *name);
-extern void renderprefab(const char *name, const vec &o, float yaw, float pitch, float roll, float size = 1, const vec &color = vec(1, 1, 1));
-extern void previewprefab(const char *name, const vec &color);
-
// octarender
extern vector<tjoint> tjoints;
extern vector<vtxarray *> varoot, valist;
extern void cleanupparticles();
extern void cleanupdecals();
extern void cleanupmodels();
- extern void cleanupprefabs();
extern void cleanuplightmaps();
extern void cleanshadowmap();
cleanupva();
cleanupparticles();
cleanupdecals();
cleanupmodels();
- cleanupprefabs();
cleanuptextures();
cleanuplightmaps();
cleanshadowmap();
}
COMMAND(guimodelpreview, "sssfisi");
-void guiprefabpreview(char *prefab, int *color, char *action, float *scale, int *overlaid, char *title, int *throttle) {
- if(!cgui) return;
- int ret = cgui->prefabpreview(prefab, vec::hexcolor(*color), *scale, *overlaid!=0 ? title : NULL, *throttle!=0);
- if(ret&G3D_UP) {
- if(*action) {
- updatelater.add().schedule(action);
- if(shouldclearmenu) clearlater = true;
- }
- }
- else if(ret&G3D_ROLLOVER) {
- alias("guirolloverpreviewname", prefab);
- alias("guirolloverpreviewaction", action);
- }
-}
-COMMAND(guiprefabpreview, "sisfisi");
-
struct change {
int type;
const char *desc;
editinfo() : copy(NULL) {}
};
-struct undoent { int i; entity e; };
-struct undoblock { // undo header, all data sits in payload {
- undoblock *prev, *next;
- int size, timestamp, numents; // if numents is 0, is a cube undo record, otherwise an entity undo record
- block3 *block() { return (block3 *)(this + 1); }
- uchar *gridmap() {
- block3 *ub = block();
- return (uchar *)(ub->c() + ub->size());
- }
- undoent *ents() { return (undoent *)(this + 1); }
-};
-
extern cube *worldroot; // the world data. only a ptr to 8 cubes (ie: like cube.children above)
extern int wtris, wverts, vtris, vverts, glde, gbatches;
extern int allocnodes, allocva, selchildcount, selchildmat;
VAR(editing, 1, 0, 0);
VAR(selectcorners, 0, 0, 1);
-void forcenextundo() { lastsel.orient = -1; }
-
void cubecancel() {
havesel = false;
moving = dragging = passthroughsel = 0;
- forcenextundo();
}
void cancelsel() {
#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 loopselxyz(f) { loopxyz(sel, sel.grid, f); changed(sel); }
#define selcube(x, y, z) blockcube(x, y, z, sel, sel.grid)
////////////// cursor ///////////////
if(commit) commitchanges();
}
-//////////// copy and undo /////////////
static inline void copycube(const cube &src, cube &dst) {
dst = src;
dst.visible = 0;
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, 8, 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(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;
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();
- 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;
e = NULL;
}
-bool packundo(undoblock *u, int &inlen, uchar *&outbuf, int &outlen) {
- vector<uchar> buf;
- buf.reserve(512);
- *(ushort *)buf.pad(2) = lilswap(ushort(u->numents));
- if(u->numents) {
- undoent *ue = u->ents();
- loopi(u->numents) {
- *(ushort *)buf.pad(2) = lilswap(ushort(ue[i].i));
- entity &e = *(entity *)buf.pad(sizeof(entity));
- e = ue[i].e;
- lilswap(&e.o.x, 3);
- lilswap(&e.attr1, 5);
- }
- }
- else {
- block3 &b = *u->block();
- if(!packblock(b, buf)) return false;
- buf.put(u->gridmap(), b.size());
- packvslots(b, buf);
- }
- inlen = buf.length();
- return compresseditinfo(buf.getbuf(), buf.length(), outbuf, outlen);
-}
-
-bool unpackundo(const uchar *inbuf, int inlen, int outlen) {
- uchar *outbuf = NULL;
- if(!uncompresseditinfo(inbuf, inlen, outbuf, outlen)) return false;
- ucharbuf buf(outbuf, outlen);
- if(buf.remaining() < 2) {
- delete[] outbuf;
- return false;
- }
- int numents = lilswap(*(const ushort *)buf.pad(2));
- if(numents) {
- if(buf.remaining() < numents*int(2 + sizeof(entity))) {
- delete[] outbuf;
- return false;
- }
- loopi(numents) {
- int idx = lilswap(*(const ushort *)buf.pad(2));
- entity &e = *(entity *)buf.pad(sizeof(entity));
- lilswap(&e.o.x, 3);
- lilswap(&e.attr1, 5);
- pasteundoent(idx, e);
- }
- }
- else {
- block3 *b = NULL;
- if(!unpackblock(b, buf) || b->grid >= worldsize || buf.remaining() < b->size()) {
- freeblock(b);
- delete[] outbuf;
- return false;
- }
- uchar *g = buf.pad(b->size());
- unpackvslots(*b, buf);
- pasteundoblock(b, g);
- changed(*b, false);
- freeblock(b);
- }
- delete[] outbuf;
- commitchanges();
- return true;
-}
-
-bool packundo(int op, int &inlen, uchar *&outbuf, int &outlen) {
- switch(op) {
- case EDIT_UNDO: return !undos.empty() && packundo(undos.last, inlen, outbuf, outlen);
- case EDIT_REDO: return !redos.empty() && packundo(redos.last, inlen, outbuf, outlen);
- default: return false;
- }
-}
-
-struct prefabheader {
- char magic[4];
- int version;
-};
-
-struct prefab : editinfo {
- char *name;
- GLuint ebo, vbo;
- int numtris, numverts;
- prefab() : name(NULL), ebo(0), vbo(0), numtris(0), numverts(0) {}
- ~prefab() { DELETEA(name); if(copy) freeblock(copy); }
- void cleanup() {
- if(ebo) { glDeleteBuffers_(1, &ebo); ebo = 0; }
- if(vbo) { glDeleteBuffers_(1, &vbo); vbo = 0; }
- numtris = numverts = 0;
- }
-};
-
-static hashnameset<prefab> prefabs;
-
-void cleanupprefabs() {
- enumerate(prefabs, prefab, p, p.cleanup());
-}
-
-void delprefab(char *name) {
- prefab *p = prefabs.access(name);
- if(p) {
- p->cleanup();
- prefabs.remove(name);
- conoutf("deleted prefab %s", name);
- }
-}
-COMMAND(delprefab, "s");
-
-void saveprefab(char *name) {
- if(!name[0] || noedit(true) || (nompedit && multiplayer())) return;
- prefab *b = prefabs.access(name);
- if(!b) {
- b = &prefabs[name];
- b->name = newstring(name);
- }
- if(b->copy) freeblock(b->copy);
- protectsel(b->copy = blockcopy(block3(sel), sel.grid));
- changed(sel);
- defformatstring(filename, strpbrk(name, "/\\") ? "packages/%s.obr" : "packages/prefab/%s.obr", name);
- path(filename);
- stream *f = opengzfile(filename, "wb");
- if(!f) { conoutf(CON_ERROR, "could not write prefab to %s", filename); return; }
- prefabheader hdr;
- memcpy(hdr.magic, "OEBR", 4);
- hdr.version = 0;
- lilswap(&hdr.version, 1);
- f->write(&hdr, sizeof(hdr));
- streambuf<uchar> s(f);
- if(!packblock(*b->copy, s)) { delete f; conoutf(CON_ERROR, "could not pack prefab %s", filename); return; }
- delete f;
- conoutf("wrote prefab file %s", filename);
-}
-COMMAND(saveprefab, "s");
-
void pasteblock(block3 &b, selinfo &sel, bool local) {
sel.s = b.s;
int o = sel.orient;
sel.orient = o;
}
-bool prefabloaded(const char *name) {
- return prefabs.access(name) != NULL;
-}
-
-prefab *loadprefab(const char *name, bool msg = true) {
- prefab *b = prefabs.access(name);
- if(b) return b;
-
- defformatstring(filename, strpbrk(name, "/\\") ? "packages/%s.obr" : "packages/prefab/%s.obr", name);
- path(filename);
- stream *f = opengzfile(filename, "rb");
- if(!f) { if(msg) conoutf(CON_ERROR, "could not read prefab %s", filename); return NULL; }
- prefabheader hdr;
- if(f->read(&hdr, sizeof(hdr)) != sizeof(prefabheader) || memcmp(hdr.magic, "OEBR", 4)) { delete f; if(msg) conoutf(CON_ERROR, "prefab %s has malformatted header", filename); return NULL; }
- lilswap(&hdr.version, 1);
- if(hdr.version != 0) { delete f; if(msg) conoutf(CON_ERROR, "prefab %s uses unsupported version", filename); return NULL; }
- streambuf<uchar> s(f);
- block3 *copy = NULL;
- if(!unpackblock(copy, s)) { delete f; if(msg) conoutf(CON_ERROR, "could not unpack prefab %s", filename); return NULL; }
- delete f;
-
- b = &prefabs[name];
- b->name = newstring(name);
- b->copy = copy;
-
- return b;
-}
-
-void pasteprefab(char *name) {
- if(!name[0] || noedit() || (nompedit && multiplayer())) return;
- prefab *b = loadprefab(name, true);
- if(b) pasteblock(*b->copy, sel, true);
-}
-COMMAND(pasteprefab, "s");
-
-struct prefabmesh {
- struct vertex { vec pos; bvec4 norm; };
- static const int SIZE = 1<<9;
- int table[SIZE];
- vector<vertex> verts;
- vector<int> chain;
- vector<ushort> tris;
- prefabmesh() { memset(table, -1, sizeof(table)); }
- int addvert(const vertex &v) {
- uint h = hthash(v.pos)&(SIZE-1);
- for(int i = table[h]; i>=0; i = chain[i]) {
- const vertex &c = verts[i];
- if(c.pos==v.pos && c.norm==v.norm) return i;
- }
- if(verts.length() >= USHRT_MAX) return -1;
- verts.add(v);
- chain.add(table[h]);
- return table[h] = verts.length()-1;
- }
- int addvert(const vec &pos, const bvec &norm) {
- vertex vtx;
- vtx.pos = pos;
- vtx.norm = norm;
- return addvert(vtx);
- }
- void setup(prefab &p) {
- if(tris.empty()) return;
- p.cleanup();
- loopv(verts) verts[i].norm.flip();
- if(!p.vbo) glGenBuffers_(1, &p.vbo);
- gle::bindvbo(p.vbo);
- glBufferData_(GL_ARRAY_BUFFER, verts.length()*sizeof(vertex), verts.getbuf(), GL_STATIC_DRAW);
- gle::clearvbo();
- p.numverts = verts.length();
- if(!p.ebo) glGenBuffers_(1, &p.ebo);
- gle::bindebo(p.ebo);
- glBufferData_(GL_ELEMENT_ARRAY_BUFFER, tris.length()*sizeof(ushort), tris.getbuf(), GL_STATIC_DRAW);
- gle::clearebo();
- p.numtris = tris.length()/3;
- }
-
-};
-
-static void genprefabmesh(prefabmesh &r, cube &c, const ivec &co, int size) {
- if(c.children) {
- neighbourstack[++neighbourdepth] = c.children;
- loopi(8) {
- ivec o(i, co, size/2);
- genprefabmesh(r, c.children[i], o, size/2);
- }
- --neighbourdepth;
- }
- else if(!isempty(c)) {
- int vis;
- loopi(6) if((vis = visibletris(c, i, co, size))) {
- ivec v[4];
- genfaceverts(c, i, v);
- int convex = 0;
- if(!flataxisface(c, i)) convex = faceconvexity(v);
- int order = vis&4 || convex < 0 ? 1 : 0, numverts = 0;
- vec vo(co), pos[4], norm[4];
- pos[numverts++] = vec(v[order]).mul(size/8.0f).add(vo);
- if(vis&1) pos[numverts++] = vec(v[order+1]).mul(size/8.0f).add(vo);
- pos[numverts++] = vec(v[order+2]).mul(size/8.0f).add(vo);
- if(vis&2) pos[numverts++] = vec(v[(order+3)&3]).mul(size/8.0f).add(vo);
- guessnormals(pos, numverts, norm);
- int index[4];
- loopj(numverts) index[j] = r.addvert(pos[j], bvec(norm[j]));
- loopj(numverts-2) if(index[0]!=index[j+1] && index[j+1]!=index[j+2] && index[j+2]!=index[0]) {
- r.tris.add(index[0]);
- r.tris.add(index[j+1]);
- r.tris.add(index[j+2]);
- }
- }
- }
-}
-
-void genprefabmesh(prefab &p) {
- block3 b = *p.copy;
- b.o = ivec(0, 0, 0);
- cube *oldworldroot = worldroot;
- int oldworldscale = worldscale, oldworldsize = worldsize;
- worldroot = newcubes();
- worldscale = 1;
- worldsize = 2;
- while(worldsize < max(max(b.s.x, b.s.y), b.s.z)*b.grid) {
- worldscale++;
- worldsize *= 2;
- }
- cube *s = p.copy->c();
- loopxyz(b, b.grid, if(!isempty(*s) || s->children) pastecube(*s, c); s++);
- prefabmesh r;
- neighbourstack[++neighbourdepth] = worldroot;
- loopi(8) genprefabmesh(r, worldroot[i], ivec(i, ivec(0, 0, 0), worldsize/2), worldsize/2);
- --neighbourdepth;
- r.setup(p);
- freeocta(worldroot);
- worldroot = oldworldroot;
- worldscale = oldworldscale;
- worldsize = oldworldsize;
- useshaderbyname("prefab");
-}
-
extern int outlinecolour;
-static void renderprefab(prefab &p, const vec &o, float yaw, float pitch, float roll, float size, const vec &color) {
- if(!p.numtris) {
- genprefabmesh(p);
- if(!p.numtris) return;
- }
- block3 &b = *p.copy;
- matrix4 m;
- m.identity();
- m.settranslation(o);
- if(yaw) m.rotate_around_z(yaw*RAD);
- if(pitch) m.rotate_around_x(pitch*RAD);
- if(roll) m.rotate_around_y(-roll*RAD);
- matrix3 w(m);
- if(size > 0 && size != 1) m.scale(size);
- m.translate(vec(b.s).mul(-b.grid*0.5f));
- gle::bindvbo(p.vbo);
- gle::bindebo(p.ebo);
- gle::enablevertex();
- gle::enablenormal();
- prefabmesh::vertex *v = (prefabmesh::vertex *)0;
- gle::vertexpointer(sizeof(prefabmesh::vertex), v->pos.v);
- gle::normalpointer(sizeof(prefabmesh::vertex), v->norm.v, GL_BYTE);
- matrix4 pm;
- pm.mul(camprojmatrix, m);
- GLOBALPARAM(prefabmatrix, pm);
- GLOBALPARAM(prefabworld, w);
- SETSHADER(prefab);
- gle::color(color);
- glDrawRangeElements_(GL_TRIANGLES, 0, p.numverts-1, p.numtris*3, GL_UNSIGNED_SHORT, (ushort *)0);
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
- pm.mul(camprojmatrix, m);
- GLOBALPARAM(prefabmatrix, pm);
- SETSHADER(prefab);
- gle::color(vec::hexcolor(outlinecolour));
- glDrawRangeElements_(GL_TRIANGLES, 0, p.numverts-1, p.numtris*3, GL_UNSIGNED_SHORT, (ushort *)0);
- disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- gle::disablevertex();
- gle::disablenormal();
- gle::clearebo();
- gle::clearvbo();
-}
-
-void renderprefab(const char *name, const vec &o, float yaw, float pitch, float roll, float size, const vec &color) {
- prefab *p = loadprefab(name, false);
- if(p) renderprefab(*p, o, yaw, pitch, roll, size, color);
-}
-
-void previewprefab(const char *name, const vec &color) {
- prefab *p = loadprefab(name, false);
- if(p) {
- block3 &b = *p->copy;
- float yaw;
- vec o = calcmodelpreviewpos(vec(b.s).mul(b.grid*0.5f), yaw);
- renderprefab(*p, o, yaw, 0, 0, 1, color);
- }
-}
-
void mpcopy(editinfo *&e, selinfo &sel, bool local) {
if(local) game::edittrigger(sel, EDIT_COPY);
if(e==NULL) e = editinfos.add(new editinfo);
if(e->copy) freeblock(e->copy);
e->copy = NULL;
- protectsel(e->copy = blockcopy(block3(sel), sel.grid));
changed(sel);
}
COMMAND(copy, "");
COMMAND(pastehilite, "");
COMMAND(paste, "");
-COMMANDN(undo, editundo, "");
-COMMANDN(redo, editredo, "");
static vector<int *> editingvslots;
struct vslotref {
editinfo *e = editinfos[i];
compactvslots(e->copy->c(), e->copy->size());
}
- for(undoblock *u = undos.first; u; u = u->next)
- if(!u->numents)
- compactvslots(u->block()->c(), u->block()->size());
- for(undoblock *u = redos.first; u; u = u->next)
- if(!u->numents)
- compactvslots(u->block()->c(), u->block()->size());
}
///////////// main cube edit ////////////////
void mpflip(selinfo &sel, bool local) {
if(local) {
game::edittrigger(sel, EDIT_FLIP);
- makeundo();
}
int zs = sel.s[dimension(sel.orient)];
loopxy(sel) {
if(!dimcoord(sel.orient)) cw = -cw;
int m = sel.s[C[d]] < sel.s[R[d]] ? C[d] : R[d];
int ss = sel.s[m] = max(sel.s[R[d]], sel.s[C[d]]);
- if(local) makeundo();
loop(z,sel.s[D[d]]) loopi(cw>0 ? 1 : 3) {
loopxy(sel) rotatecube(selcube(x,y,z), d);
loop(y,ss/2) loop(x,ss-1-y*2) rotatequad
extern bool havesel;
int entlooplevel = 0;
int efocus = -1, enthover = -1, entorient = -1, oldhover = -1;
-bool undonext = true;
VARF(entediting, 0, 0, 1, { if(!entediting) { entcancel(); efocus = enthover = -1; } });
}
void entadd(int id) {
- undonext = true;
entgroup.add(id);
}
-undoblock *newundoent() {
- int numents = entgroup.length();
- if(numents <= 0) return NULL;
- undoblock *u = (undoblock *)new uchar[sizeof(undoblock) + numents*sizeof(undoent)];
- u->numents = numents;
- undoent *e = (undoent *)(u + 1);
- loopv(entgroup) {
- e->i = entgroup[i];
- e->e = *entities::getents()[entgroup[i]];
- e++;
- }
- return u;
-}
-
-void makeundoent() {
- if(!undonext) return;
- undonext = false;
- oldhover = enthover;
- undoblock *u = newundoent();
- if(u) addundo(u);
-}
-
// convenience macros implicitly define:
// e entity, currently edited ent
// n int, index to currently edited ent
-#define addimplicit(f) { if(entgroup.empty() && enthover>=0) { entadd(enthover); undonext = (enthover != oldhover); f; entgroup.drop(); } else f; }
+#define addimplicit(f) { if(entgroup.empty() && enthover>=0) { entadd(enthover); f; entgroup.drop(); } else f; }
#define entfocusv(i, f, v){ int n = efocus = (i); if(n>=0) { extentity &e = *v[n]; f; (void) e; } }
#define entfocus(i, f) entfocusv(i, f, entities::getents())
#define enteditv(i, f, v) { entfocusv(i, { int a = 0; (void) a; }, v); }
#define setgroup(exp) { entcancel(); addgroup(exp); }
#define groupeditloop(f){ vector<extentity *> &ents = entities::getents(); entlooplevel++; int _ = efocus; loopv(entgroup) enteditv(entgroup[i], f, ents); efocus = _; entlooplevel--; }
#define groupeditpure(f){ if(entlooplevel>0) { entedit(efocus, f); } else groupeditloop(f); }
-#define groupeditundo(f){ makeundoent(); groupeditpure(f); }
-#define groupedit(f) { addimplicit(groupeditundo(f)); }
+#define groupedit(f) { addimplicit(groupeditpure(f)); }
vec getselpos() {
vector<extentity *> &ents = entities::getents();
return vec(sel.o);
}
-undoblock *copyundoents(undoblock *u) {
- entcancel();
- undoent *e = u->ents();
- loopi(u->numents)
- entadd(e[i].i);
- undoblock *c = newundoent();
- loopi(u->numents) if(e[i].e.type==ET_EMPTY)
- entgroup.removeobj(e[i].i);
- return c;
-}
-
-void pasteundoent(int idx, const entity &ue) {
- if(idx < 0 || idx >= MAXENTS) return;
- vector<extentity *> &ents = entities::getents();
- while(ents.length() < idx) ents.add(entities::newentity())->type = ET_EMPTY;
- int efocus = -1;
- entedit(idx, (entity &)e = ue);
-}
-
-void pasteundoents(undoblock *u) {
- undoent *ue = u->ents();
- loopi(u->numents)
- entedit(ue[i].i, (entity &)e = ue[i].e);
-}
-
void entflip() {
if(noentedit()) return;
int d = dimension(sel.orient);
float mid = sel.s[d]*sel.grid/2+sel.o[d];
- groupeditundo(e.o[d] -= (e.o[d]-mid)*2);
+ groupeditpure(e.o[d] -= (e.o[d]-mid)*2);
}
void entrotate(int *cw) {
int dd = (*cw<0) == dimcoord(sel.orient) ? R[d] : C[d];
float mid = sel.s[dd]*sel.grid/2+sel.o[dd];
vec s(sel.o.v);
- groupeditundo(
+ groupeditpure(
e.o[dd] -= (e.o[dd]-mid)*2;
e.o.sub(s);
swap(e.o[R[d]], e.o[C[d]]);
r = (entselsnap ? g[R[d]] : v[R[d]]) - e.o[R[d]];
c = (entselsnap ? g[C[d]] : v[C[d]]) - e.o[C[d]];
);
- if(entmoving==1) makeundoent();
groupeditpure(e.o[R[d]] += r; e.o[C[d]] += c);
entmoving = 2;
}
}
bool enttoggle(int id) {
- undonext = true;
int i = entgroup.find(id);
if(i < 0)
entadd(id);
dropentity(*t);
t->type = ET_EMPTY;
enttoggle(idx);
- makeundoent();
entedit(idx, e.type = type);
}
}
keepents = 0;
int j = 0;
- groupeditundo(e.type = entcopybuf[j++].type;);
}
COMMAND(newent, "siiiii");
cleardecals();
clearsleep();
cancelsel();
- pruneundos();
clearmapcrc();
entities::clearents();
outsideents.setsize(0);
}
break;
}
- case EDIT_UNDO:
- case EDIT_REDO: {
- uchar *outbuf = NULL;
- int inlen = 0, outlen = 0;
- if(packundo(op, inlen, outbuf, outlen)) {
- if(addmsg(N_EDITF + op, "ri2", inlen, outlen)) messages.put(outbuf, outlen);
- delete[] outbuf;
- }
- break;
- }
}
}
void printvar(fpsent *d, ident *id) {
if(d) unpackeditinfo(d->edit, q.buf, q.maxlen, unpacklen);
break;
}
- case N_UNDO:
- case N_REDO: {
- int cn = getint(p), unpacklen = getint(p), packlen = getint(p);
- fpsent *d = getclient(cn);
- ucharbuf q = p.subbuf(max(packlen, 0));
- if(d) unpackundo(q.buf, q.maxlen, unpacklen);
- break;
- }
case N_EDITF: // coop editing messages
case N_EDITT:
case N_EDITM:
N_PING, N_PONG, N_CLIENTPING,
N_TIMEUP, N_FORCEINTERMISSION,
N_SERVMSG, N_ITEMLIST, N_RESUME,
- N_EDITMODE, N_EDITENT, N_EDITF, N_EDITT, N_EDITM, N_FLIP, N_COPY, N_PASTE, N_ROTATE, N_REPLACE, N_DELCUBE, N_REMIP, N_EDITVSLOT, N_UNDO, N_REDO, N_NEWMAP, N_GETMAP, N_SENDMAP, N_CLIPBOARD, N_EDITVAR,
+ N_EDITMODE, N_EDITENT, N_EDITF, N_EDITT, N_EDITM, N_FLIP, N_COPY, N_PASTE, N_ROTATE, N_REPLACE, N_DELCUBE, N_REMIP, N_EDITVSLOT, N_NEWMAP, N_GETMAP, N_SENDMAP, N_CLIPBOARD, N_EDITVAR,
N_MASTERMODE, N_KICK, N_CLEARBANS, N_CURRENTMASTER, N_SPECTATOR, N_SETMASTER, N_SETTEAM, N_ANNOUNCE,
N_LISTDEMOS, N_SENDDEMOLIST, N_GETDEMO, N_SENDDEMO,
N_DEMOPLAYBACK, N_RECORDDEMO, N_STOPDEMO, N_CLEARDEMOS,
N_PING, 2, N_PONG, 2, N_CLIENTPING, 2,
N_TIMEUP, 2, N_FORCEINTERMISSION, 1,
N_SERVMSG, 0, N_ITEMLIST, 0, N_RESUME, 0,
- N_EDITMODE, 2, N_EDITENT, 11, N_EDITF, 16, N_EDITT, 16, N_EDITM, 16, N_FLIP, 14, N_COPY, 14, N_PASTE, 14, N_ROTATE, 15, N_REPLACE, 17, N_DELCUBE, 14, N_REMIP, 1, N_EDITVSLOT, 16, N_UNDO, 0, N_REDO, 0, N_NEWMAP, 2, N_GETMAP, 1, N_SENDMAP, 0, N_EDITVAR, 0,
+ N_EDITMODE, 2, N_EDITENT, 11, N_EDITF, 16, N_EDITT, 16, N_EDITM, 16, N_FLIP, 14, N_COPY, 14, N_PASTE, 14, N_ROTATE, 15, N_REPLACE, 17, N_DELCUBE, 14, N_REMIP, 1, N_EDITVSLOT, 16, N_NEWMAP, 2, N_GETMAP, 1, N_SENDMAP, 0, N_EDITVAR, 0,
N_MASTERMODE, 2, N_KICK, 0, N_CLEARBANS, 1, N_CURRENTMASTER, 0, N_SPECTATOR, 3, N_SETMASTER, 0, N_SETTEAM, 0, N_ANNOUNCE, 2,
N_LISTDEMOS, 1, N_SENDDEMOLIST, 0, N_GETDEMO, 3, N_SENDDEMO, 0,
N_DEMOPLAYBACK, 3, N_RECORDDEMO, 2, N_STOPDEMO, 1, N_CLEARDEMOS, 2,
N_ANNOUNCE, N_SENDDEMOLIST, N_SENDDEMO, N_DEMOPLAYBACK, N_SENDMAP,
N_CLIENT, N_AUTHCHAL, N_INITAI, N_EXPIRETOKENS, N_DROPTOKENS, N_STEALTOKENS, N_DEMOPACKET, -2, N_REMIP,
N_NEWMAP, N_GETMAP, N_SENDMAP, N_CLIPBOARD, -3, N_EDITENT, N_EDITF, N_EDITT, N_EDITM, N_FLIP, N_COPY, N_PASTE, N_ROTATE, N_REPLACE,
- N_DELCUBE, N_EDITVAR, N_EDITVSLOT, N_UNDO, N_REDO, -4, N_POS, NUMMSG),
+ N_DELCUBE, N_EDITVAR, N_EDITVSLOT, -4, N_POS, NUMMSG),
connectfilter(-1, N_CONNECT, -2, N_AUTHANS, -3, N_PING, NUMMSG);
int checktype(int type, clientinfo *ci) {
if(ci) {
if(ci && ci->state.state!=CS_SPECTATOR) QUEUE_MSG;
break;
}
- case N_UNDO:
- case N_REDO: {
- int unpacklen = getint(p), packlen = getint(p);
- if(!ci || ci->state.state==CS_SPECTATOR || packlen <= 0 || packlen > (1<<16) || unpacklen <= 0) {
- if(packlen > 0) p.subbuf(packlen);
- break;
- }
- //~if(p.remaining() < packlen) { disconnect_client(sender, DISC_MSGERR); return; }
- packetbuf q(32 + packlen, ENET_PACKET_FLAG_RELIABLE);
- putint(q, type);
- putint(q, ci->clientnum);
- putint(q, unpacklen);
- putint(q, packlen);
- if(packlen > 0) p.get(q.subbuf(packlen).buf, packlen);
- sendpacket(-1, 1, q.finalize(), ci->clientnum);
- break;
- }
case N_SERVCMD:
getstring(text, p);
break;
-
case -1:
//~disconnect_client(sender, DISC_MSGERR);
return;
// octaedit
-enum { EDIT_FACE = 0, EDIT_TEX, EDIT_MAT, EDIT_FLIP, EDIT_COPY, EDIT_PASTE, EDIT_ROTATE, EDIT_REPLACE, EDIT_DELCUBE, EDIT_REMIP, EDIT_VSLOT, EDIT_UNDO, EDIT_REDO };
+enum { EDIT_FACE = 0, EDIT_TEX, EDIT_MAT, EDIT_FLIP, EDIT_COPY, EDIT_PASTE, EDIT_ROTATE, EDIT_REPLACE, EDIT_DELCUBE, EDIT_REMIP, EDIT_VSLOT };
struct selinfo {
int corner;
extern bool packeditinfo(editinfo *e, int &inlen, uchar *&outbuf, int &outlen);
extern bool unpackeditinfo(editinfo *&e, const uchar *inbuf, int inlen, int outlen);
extern void freeeditinfo(editinfo *&e);
-extern void pruneundos(int maxremain = 0);
-extern bool packundo(int op, int &inlen, uchar *&outbuf, int &outlen);
-extern bool unpackundo(const uchar *inbuf, int inlen, int outlen);
extern bool noedit(bool view = false, bool msg = true);
extern void toggleedit(bool force = true);
extern void mpeditface(int dir, int mode, selinfo &sel, bool local);
virtual int texture(VSlot &vslot, float scale, bool overlaid = true) = 0;
virtual int playerpreview(int model, int team, int weap, float scale, const char *overlaid = NULL) = 0;
virtual int modelpreview(const char *name, int anim, float scale, const char *overlaid = NULL, bool throttle = false) = 0;
- virtual int prefabpreview(const char *prefab, const vec &color, float scale, const char *overlaid = NULL, bool throttle = false) = 0;
virtual void slider(int &val, int vmin, int vmax, int color, const char *label = NULL) = 0;
virtual void separator() = 0;
virtual void progress(float percent) = 0;