#ifndef ENGINE_OCTA_HH #define ENGINE_OCTA_HH #include #include struct vertex { vec pos; bvec4 norm; vec tc; bvec4 tangent; }; struct selinfo { int corner; int cx, cxs, cy, cys; ivec o, s; int grid, orient; selinfo() : corner(0), cx(0), cxs(0), cy(0), cys(0), o(0, 0, 0), s(0, 0, 0), grid(8), orient(0) {} int size() const { return s.x*s.y*s.z; } int us(int d) const { return s[d]*grid; } bool operator==(const selinfo &sel) const { return o==sel.o && s==sel.s && grid==sel.grid && orient==sel.orient; } bool validate() { extern int worldsize; if(grid <= 0 || grid >= worldsize) return false; if(o.x >= worldsize || o.y >= worldsize || o.z >= worldsize) return false; if(o.x < 0) { s.x -= (grid - 1 - o.x)/grid; o.x = 0; } if(o.y < 0) { s.y -= (grid - 1 - o.y)/grid; o.y = 0; } if(o.z < 0) { s.z -= (grid - 1 - o.z)/grid; o.z = 0; } s.x = clamp(s.x, 0, (worldsize - o.x)/grid); s.y = clamp(s.y, 0, (worldsize - o.y)/grid); s.z = clamp(s.z, 0, (worldsize - o.z)/grid); return s.x > 0 && s.y > 0 && s.z > 0; } }; // 6-directional octree heightfield map format struct elementset { ushort texture, envmap; union { struct { uchar orient, layer; }; ushort reuse; }; ushort length, minvert, maxvert; }; enum { EMID_NONE = 0, EMID_CUSTOM, EMID_SKY, EMID_RESERVED }; struct materialsurface { ivec o; ushort csize, rsize; ushort material, skip; uchar orient, visible; union { ushort envmap; uchar ends; }; }; struct vertinfo { ushort x, y, z, norm; void setxyz(ushort a, ushort b, ushort c) { x = a; y = b; z = c; } void setxyz(const ivec &v) { setxyz(v.x, v.y, v.z); } void set(ushort a, ushort b, ushort c, ushort n = 0) { setxyz(a, b, c); norm = n; } void set(const ivec &v, ushort n = 0) { set(v.x, v.y, v.z, n); } ivec getxyz() const { return ivec(x, y, z); } }; enum { LAYER_TOP = (1<<5), LAYER_BOTTOM = (1<<6), LAYER_BLEND = LAYER_TOP|LAYER_BOTTOM, MAXFACEVERTS = 15 }; struct surfaceinfo { uchar verts, numverts; int totalverts() const { return numverts&MAXFACEVERTS; } bool used() const { return (numverts&~LAYER_TOP) != 0; } void clear() { numverts = (numverts&MAXFACEVERTS) | LAYER_TOP; } void brighten() { clear(); } }; static const surfaceinfo topsurface = {0, LAYER_TOP}; static const surfaceinfo bottomsurface = {0, LAYER_BOTTOM}; #define brightsurface topsurface #define ambientsurface topsurface struct grasstri { vec v[4]; int numv; plane surface; vec center; float radius; float minz, maxz; ushort texture, blend; }; struct occludequery { void *owner; GLuint id; int fragments; }; struct vtxarray; struct octaentities { vector mapmodels, decals, other; occludequery *query; octaentities *next, *rnext; int distance; ivec o; int size; ivec bbmin, bbmax; octaentities(const ivec &o, int size) : query(0), o(o), size(size), bbmin(o), bbmax(o) { bbmin.add(size); } }; enum { OCCLUDE_NOTHING = 0, OCCLUDE_GEOM, OCCLUDE_BB, OCCLUDE_PARENT }; enum { MERGE_ORIGIN = 1<<0, MERGE_PART = 1<<1, MERGE_USE = 1<<2 }; struct vtxarray { vtxarray *parent; vector children; vtxarray *next, *rnext; // linked list of visible VOBs vertex *vdata; // vertex data ushort voffset, eoffset, skyoffset, decaloffset; // offset into vertex data ushort *edata, *skydata, *decaldata; // vertex indices GLuint vbuf, ebuf, skybuf, decalbuf; // VBOs ushort minvert, maxvert; // DRE info elementset *texelems, *decalelems; // List of element indices sets (range) per texture materialsurface *matbuf; // buffer of material surfaces int verts, tris, texs, blendtris, blends, alphabacktris, alphaback, alphafronttris, alphafront, refracttris, refract, texmask, sky, matsurfs, matmask, distance, rdistance, dyntexs, decaltris, decaltexs; ivec o; int size; // location and size of cube. ivec geommin, geommax; // BB of geom ivec alphamin, alphamax; // BB of alpha geom ivec refractmin, refractmax; // BB of refract geom ivec skymin, skymax; // BB of any sky geom ivec lavamin, lavamax; // BB of any lava ivec watermin, watermax; // BB of any water ivec glassmin, glassmax; // BB of any glass ivec nogimin, nogimax; // BB of any nogi ivec bbmin, bbmax; // BB of everything including children uchar curvfc, occluded; occludequery *query; vector mapmodels, decals; vector grasstris; int hasmerges, mergelevel; int shadowmask; }; struct cube; struct clipplanes { vec o, r, v[8]; plane p[12]; uchar side[12]; uchar size, visible; const cube *owner; int version; }; struct facebounds { ushort u1, u2, v1, v2; bool empty() const { return u1 >= u2 || v1 >= v2; } }; struct tjoint { int next; ushort offset; uchar edge; }; struct cubeext { vtxarray *va; // vertex array for children, or NULL octaentities *ents; // map entities inside cube surfaceinfo surfaces[6]; // render info for each surface int tjoints; // linked list of t-joints uchar maxverts; // allocated space for verts vertinfo *verts() { return (vertinfo *)(this+1); } }; struct cube { cube *children; // points to 8 cube structures which are its children, or NULL. -Z first, then -Y, -X cubeext *ext; // extended info for the cube union { uchar edges[12]; // edges of the cube, each uchar is 2 4bit values denoting the range. // see documentation jpgs for more info. uint faces[3]; // 4 edges of each dimension together representing 2 perpendicular faces }; ushort texture[6]; // one for each face. same order as orient. ushort material; // empty-space material uchar merged; // merged faces of the cube union { uchar escaped; // mask of which children have escaped merges uchar visible; // visibility info for faces }; }; struct block3 { ivec o, s; int grid, orient; block3() {} block3(const selinfo &sel) : o(sel.o), s(sel.s), grid(sel.grid), orient(sel.orient) {} cube *c() { return (cube *)(this+1); } int size() const { return s.x*s.y*s.z; } }; struct editinfo { block3 *copy; 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 allocnodes, selchildcount, selchildmat; const uint F_EMPTY = 0; // all edges in the range (0,0) const uint F_SOLID = 0x80808080; // all edges in the range (0,8) #define isempty(c) ((c).faces[0]==F_EMPTY) #define isentirelysolid(c) ((c).faces[0]==F_SOLID && (c).faces[1]==F_SOLID && (c).faces[2]==F_SOLID) #define setfaces(c, face) { (c).faces[0] = (c).faces[1] = (c).faces[2] = face; } #define solidfaces(c) setfaces(c, F_SOLID) #define emptyfaces(c) setfaces(c, F_EMPTY) #define edgemake(a, b) ((b)<<4|a) #define edgeget(edge, coord) ((coord) ? (edge)>>4 : (edge)&0xF) #define edgeset(edge, coord, val) ((edge) = ((coord) ? ((edge)&0xF)|((val)<<4) : ((edge)&0xF0)|(val))) #define cubeedge(c, d, x, y) ((c).edges[(((d)<<2)+((y)<<1)+(x))]) #define octadim(d) (1<<(d)) // creates mask for bit of given dimension #define octacoord(d, i) (((i)&octadim(d))>>(d)) #define oppositeocta(d, i) ((i)^octadim(D[d])) #define octaindex(d,x,y,z) (((z)<>(scale))&1)<<2) | ((((y)>>(scale))&1)<<1) | (((x)>>(scale))&1)) static inline uchar octaboxoverlap(const ivec &o, int size, const ivec &bbmin, const ivec &bbmax) { uchar p = 0xFF; // bitmask of possible collisions with octants. 0 bit = 0 octant, etc ivec mid = ivec(o).add(size); if(mid.z <= bbmin.z) p &= 0xF0; // not in a -ve Z octant else if(mid.z >= bbmax.z) p &= 0x0F; // not in a +ve Z octant if(mid.y <= bbmin.y) p &= 0xCC; // not in a -ve Y octant else if(mid.y >= bbmax.y) p &= 0x33; // etc.. if(mid.x <= bbmin.x) p &= 0xAA; else if(mid.x >= bbmax.x) p &= 0x55; return p; } #define loopoctabox(o, size, bbmin, bbmax) uchar possible = octaboxoverlap(o, size, bbmin, bbmax); loopi(8) if(possible&(1<>1) #define dimcoord(orient) ((orient)&1) #define opposite(orient) ((orient)^1) enum { VFC_FULL_VISIBLE = 0, VFC_PART_VISIBLE, VFC_FOGGED, VFC_NOT_VISIBLE, PVS_FULL_VISIBLE, PVS_PART_VISIBLE, PVS_FOGGED }; #define GENCUBEVERTS(x0,x1, y0,y1, z0,z1) \ GENCUBEVERT(0, x1, y1, z0) \ GENCUBEVERT(1, x0, y1, z0) \ GENCUBEVERT(2, x0, y1, z1) \ GENCUBEVERT(3, x1, y1, z1) \ GENCUBEVERT(4, x1, y0, z1) \ GENCUBEVERT(5, x0, y0, z1) \ GENCUBEVERT(6, x0, y0, z0) \ GENCUBEVERT(7, x1, y0, z0) #define GENFACEVERTX(o,n, x,y,z, xv,yv,zv) GENFACEVERT(o,n, x,y,z, xv,yv,zv) #define GENFACEVERTSX(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1) \ GENFACEORIENT(0, GENFACEVERTX(0,0, x0,y1,z1, d0,r1,c1), GENFACEVERTX(0,1, x0,y1,z0, d0,r1,c0), GENFACEVERTX(0,2, x0,y0,z0, d0,r0,c0), GENFACEVERTX(0,3, x0,y0,z1, d0,r0,c1)) \ GENFACEORIENT(1, GENFACEVERTX(1,0, x1,y1,z1, d1,r1,c1), GENFACEVERTX(1,1, x1,y0,z1, d1,r0,c1), GENFACEVERTX(1,2, x1,y0,z0, d1,r0,c0), GENFACEVERTX(1,3, x1,y1,z0, d1,r1,c0)) #define GENFACEVERTY(o,n, x,y,z, xv,yv,zv) GENFACEVERT(o,n, x,y,z, xv,yv,zv) #define GENFACEVERTSY(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1) \ GENFACEORIENT(2, GENFACEVERTY(2,0, x1,y0,z1, c1,d0,r1), GENFACEVERTY(2,1, x0,y0,z1, c0,d0,r1), GENFACEVERTY(2,2, x0,y0,z0, c0,d0,r0), GENFACEVERTY(2,3, x1,y0,z0, c1,d0,r0)) \ GENFACEORIENT(3, GENFACEVERTY(3,0, x0,y1,z0, c0,d1,r0), GENFACEVERTY(3,1, x0,y1,z1, c0,d1,r1), GENFACEVERTY(3,2, x1,y1,z1, c1,d1,r1), GENFACEVERTY(3,3, x1,y1,z0, c1,d1,r0)) #define GENFACEVERTZ(o,n, x,y,z, xv,yv,zv) GENFACEVERT(o,n, x,y,z, xv,yv,zv) #define GENFACEVERTSZ(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1) \ GENFACEORIENT(4, GENFACEVERTZ(4,0, x0,y0,z0, r0,c0,d0), GENFACEVERTZ(4,1, x0,y1,z0, r0,c1,d0), GENFACEVERTZ(4,2, x1,y1,z0, r1,c1,d0), GENFACEVERTZ(4,3, x1,y0,z0, r1,c0,d0)) \ GENFACEORIENT(5, GENFACEVERTZ(5,0, x0,y0,z1, r0,c0,d1), GENFACEVERTZ(5,1, x1,y0,z1, r1,c0,d1), GENFACEVERTZ(5,2, x1,y1,z1, r1,c1,d1), GENFACEVERTZ(5,3, x0,y1,z1, r0,c1,d1)) #define GENFACEVERTSXY(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1) \ GENFACEVERTSX(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1) \ GENFACEVERTSY(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1) #define GENFACEVERTS(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1) \ GENFACEVERTSXY(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1) \ GENFACEVERTSZ(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1) /* material flags and ids */ enum { MATF_INDEX_SHIFT = 0, MATF_VOLUME_SHIFT = 2, MATF_CLIP_SHIFT = 5, MATF_FLAG_SHIFT = 8, MATF_INDEX = 3 << MATF_INDEX_SHIFT, MATF_VOLUME = 7 << MATF_VOLUME_SHIFT, MATF_CLIP = 7 << MATF_CLIP_SHIFT, MATF_FLAGS = 0xFF << MATF_FLAG_SHIFT }; enum // cube empty-space materials { MAT_AIR = 0, // the default, fill the empty space with air MAT_WATER = 1 << MATF_VOLUME_SHIFT, // fill with water, showing waves at the surface MAT_LAVA = 2 << MATF_VOLUME_SHIFT, // fill with lava MAT_GLASS = 3 << MATF_VOLUME_SHIFT, // behaves like clip but is blended blueish MAT_NOCLIP = 1 << MATF_CLIP_SHIFT, // collisions always treat cube as empty MAT_CLIP = 2 << MATF_CLIP_SHIFT, // collisions always treat cube as solid MAT_GAMECLIP = 3 << MATF_CLIP_SHIFT, // game specific clip material MAT_DEATH = 1 << MATF_FLAG_SHIFT, // force player suicide MAT_NOGI = 2 << MATF_FLAG_SHIFT, // disable global illumination MAT_ALPHA = 4 << MATF_FLAG_SHIFT // alpha blended }; #define isliquid(mat) ((mat)==MAT_WATER || (mat)==MAT_LAVA) #define isclipped(mat) ((mat)==MAT_GLASS) #define isdeadly(mat) ((mat)==MAT_LAVA) /* API */ void freeocta(cube *c); cube *newcubes(uint face = F_EMPTY, int mat = MAT_AIR); cubeext *growcubeext(cubeext *ext, int maxverts); void setcubeext(cube &c, cubeext *ext); cubeext *newcubeext(cube &c, int maxverts = 0, bool init = true); void getcubevector(cube &c, int d, int x, int y, int z, ivec &p); void setcubevector(cube &c, int d, int x, int y, int z, const ivec &p); int familysize(const cube &c); void discardchildren(cube &c, bool fixtex = false, int depth = 0); void optiface(uchar *p, cube &c); void validatec(cube *c, int size = 0); bool isvalidcube(const cube &c); extern ivec lu; extern int lusize; cube &lookupcube(const ivec &to, int tsize = 0, ivec &ro = lu, int &rsize = lusize); extern const cube *neighbourstack[32]; extern int neighbourdepth; const cube &neighbourcube(const cube &c, int orient, const ivec &co, int size, ivec &ro = lu, int &rsize = lusize); void resetclipplanes(); void forcemip(cube &c, bool fixtex = true); bool subdividecube(cube &c, bool fullcheck=true, bool brighten=true); int faceconvexity(const ivec v[4]); int faceconvexity(const ivec v[4], int &vis); int faceconvexity(const vertinfo *verts, int numverts, int size); int faceconvexity(const cube &c, int orient); bool flataxisface(const cube &c, int orient); void genclipbounds(const cube &c, const ivec &co, int size, clipplanes &p); void genclipplanes(const cube &c, const ivec &co, int size, clipplanes &p, bool collide = true, bool noclip = false); bool visibleface(const cube &c, int orient, const ivec &co, int size, ushort mat = MAT_AIR, ushort nmat = MAT_AIR, ushort matmask = MATF_VOLUME); int classifyface(const cube &c, int orient, const ivec &co, int size); int visibletris(const cube &c, int orient, const ivec &co, int size, ushort vmat = MAT_AIR, ushort nmat = MAT_ALPHA, ushort matmask = MAT_ALPHA); int visibleorient(const cube &c, int orient); void genfaceverts(const cube &c, int orient, ivec v[4]); int calcmergedsize(int orient, const ivec &co, int size, const vertinfo *verts, int numverts); void invalidatemerges(cube &c, const ivec &co, int size, bool msg); void remip(); static inline cubeext &ext(cube &c) { return *(c.ext ? c.ext : newcubeext(c)); } int lookupmaterial(const vec &o); static inline bool insideworld(const vec &o) { extern int worldsize; return o.x>=0 && o.x=0 && o.y=0 && o.z