1787 lines
57 KiB
C++
1787 lines
57 KiB
C++
// octarender.cpp: fill vertex arrays with different cube surfaces.
|
|
|
|
#include "octarender.hh"
|
|
|
|
#include <cassert>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <shared/command.hh>
|
|
#include <shared/glemu.hh>
|
|
#include <shared/igame.hh>
|
|
|
|
#include "blend.hh"
|
|
#include "console.hh" /* conoutf */
|
|
#include "grass.hh"
|
|
#include "light.hh"
|
|
#include "main.hh" // mainmenu, laodprogress, isconnected
|
|
#include "material.hh"
|
|
#include "rendergl.hh"
|
|
#include "renderlights.hh"
|
|
#include "renderparticles.hh"
|
|
#include "rendersky.hh"
|
|
#include "renderva.hh"
|
|
#include "texture.hh"
|
|
#include "world.hh"
|
|
|
|
struct vboinfo
|
|
{
|
|
int uses;
|
|
uchar *data;
|
|
};
|
|
|
|
static hashtable<GLuint, vboinfo> vbos;
|
|
|
|
VAR(printvbo, 0, 0, 1);
|
|
VARFN(vbosize, maxvbosize, 0, 1<<14, 1<<16, allchanged());
|
|
|
|
enum
|
|
{
|
|
VBO_VBUF = 0,
|
|
VBO_EBUF,
|
|
VBO_SKYBUF,
|
|
VBO_DECALBUF,
|
|
NUMVBO
|
|
};
|
|
|
|
static vector<uchar> vbodata[NUMVBO];
|
|
static vector<vtxarray *> vbovas[NUMVBO];
|
|
static int vbosize[NUMVBO];
|
|
|
|
static void destroyvbo(GLuint vbo)
|
|
{
|
|
vboinfo *exists = vbos.access(vbo);
|
|
if(!exists) return;
|
|
vboinfo &vbi = *exists;
|
|
if(vbi.uses <= 0) return;
|
|
vbi.uses--;
|
|
if(!vbi.uses)
|
|
{
|
|
glDeleteBuffers_(1, &vbo);
|
|
if(vbi.data) delete[] vbi.data;
|
|
vbos.remove(vbo);
|
|
}
|
|
}
|
|
|
|
static void genvbo(int type, void *buf, int len, vtxarray **vas, int numva)
|
|
{
|
|
gle::disable();
|
|
|
|
GLuint vbo;
|
|
glGenBuffers_(1, &vbo);
|
|
GLenum target = type==VBO_VBUF ? GL_ARRAY_BUFFER : GL_ELEMENT_ARRAY_BUFFER;
|
|
glBindBuffer_(target, vbo);
|
|
glBufferData_(target, len, buf, GL_STATIC_DRAW);
|
|
glBindBuffer_(target, 0);
|
|
|
|
vboinfo &vbi = vbos[vbo];
|
|
vbi.uses = numva;
|
|
vbi.data = new uchar[len];
|
|
memcpy(vbi.data, buf, len);
|
|
|
|
if(printvbo) conoutf(CON_DEBUG, "vbo %d: type %d, size %d, %d uses", vbo, type, len, numva);
|
|
|
|
loopi(numva)
|
|
{
|
|
vtxarray *va = vas[i];
|
|
switch(type)
|
|
{
|
|
case VBO_VBUF:
|
|
va->vbuf = vbo;
|
|
va->vdata = (vertex *)vbi.data;
|
|
break;
|
|
case VBO_EBUF:
|
|
va->ebuf = vbo;
|
|
va->edata = (ushort *)vbi.data;
|
|
break;
|
|
case VBO_SKYBUF:
|
|
va->skybuf = vbo;
|
|
va->skydata = (ushort *)vbi.data;
|
|
break;
|
|
case VBO_DECALBUF:
|
|
va->decalbuf = vbo;
|
|
va->decaldata = (ushort *)vbi.data;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void flushvbo(int type = -1)
|
|
{
|
|
if(type < 0)
|
|
{
|
|
loopi(NUMVBO) flushvbo(i);
|
|
return;
|
|
}
|
|
|
|
vector<uchar> &data = vbodata[type];
|
|
if(data.empty()) return;
|
|
vector<vtxarray *> &vas = vbovas[type];
|
|
genvbo(type, data.getbuf(), data.length(), vas.getbuf(), vas.length());
|
|
data.setsize(0);
|
|
vas.setsize(0);
|
|
vbosize[type] = 0;
|
|
}
|
|
|
|
static uchar *addvbo(vtxarray *va, int type, int numelems, int elemsize)
|
|
{
|
|
switch(type)
|
|
{
|
|
case VBO_VBUF: va->voffset = vbosize[type]; break;
|
|
case VBO_EBUF: va->eoffset = vbosize[type]; break;
|
|
case VBO_SKYBUF: va->skyoffset = vbosize[type]; break;
|
|
case VBO_DECALBUF: va->decaloffset = vbosize[type]; break;
|
|
}
|
|
|
|
vbosize[type] += numelems;
|
|
|
|
vector<uchar> &data = vbodata[type];
|
|
vector<vtxarray *> &vas = vbovas[type];
|
|
|
|
vas.add(va);
|
|
|
|
int len = numelems*elemsize;
|
|
uchar *buf = data.reserve(len).buf;
|
|
data.advance(len);
|
|
return buf;
|
|
}
|
|
|
|
struct verthash
|
|
{
|
|
static const int SIZE = 1<<13;
|
|
int table[SIZE];
|
|
vector<vertex> verts;
|
|
vector<int> chain;
|
|
|
|
verthash() { clearverts(); }
|
|
|
|
void clearverts()
|
|
{
|
|
memset(table, -1, sizeof(table));
|
|
chain.setsize(0);
|
|
verts.setsize(0);
|
|
}
|
|
|
|
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.tc==v.tc && c.norm==v.norm && c.tangent==v.tangent)
|
|
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 vec &tc = vec(0, 0, 0), const bvec &norm = bvec(128, 128, 128), const bvec4 &tangent = bvec4(128, 128, 128, 128))
|
|
{
|
|
vertex vtx;
|
|
vtx.pos = pos;
|
|
vtx.tc = tc;
|
|
vtx.norm = norm;
|
|
vtx.tangent = tangent;
|
|
return addvert(vtx);
|
|
}
|
|
};
|
|
|
|
enum
|
|
{
|
|
NO_ALPHA = 0,
|
|
ALPHA_BACK,
|
|
ALPHA_FRONT,
|
|
ALPHA_REFRACT
|
|
};
|
|
|
|
struct sortkey
|
|
{
|
|
ushort tex, envmap;
|
|
uchar orient, layer, alpha;
|
|
|
|
sortkey() {}
|
|
sortkey(ushort tex, uchar orient, uchar layer = LAYER_TOP, ushort envmap = EMID_NONE, uchar alpha = NO_ALPHA)
|
|
: tex(tex), envmap(envmap), orient(orient), layer(layer), alpha(alpha)
|
|
{}
|
|
|
|
bool operator==(const sortkey &o) const { return tex==o.tex && envmap==o.envmap && orient==o.orient && layer==o.layer && alpha==o.alpha; }
|
|
|
|
static inline bool sort(const sortkey &x, const sortkey &y)
|
|
{
|
|
if(x.alpha < y.alpha) return true;
|
|
if(x.alpha > y.alpha) return false;
|
|
if(x.layer < y.layer) return true;
|
|
if(x.layer > y.layer) return false;
|
|
if(x.tex == y.tex)
|
|
{
|
|
if(x.envmap < y.envmap) return true;
|
|
if(x.envmap > y.envmap) return false;
|
|
if(x.orient < y.orient) return true;
|
|
if(x.orient > y.orient) return false;
|
|
return false;
|
|
}
|
|
VSlot &xs = lookupvslot(x.tex, false), &ys = lookupvslot(y.tex, false);
|
|
if(xs.slot->shader < ys.slot->shader) return true;
|
|
if(xs.slot->shader > ys.slot->shader) return false;
|
|
if(xs.slot->params.length() < ys.slot->params.length()) return true;
|
|
if(xs.slot->params.length() > ys.slot->params.length()) return false;
|
|
if(x.tex < y.tex) return true;
|
|
else return false;
|
|
}
|
|
};
|
|
|
|
static inline bool htcmp(const sortkey &x, const sortkey &y)
|
|
{
|
|
return x == y;
|
|
}
|
|
|
|
static inline uint hthash(const sortkey &k)
|
|
{
|
|
return k.tex;
|
|
}
|
|
|
|
struct decalkey
|
|
{
|
|
ushort tex, envmap, reuse;
|
|
|
|
decalkey() {}
|
|
decalkey(ushort tex, ushort envmap = EMID_NONE, ushort reuse = 0)
|
|
: tex(tex), envmap(envmap), reuse(reuse)
|
|
{}
|
|
|
|
bool operator==(const decalkey &o) const { return tex==o.tex && envmap==o.envmap && reuse==o.reuse; }
|
|
|
|
static inline bool sort(const decalkey &x, const decalkey &y)
|
|
{
|
|
if(x.tex == y.tex)
|
|
{
|
|
if(x.envmap < y.envmap) return true;
|
|
if(x.envmap > y.envmap) return false;
|
|
if(x.reuse < y.reuse) return true;
|
|
else return false;
|
|
}
|
|
DecalSlot &xs = lookupdecalslot(x.tex, false), &ys = lookupdecalslot(y.tex, false);
|
|
if(xs.slot->shader < ys.slot->shader) return true;
|
|
if(xs.slot->shader > ys.slot->shader) return false;
|
|
if(xs.slot->params.length() < ys.slot->params.length()) return true;
|
|
if(xs.slot->params.length() > ys.slot->params.length()) return false;
|
|
if(x.tex < y.tex) return true;
|
|
else return false;
|
|
}
|
|
};
|
|
|
|
static inline bool htcmp(const decalkey &x, const decalkey &y)
|
|
{
|
|
return x == y;
|
|
}
|
|
|
|
static inline uint hthash(const decalkey &k)
|
|
{
|
|
return k.tex;
|
|
}
|
|
|
|
struct sortval
|
|
{
|
|
vector<ushort> tris;
|
|
|
|
sortval() {}
|
|
};
|
|
|
|
struct vacollect : verthash
|
|
{
|
|
ivec origin;
|
|
int size;
|
|
hashtable<sortkey, sortval> indices;
|
|
hashtable<decalkey, sortval> decalindices;
|
|
vector<ushort> skyindices;
|
|
vector<sortkey> texs;
|
|
vector<decalkey> decaltexs;
|
|
vector<grasstri> grasstris;
|
|
vector<materialsurface> matsurfs;
|
|
vector<octaentities *> mapmodels, decals, extdecals;
|
|
int worldtris, skytris, decaltris;
|
|
vec alphamin, alphamax;
|
|
vec refractmin, refractmax;
|
|
vec skymin, skymax;
|
|
ivec nogimin, nogimax;
|
|
|
|
void clear()
|
|
{
|
|
clearverts();
|
|
worldtris = skytris = decaltris = 0;
|
|
indices.clear();
|
|
decalindices.clear();
|
|
skyindices.setsize(0);
|
|
matsurfs.setsize(0);
|
|
mapmodels.setsize(0);
|
|
decals.setsize(0);
|
|
extdecals.setsize(0);
|
|
grasstris.setsize(0);
|
|
texs.setsize(0);
|
|
decaltexs.setsize(0);
|
|
alphamin = refractmin = skymin = vec(1e16f, 1e16f, 1e16f);
|
|
alphamax = refractmax = skymax = vec(-1e16f, -1e16f, -1e16f);
|
|
nogimin = ivec(INT_MAX, INT_MAX, INT_MAX);
|
|
nogimax = ivec(INT_MIN, INT_MIN, INT_MIN);
|
|
}
|
|
|
|
void optimize()
|
|
{
|
|
enumeratekt(indices, sortkey, k, sortval, t,
|
|
{
|
|
if(t.tris.length()) texs.add(k);
|
|
});
|
|
texs.sort(sortkey::sort);
|
|
|
|
matsurfs.shrink(optimizematsurfs(matsurfs.getbuf(), matsurfs.length()));
|
|
}
|
|
|
|
#define GENVERTS(type, ptr, body) do \
|
|
{ \
|
|
type *f = (type *)ptr; \
|
|
loopv(verts) \
|
|
{ \
|
|
const vertex &v = verts[i]; \
|
|
body; \
|
|
f++; \
|
|
} \
|
|
} while(0)
|
|
|
|
void genverts(void *buf)
|
|
{
|
|
GENVERTS(vertex, buf, { *f = v; f->norm.flip(); f->tangent.flip(); });
|
|
}
|
|
|
|
void gendecal(const extentity &e, DecalSlot &s, const decalkey &key)
|
|
{
|
|
matrix3 orient;
|
|
orient.identity();
|
|
if(e.attr2) orient.rotate_around_z(sincosmod360(e.attr2));
|
|
if(e.attr3) orient.rotate_around_x(sincosmod360(e.attr3));
|
|
if(e.attr4) orient.rotate_around_y(sincosmod360(-e.attr4));
|
|
vec size(max(float(e.attr5), 1.0f));
|
|
size.y *= s.depth;
|
|
if(!s.sts.empty())
|
|
{
|
|
Texture *t = s.sts[0].t;
|
|
if(t->xs < t->ys) size.x *= t->xs / float(t->ys);
|
|
else if(t->xs > t->ys) size.z *= t->ys / float(t->xs);
|
|
}
|
|
vec center = orient.transform(vec(0, size.y*0.5f, 0)).add(e.o), radius = orient.abstransform(vec(size).mul(0.5f));
|
|
vec bbmin = vec(center).sub(radius), bbmax = vec(center).add(radius);
|
|
vec clipoffset = orient.transposedtransform(center).msub(size, 0.5f);
|
|
loopv(texs)
|
|
{
|
|
const sortkey &k = texs[i];
|
|
if(k.layer == LAYER_BLEND || k.alpha != NO_ALPHA) continue;
|
|
const sortval &t = indices[k];
|
|
if(t.tris.empty()) continue;
|
|
decalkey tkey(key);
|
|
if(shouldreuseparams(s, lookupvslot(k.tex, false))) tkey.reuse = k.tex;
|
|
for(int j = 0; j < t.tris.length(); j += 3)
|
|
{
|
|
const vertex &t0 = verts[t.tris[j]], &t1 = verts[t.tris[j+1]], &t2 = verts[t.tris[j+2]];
|
|
vec v0 = t0.pos, v1 = t1.pos, v2 = t2.pos;
|
|
vec tmin = vec(v0).min(v1).min(v2), tmax = vec(v0).max(v1).max(v2);
|
|
if(tmin.x >= bbmax.x || tmin.y >= bbmax.y || tmin.z >= bbmax.z ||
|
|
tmax.x <= bbmin.x || tmax.y <= bbmin.y || tmax.z <= bbmin.z)
|
|
continue;
|
|
float f0 = t0.norm.tonormal().dot(orient.b), f1 = t1.norm.tonormal().dot(orient.b), f2 = t2.norm.tonormal().dot(orient.b);
|
|
if(f0 >= 0 && f1 >= 0 && f2 >= 0) continue;
|
|
vec p1[9], p2[9];
|
|
p1[0] = v0; p1[1] = v1; p1[2] = v2;
|
|
int nump = polyclip(p1, 3, orient.b, clipoffset.y, clipoffset.y + size.y, p2);
|
|
if(nump < 3) continue;
|
|
nump = polyclip(p2, nump, orient.a, clipoffset.x, clipoffset.x + size.x, p1);
|
|
if(nump < 3) continue;
|
|
nump = polyclip(p1, nump, orient.c, clipoffset.z, clipoffset.z + size.z, p2);
|
|
if(nump < 3) continue;
|
|
|
|
bvec4 n0 = t0.norm, n1 = t1.norm, n2 = t2.norm,
|
|
x0 = t0.tangent, x1 = t1.tangent, x2 = t2.tangent;
|
|
vec e1 = vec(v1).sub(v0), e2 = vec(v2).sub(v0);
|
|
float d11 = e1.dot(e1), d12 = e1.dot(e2), d22 = e2.dot(e2);
|
|
int idx[9];
|
|
loopk(nump)
|
|
{
|
|
vertex v;
|
|
v.pos = p2[k];
|
|
vec ep = vec(v.pos).sub(v0);
|
|
float dp1 = ep.dot(e1), dp2 = ep.dot(e2), denom = d11*d22 - d12*d12,
|
|
b1 = (d22*dp1 - d12*dp2) / denom,
|
|
b2 = (d11*dp2 - d12*dp1) / denom,
|
|
b0 = 1 - b1 - b2;
|
|
v.norm.lerp(n0, n1, n2, b0, b1, b2);
|
|
v.norm.w = uchar(127.5f - 127.5f*(f0*b0 + f1*b1 + f2*b2));
|
|
vec tc = orient.transposedtransform(vec(center).sub(v.pos)).div(size).add(0.5f);
|
|
v.tc = vec(tc.x, tc.z, s.fade ? tc.y * s.depth / s.fade : 1.0f);
|
|
v.tangent.lerp(x0, x1, x2, b0, b1, b2);
|
|
idx[k] = addvert(v);
|
|
}
|
|
vector<ushort> &tris = decalindices[tkey].tris;
|
|
loopk(nump-2) if(idx[0] != idx[k+1] && idx[k+1] != idx[k+2] && idx[k+2] != idx[0])
|
|
{
|
|
tris.add(idx[0]);
|
|
tris.add(idx[k+1]);
|
|
tris.add(idx[k+2]);
|
|
decaltris += 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void gendecals()
|
|
{
|
|
if(decals.length()) extdecals.put(decals.getbuf(), decals.length());
|
|
if(extdecals.empty()) return;
|
|
vector<extentity *> &ents = entities::getents();
|
|
loopv(extdecals)
|
|
{
|
|
octaentities *oe = extdecals[i];
|
|
loopvj(oe->decals)
|
|
{
|
|
extentity &e = *ents[oe->decals[j]];
|
|
if(e.flags&EF_RENDER) continue;
|
|
e.flags |= EF_RENDER;
|
|
DecalSlot &s = lookupdecalslot(e.attr1, true);
|
|
if(!s.shader) continue;
|
|
ushort envmap = s.shader->type&SHADER_ENVMAP ? (s.texmask&(1<<TEX_ENVMAP) ? EMID_CUSTOM : closestenvmap(e.o)) : EMID_NONE;
|
|
decalkey k(e.attr1, envmap);
|
|
gendecal(e, s, k);
|
|
}
|
|
}
|
|
loopv(extdecals)
|
|
{
|
|
octaentities *oe = extdecals[i];
|
|
loopvj(oe->decals)
|
|
{
|
|
extentity &e = *ents[oe->decals[j]];
|
|
if(e.flags&EF_RENDER) e.flags &= ~EF_RENDER;
|
|
}
|
|
}
|
|
enumeratekt(decalindices, decalkey, k, sortval, t,
|
|
{
|
|
if(t.tris.length()) decaltexs.add(k);
|
|
});
|
|
decaltexs.sort(decalkey::sort);
|
|
}
|
|
|
|
void setupdata(vtxarray *va)
|
|
{
|
|
optimize();
|
|
gendecals();
|
|
|
|
va->verts = verts.length();
|
|
va->tris = worldtris/3;
|
|
va->vbuf = 0;
|
|
va->vdata = 0;
|
|
va->minvert = 0;
|
|
va->maxvert = va->verts-1;
|
|
va->voffset = 0;
|
|
if(va->verts)
|
|
{
|
|
if(vbosize[VBO_VBUF] + verts.length() > maxvbosize ||
|
|
vbosize[VBO_EBUF] + worldtris > USHRT_MAX ||
|
|
vbosize[VBO_SKYBUF] + skytris > USHRT_MAX ||
|
|
vbosize[VBO_DECALBUF] + decaltris > USHRT_MAX)
|
|
flushvbo();
|
|
|
|
uchar *vdata = addvbo(va, VBO_VBUF, va->verts, sizeof(vertex));
|
|
genverts(vdata);
|
|
va->minvert += va->voffset;
|
|
va->maxvert += va->voffset;
|
|
}
|
|
|
|
va->matbuf = nullptr;
|
|
va->matsurfs = matsurfs.length();
|
|
va->matmask = 0;
|
|
if(va->matsurfs)
|
|
{
|
|
va->matbuf = new materialsurface[matsurfs.length()];
|
|
memcpy(va->matbuf, matsurfs.getbuf(), matsurfs.length()*sizeof(materialsurface));
|
|
loopv(matsurfs)
|
|
{
|
|
materialsurface &m = matsurfs[i];
|
|
if(m.visible == MATSURF_EDIT_ONLY) continue;
|
|
switch(m.material)
|
|
{
|
|
case MAT_GLASS: case MAT_LAVA: case MAT_WATER: break;
|
|
default: continue;
|
|
}
|
|
va->matmask |= 1<<m.material;
|
|
}
|
|
}
|
|
|
|
va->skybuf = 0;
|
|
va->skydata = 0;
|
|
va->skyoffset = 0;
|
|
va->sky = skyindices.length();
|
|
if(va->sky)
|
|
{
|
|
ushort *skydata = (ushort *)addvbo(va, VBO_SKYBUF, va->sky, sizeof(ushort));
|
|
memcpy(skydata, skyindices.getbuf(), va->sky*sizeof(ushort));
|
|
if(va->voffset) loopi(va->sky) skydata[i] += va->voffset;
|
|
}
|
|
|
|
va->texelems = nullptr;
|
|
va->texs = texs.length();
|
|
va->blendtris = 0;
|
|
va->blends = 0;
|
|
va->alphabacktris = 0;
|
|
va->alphaback = 0;
|
|
va->alphafronttris = 0;
|
|
va->alphafront = 0;
|
|
va->refracttris = 0;
|
|
va->refract = 0;
|
|
va->ebuf = 0;
|
|
va->edata = 0;
|
|
va->eoffset = 0;
|
|
va->texmask = 0;
|
|
va->dyntexs = 0;
|
|
va->dynalphatexs = 0;
|
|
if(va->texs)
|
|
{
|
|
va->texelems = new elementset[va->texs];
|
|
ushort *edata = (ushort *)addvbo(va, VBO_EBUF, worldtris, sizeof(ushort)), *curbuf = edata;
|
|
loopv(texs)
|
|
{
|
|
const sortkey &k = texs[i];
|
|
const sortval &t = indices[k];
|
|
elementset &e = va->texelems[i];
|
|
e.texture = k.tex;
|
|
e.orient = k.orient;
|
|
e.layer = k.layer;
|
|
e.envmap = k.envmap;
|
|
ushort *startbuf = curbuf;
|
|
e.minvert = USHRT_MAX;
|
|
e.maxvert = 0;
|
|
|
|
if(t.tris.length())
|
|
{
|
|
memcpy(curbuf, t.tris.getbuf(), t.tris.length() * sizeof(ushort));
|
|
|
|
loopvj(t.tris)
|
|
{
|
|
curbuf[j] += va->voffset;
|
|
e.minvert = min(e.minvert, curbuf[j]);
|
|
e.maxvert = max(e.maxvert, curbuf[j]);
|
|
}
|
|
|
|
curbuf += t.tris.length();
|
|
}
|
|
e.length = curbuf-startbuf;
|
|
|
|
if(k.layer==LAYER_BLEND) { va->texs--; va->tris -= e.length/3; va->blends++; va->blendtris += e.length/3; }
|
|
else if(k.alpha==ALPHA_BACK) { va->texs--; va->tris -= e.length/3; va->alphaback++; va->alphabacktris += e.length/3; }
|
|
else if(k.alpha==ALPHA_FRONT) { va->texs--; va->tris -= e.length/3; va->alphafront++; va->alphafronttris += e.length/3; }
|
|
else if(k.alpha==ALPHA_REFRACT) { va->texs--; va->tris -= e.length/3; va->refract++; va->refracttris += e.length/3; }
|
|
|
|
VSlot &vslot = lookupvslot(k.tex, false);
|
|
if(vslot.isdynamic())
|
|
{
|
|
va->dyntexs++;
|
|
if(k.alpha) va->dynalphatexs++;
|
|
}
|
|
Slot &slot = *vslot.slot;
|
|
loopvj(slot.sts) va->texmask |= 1<<slot.sts[j].type;
|
|
if(slot.shader->type&SHADER_ENVMAP) va->texmask |= 1<<TEX_ENVMAP;
|
|
}
|
|
}
|
|
|
|
va->alphatris = va->alphabacktris + va->alphafronttris + va->refracttris;
|
|
|
|
va->decalbuf = 0;
|
|
va->decaldata = 0;
|
|
va->decaloffset = 0;
|
|
va->decalelems = nullptr;
|
|
va->decaltexs = decaltexs.length();
|
|
va->decaltris = decaltris/3;
|
|
if(va->decaltexs)
|
|
{
|
|
va->decalelems = new elementset[va->decaltexs];
|
|
ushort *edata = (ushort *)addvbo(va, VBO_DECALBUF, decaltris, sizeof(ushort)), *curbuf = edata;
|
|
loopv(decaltexs)
|
|
{
|
|
const decalkey &k = decaltexs[i];
|
|
const sortval &t = decalindices[k];
|
|
elementset &e = va->decalelems[i];
|
|
e.texture = k.tex;
|
|
e.reuse = k.reuse;
|
|
e.envmap = k.envmap;
|
|
ushort *startbuf = curbuf;
|
|
e.minvert = USHRT_MAX;
|
|
e.maxvert = 0;
|
|
|
|
if(t.tris.length())
|
|
{
|
|
memcpy(curbuf, t.tris.getbuf(), t.tris.length() * sizeof(ushort));
|
|
|
|
loopvj(t.tris)
|
|
{
|
|
curbuf[j] += va->voffset;
|
|
e.minvert = min(e.minvert, curbuf[j]);
|
|
e.maxvert = max(e.maxvert, curbuf[j]);
|
|
}
|
|
|
|
curbuf += t.tris.length();
|
|
}
|
|
e.length = curbuf-startbuf;
|
|
}
|
|
}
|
|
|
|
if(grasstris.length())
|
|
{
|
|
va->grasstris.move(grasstris);
|
|
loadgrassshaders();
|
|
}
|
|
|
|
if(mapmodels.length()) va->mapmodels.put(mapmodels.getbuf(), mapmodels.length());
|
|
if(decals.length()) va->decals.put(decals.getbuf(), decals.length());
|
|
}
|
|
|
|
bool emptyva()
|
|
{
|
|
return verts.empty() && matsurfs.empty() && skyindices.empty() && grasstris.empty() && mapmodels.empty() && decals.empty();
|
|
}
|
|
} vc;
|
|
|
|
static int recalcprogress = 0;
|
|
#define progress(s) if((recalcprogress++&0xFFF)==0) renderprogress(recalcprogress/(float)allocnodes, s);
|
|
|
|
vector<tjoint> tjoints;
|
|
|
|
VARFP(filltjoints, 0, 1, 1, allchanged());
|
|
|
|
void reduceslope(ivec &n)
|
|
{
|
|
int mindim = -1, minval = 64;
|
|
loopi(3) if(n[i])
|
|
{
|
|
int val = abs(n[i]);
|
|
if(mindim < 0 || val < minval)
|
|
{
|
|
mindim = i;
|
|
minval = val;
|
|
}
|
|
}
|
|
if(!(n[R[mindim]]%minval) && !(n[C[mindim]]%minval)) n.div(minval);
|
|
while(!((n.x|n.y|n.z)&1)) n.shr(1);
|
|
}
|
|
|
|
// [rotation][orient]
|
|
extern const vec orientation_tangent[8][6] =
|
|
{
|
|
{ vec( 0, 1, 0), vec( 0, -1, 0), vec(-1, 0, 0), vec( 1, 0, 0), vec( 1, 0, 0), vec( 1, 0, 0) },
|
|
{ vec( 0, 0, -1), vec( 0, 0, -1), vec( 0, 0, -1), vec( 0, 0, -1), vec( 0, -1, 0), vec( 0, 1, 0) },
|
|
{ vec( 0, -1, 0), vec( 0, 1, 0), vec( 1, 0, 0), vec(-1, 0, 0), vec(-1, 0, 0), vec(-1, 0, 0) },
|
|
{ vec( 0, 0, 1), vec( 0, 0, 1), vec( 0, 0, 1), vec( 0, 0, 1), vec( 0, 1, 0), vec( 0, -1, 0) },
|
|
{ vec( 0, -1, 0), vec( 0, 1, 0), vec( 1, 0, 0), vec(-1, 0, 0), vec(-1, 0, 0), vec(-1, 0, 0) },
|
|
{ vec( 0, 1, 0), vec( 0, -1, 0), vec(-1, 0, 0), vec( 1, 0, 0), vec( 1, 0, 0), vec( 1, 0, 0) },
|
|
{ vec( 0, 0, -1), vec( 0, 0, -1), vec( 0, 0, -1), vec( 0, 0, -1), vec( 0, -1, 0), vec( 0, 1, 0) },
|
|
{ vec( 0, 0, 1), vec( 0, 0, 1), vec( 0, 0, 1), vec( 0, 0, 1), vec( 0, 1, 0), vec( 0, -1, 0) },
|
|
};
|
|
extern const vec orientation_bitangent[8][6] =
|
|
{
|
|
{ vec( 0, 0, -1), vec( 0, 0, -1), vec( 0, 0, -1), vec( 0, 0, -1), vec( 0, -1, 0), vec( 0, 1, 0) },
|
|
{ vec( 0, -1, 0), vec( 0, 1, 0), vec( 1, 0, 0), vec(-1, 0, 0), vec(-1, 0, 0), vec(-1, 0, 0) },
|
|
{ vec( 0, 0, 1), vec( 0, 0, 1), vec( 0, 0, 1), vec( 0, 0, 1), vec( 0, 1, 0), vec( 0, -1, 0) },
|
|
{ vec( 0, 1, 0), vec( 0, -1, 0), vec(-1, 0, 0), vec( 1, 0, 0), vec( 1, 0, 0), vec( 1, 0, 0) },
|
|
{ vec( 0, 0, -1), vec( 0, 0, -1), vec( 0, 0, -1), vec( 0, 0, -1), vec( 0, -1, 0), vec( 0, 1, 0) },
|
|
{ vec( 0, 0, 1), vec( 0, 0, 1), vec( 0, 0, 1), vec( 0, 0, 1), vec( 0, 1, 0), vec( 0, -1, 0) },
|
|
{ vec( 0, 1, 0), vec( 0, -1, 0), vec(-1, 0, 0), vec( 1, 0, 0), vec( 1, 0, 0), vec( 1, 0, 0) },
|
|
{ vec( 0, -1, 0), vec( 0, 1, 0), vec( 1, 0, 0), vec(-1, 0, 0), vec(-1, 0, 0), vec(-1, 0, 0) },
|
|
};
|
|
|
|
static void addtris(VSlot &vslot, int orient, const sortkey &key, vertex *verts, int *index, int numverts, int convex, int tj)
|
|
{
|
|
int &total = key.tex==DEFAULT_SKY ? vc.skytris : vc.worldtris;
|
|
int edge = orient*(MAXFACEVERTS+1);
|
|
loopi(numverts-2) if(index[0]!=index[i+1] && index[i+1]!=index[i+2] && index[i+2]!=index[0])
|
|
{
|
|
vector<ushort> &idxs = key.tex==DEFAULT_SKY ? vc.skyindices : vc.indices[key].tris;
|
|
int left = index[0], mid = index[i+1], right = index[i+2], start = left, i0 = left, i1 = -1;
|
|
loopk(4)
|
|
{
|
|
int i2 = -1, ctj = -1, cedge = -1;
|
|
switch(k)
|
|
{
|
|
case 1: i1 = i2 = mid; cedge = edge+i+1; break;
|
|
case 2: if(i1 != mid || i0 == left) { i0 = i1; i1 = right; } i2 = right; if(i+1 == numverts-2) cedge = edge+i+2; break;
|
|
case 3: if(i0 == start) { i0 = i1; i1 = left; } i2 = left; // fall-through
|
|
default: if(!i) cedge = edge; break;
|
|
}
|
|
if(i1 != i2)
|
|
{
|
|
if(total + 3 > USHRT_MAX) return;
|
|
total += 3;
|
|
idxs.add(i0);
|
|
idxs.add(i1);
|
|
idxs.add(i2);
|
|
i1 = i2;
|
|
}
|
|
if(cedge >= 0)
|
|
{
|
|
for(ctj = tj;;)
|
|
{
|
|
if(ctj < 0) break;
|
|
if(tjoints[ctj].edge < cedge) { ctj = tjoints[ctj].next; continue; }
|
|
if(tjoints[ctj].edge != cedge) ctj = -1;
|
|
break;
|
|
}
|
|
}
|
|
if(ctj >= 0)
|
|
{
|
|
int e1 = cedge%(MAXFACEVERTS+1), e2 = (e1+1)%numverts;
|
|
vertex &v1 = verts[e1], &v2 = verts[e2];
|
|
ivec d(vec(v2.pos).sub(v1.pos).mul(8));
|
|
int axis = abs(d.x) > abs(d.y) ? (abs(d.x) > abs(d.z) ? 0 : 2) : (abs(d.y) > abs(d.z) ? 1 : 2);
|
|
if(d[axis] < 0) d.neg();
|
|
reduceslope(d);
|
|
int origin = int(min(v1.pos[axis], v2.pos[axis])*8)&~0x7FFF,
|
|
offset1 = (int(v1.pos[axis]*8) - origin) / d[axis],
|
|
offset2 = (int(v2.pos[axis]*8) - origin) / d[axis];
|
|
vec o = vec(v1.pos).sub(vec(d).mul(offset1/8.0f));
|
|
float doffset = 1.0f / (offset2 - offset1);
|
|
|
|
if(i1 < 0) for(;;)
|
|
{
|
|
tjoint &t = tjoints[ctj];
|
|
if(t.next < 0 || tjoints[t.next].edge != cedge) break;
|
|
ctj = t.next;
|
|
}
|
|
while(ctj >= 0)
|
|
{
|
|
tjoint &t = tjoints[ctj];
|
|
if(t.edge != cedge) break;
|
|
float offset = (t.offset - offset1) * doffset;
|
|
vertex vt;
|
|
vt.pos = vec(d).mul(t.offset/8.0f).add(o);
|
|
vt.tc.lerp(v1.tc, v2.tc, offset);
|
|
vt.norm.lerp(v1.norm, v2.norm, offset);
|
|
vt.tangent.lerp(v1.tangent, v2.tangent, offset);
|
|
if(v1.tangent.w != v2.tangent.w)
|
|
vt.tangent.w = orientation_bitangent[vslot.rotation][orient].scalartriple(vt.norm.tonormal(), vt.tangent.tonormal()) < 0 ? 0 : 255;
|
|
int i2 = vc.addvert(vt);
|
|
if(i2 < 0) return;
|
|
if(i1 >= 0)
|
|
{
|
|
if(total + 3 > USHRT_MAX) return;
|
|
total += 3;
|
|
idxs.add(i0);
|
|
idxs.add(i1);
|
|
idxs.add(i2);
|
|
i1 = i2;
|
|
}
|
|
else start = i0 = i2;
|
|
ctj = t.next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void addgrasstri(int face, vertex *verts, int numv, ushort texture, int layer)
|
|
{
|
|
grasstri &g = vc.grasstris.add();
|
|
int i1, i2, i3, i4;
|
|
if(numv <= 3 && face%2) { i1 = face+1; i2 = face+2; i3 = i4 = 0; }
|
|
else { i1 = 0; i2 = face+1; i3 = face+2; i4 = numv > 3 ? face+3 : i3; }
|
|
g.v[0] = verts[i1].pos;
|
|
g.v[1] = verts[i2].pos;
|
|
g.v[2] = verts[i3].pos;
|
|
g.v[3] = verts[i4].pos;
|
|
g.numv = numv;
|
|
|
|
g.surface.toplane(g.v[0], g.v[1], g.v[2]);
|
|
if(g.surface.z <= 0) { vc.grasstris.pop(); return; }
|
|
|
|
g.minz = min(min(g.v[0].z, g.v[1].z), min(g.v[2].z, g.v[3].z));
|
|
g.maxz = max(max(g.v[0].z, g.v[1].z), max(g.v[2].z, g.v[3].z));
|
|
|
|
g.center = vec(0, 0, 0);
|
|
loopk(numv) g.center.add(g.v[k]);
|
|
g.center.div(numv);
|
|
g.radius = 0;
|
|
loopk(numv) g.radius = max(g.radius, g.v[k].dist(g.center));
|
|
|
|
g.texture = texture;
|
|
g.blend = layer == LAYER_BLEND ? ((int(g.center.x)>>12)+1) | (((int(g.center.y)>>12)+1)<<8) : 0;
|
|
}
|
|
|
|
static inline void calctexgen(VSlot &vslot, int orient, vec4 &sgen, vec4 &tgen)
|
|
{
|
|
Texture *tex = vslot.slot->sts.empty() ? notexture : vslot.slot->sts[0].t;
|
|
const texrotation &r = texrotations[vslot.rotation];
|
|
float k = TEX_SCALE/vslot.scale,
|
|
xs = r.flipx ? -tex->xs : tex->xs,
|
|
ys = r.flipy ? -tex->ys : tex->ys,
|
|
sk = k/xs, tk = k/ys,
|
|
soff = -(r.swapxy ? vslot.offset.y : vslot.offset.x)/xs,
|
|
toff = -(r.swapxy ? vslot.offset.x : vslot.offset.y)/ys;
|
|
sgen = vec4(0, 0, 0, soff);
|
|
tgen = vec4(0, 0, 0, toff);
|
|
if(r.swapxy) switch(orient)
|
|
{
|
|
case 0: sgen.z = -sk; tgen.y = tk; break;
|
|
case 1: sgen.z = -sk; tgen.y = -tk; break;
|
|
case 2: sgen.z = -sk; tgen.x = -tk; break;
|
|
case 3: sgen.z = -sk; tgen.x = tk; break;
|
|
case 4: sgen.y = -sk; tgen.x = tk; break;
|
|
case 5: sgen.y = sk; tgen.x = tk; break;
|
|
}
|
|
else switch(orient)
|
|
{
|
|
case 0: sgen.y = sk; tgen.z = -tk; break;
|
|
case 1: sgen.y = -sk; tgen.z = -tk; break;
|
|
case 2: sgen.x = -sk; tgen.z = -tk; break;
|
|
case 3: sgen.x = sk; tgen.z = -tk; break;
|
|
case 4: sgen.x = sk; tgen.y = -tk; break;
|
|
case 5: sgen.x = sk; tgen.y = tk; break;
|
|
}
|
|
}
|
|
|
|
ushort encodenormal(const vec &n)
|
|
{
|
|
if(n.iszero()) return 0;
|
|
int yaw = int(-atan2(n.x, n.y)/RAD), pitch = int(asin(n.z)/RAD);
|
|
return ushort(std::clamp(pitch + 90, 0, 180)*360 + (yaw < 0 ? yaw%360 + 360 : yaw%360) + 1);
|
|
}
|
|
|
|
static vec decodenormal(ushort norm)
|
|
{
|
|
if(!norm) return vec(0, 0, 1);
|
|
norm--;
|
|
const vec2 &yaw = sincos360[norm%360], &pitch = sincos360[norm/360+270];
|
|
return vec(-yaw.y*pitch.x, yaw.x*pitch.x, pitch.y);
|
|
}
|
|
|
|
void guessnormals(const vec *pos, int numverts, vec *normals)
|
|
{
|
|
vec n1, n2;
|
|
n1.cross(pos[0], pos[1], pos[2]);
|
|
if(numverts != 4)
|
|
{
|
|
n1.normalize();
|
|
loopk(numverts) normals[k] = n1;
|
|
return;
|
|
}
|
|
n2.cross(pos[0], pos[2], pos[3]);
|
|
if(n1.iszero())
|
|
{
|
|
n2.normalize();
|
|
loopk(4) normals[k] = n2;
|
|
return;
|
|
}
|
|
else n1.normalize();
|
|
if(n2.iszero())
|
|
{
|
|
loopk(4) normals[k] = n1;
|
|
return;
|
|
}
|
|
else n2.normalize();
|
|
vec avg = vec(n1).add(n2).normalize();
|
|
normals[0] = avg;
|
|
normals[1] = n1;
|
|
normals[2] = avg;
|
|
normals[3] = n2;
|
|
}
|
|
|
|
static void addcubeverts(VSlot &vslot, int orient, int size, vec *pos, int convex, ushort texture, vertinfo *vinfo, int numverts, int tj = -1, ushort envmap = EMID_NONE, int grassy = 0, bool alpha = false, int layer = LAYER_TOP)
|
|
{
|
|
vec4 sgen, tgen;
|
|
calctexgen(vslot, orient, sgen, tgen);
|
|
vertex verts[MAXFACEVERTS];
|
|
int index[MAXFACEVERTS];
|
|
vec normals[MAXFACEVERTS];
|
|
loopk(numverts)
|
|
{
|
|
vertex &v = verts[k];
|
|
v.pos = pos[k];
|
|
v.tc = vec(sgen.dot(v.pos), tgen.dot(v.pos), 0);
|
|
if(vinfo && vinfo[k].norm)
|
|
{
|
|
vec n = decodenormal(vinfo[k].norm), t = orientation_tangent[vslot.rotation][orient];
|
|
t.project(n).normalize();
|
|
v.norm = bvec(n);
|
|
v.tangent = bvec4(bvec(t), orientation_bitangent[vslot.rotation][orient].scalartriple(n, t) < 0 ? 0 : 255);
|
|
}
|
|
else if(texture != DEFAULT_SKY)
|
|
{
|
|
if(!k) guessnormals(pos, numverts, normals);
|
|
const vec &n = normals[k];
|
|
vec t = orientation_tangent[vslot.rotation][orient];
|
|
t.project(n).normalize();
|
|
v.norm = bvec(n);
|
|
v.tangent = bvec4(bvec(t), orientation_bitangent[vslot.rotation][orient].scalartriple(n, t) < 0 ? 0 : 255);
|
|
}
|
|
else
|
|
{
|
|
v.norm = bvec(128, 128, 255);
|
|
v.tangent = bvec4(255, 128, 128, 255);
|
|
}
|
|
index[k] = vc.addvert(v);
|
|
if(index[k] < 0) return;
|
|
}
|
|
|
|
if(alpha)
|
|
{
|
|
loopk(numverts) { vc.alphamin.min(pos[k]); vc.alphamax.max(pos[k]); }
|
|
if(vslot.refractscale > 0) loopk(numverts) { vc.refractmin.min(pos[k]); vc.refractmax.max(pos[k]); }
|
|
}
|
|
if(texture == DEFAULT_SKY) loopi(numverts) if(pos[i][orient>>1] != ((orient&1)<<worldscale))
|
|
{
|
|
loopk(numverts) { vc.skymin.min(pos[k]); vc.skymax.max(pos[k]); }
|
|
break;
|
|
}
|
|
|
|
sortkey key(texture, vslot.scroll.iszero() ? O_ANY : orient, layer&LAYER_BOTTOM ? layer : LAYER_TOP, envmap, alpha ? (vslot.refractscale > 0 ? ALPHA_REFRACT : (vslot.alphaback ? ALPHA_BACK : ALPHA_FRONT)) : NO_ALPHA);
|
|
addtris(vslot, orient, key, verts, index, numverts, convex, tj);
|
|
|
|
if(grassy)
|
|
{
|
|
for(int i = 0; i < numverts-2; i += 2)
|
|
{
|
|
int faces = 0;
|
|
if(index[0]!=index[i+1] && index[i+1]!=index[i+2] && index[i+2]!=index[0]) faces |= 1;
|
|
if(i+3 < numverts && index[0]!=index[i+2] && index[i+2]!=index[i+3] && index[i+3]!=index[0]) faces |= 2;
|
|
if(grassy > 1 && faces==3) addgrasstri(i, verts, 4, texture, layer);
|
|
else
|
|
{
|
|
if(faces&1) addgrasstri(i, verts, 3, texture, layer);
|
|
if(faces&2) addgrasstri(i+1, verts, 3, texture, layer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct edgegroup
|
|
{
|
|
ivec slope, origin;
|
|
int axis;
|
|
};
|
|
|
|
static inline uint hthash(const edgegroup &g)
|
|
{
|
|
return g.slope.x^(g.slope.y<<2)^(g.slope.z<<4)^g.origin.x^g.origin.y^g.origin.z;
|
|
}
|
|
|
|
static inline bool htcmp(const edgegroup &x, const edgegroup &y)
|
|
{
|
|
return x.slope==y.slope && x.origin==y.origin;
|
|
}
|
|
|
|
enum
|
|
{
|
|
CE_START = 1<<0,
|
|
CE_END = 1<<1,
|
|
CE_FLIP = 1<<2,
|
|
CE_DUP = 1<<3
|
|
};
|
|
|
|
struct cubeedge
|
|
{
|
|
cube *c;
|
|
int next, offset;
|
|
ushort size;
|
|
uchar index, flags;
|
|
};
|
|
|
|
static vector<cubeedge> cubeedges;
|
|
static hashtable<edgegroup, int> edgegroups(1<<13);
|
|
|
|
static void gencubeedges(cube &c, const ivec &co, int size)
|
|
{
|
|
ivec pos[MAXFACEVERTS];
|
|
int vis;
|
|
loopi(6) if((vis = visibletris(c, i, co, size)))
|
|
{
|
|
int numverts = c.ext ? c.ext->surfaces[i].numverts&MAXFACEVERTS : 0;
|
|
if(numverts)
|
|
{
|
|
vertinfo *verts = c.ext->verts() + c.ext->surfaces[i].verts;
|
|
ivec vo = ivec(co).mask(~0xFFF).shl(3);
|
|
loopj(numverts)
|
|
{
|
|
vertinfo &v = verts[j];
|
|
pos[j] = ivec(v.x, v.y, v.z).add(vo);
|
|
}
|
|
}
|
|
else if(c.merged&(1<<i)) continue;
|
|
else
|
|
{
|
|
ivec v[4];
|
|
genfaceverts(c, i, v);
|
|
int order = vis&4 || (!flataxisface(c, i) && faceconvexity(v) < 0) ? 1 : 0;
|
|
ivec vo = ivec(co).shl(3);
|
|
pos[numverts++] = v[order].mul(size).add(vo);
|
|
if(vis&1) pos[numverts++] = v[order+1].mul(size).add(vo);
|
|
pos[numverts++] = v[order+2].mul(size).add(vo);
|
|
if(vis&2) pos[numverts++] = v[(order+3)&3].mul(size).add(vo);
|
|
}
|
|
loopj(numverts)
|
|
{
|
|
int e1 = j, e2 = j+1 < numverts ? j+1 : 0;
|
|
ivec d = pos[e2];
|
|
d.sub(pos[e1]);
|
|
if(d.iszero()) continue;
|
|
int axis = abs(d.x) > abs(d.y) ? (abs(d.x) > abs(d.z) ? 0 : 2) : (abs(d.y) > abs(d.z) ? 1 : 2);
|
|
if(d[axis] < 0)
|
|
{
|
|
d.neg();
|
|
swap(e1, e2);
|
|
}
|
|
reduceslope(d);
|
|
|
|
int t1 = pos[e1][axis]/d[axis],
|
|
t2 = pos[e2][axis]/d[axis];
|
|
edgegroup g;
|
|
g.origin = ivec(pos[e1]).sub(ivec(d).mul(t1));
|
|
g.slope = d;
|
|
g.axis = axis;
|
|
cubeedge ce;
|
|
ce.c = &c;
|
|
ce.offset = t1;
|
|
ce.size = t2 - t1;
|
|
ce.index = i*(MAXFACEVERTS+1)+j;
|
|
ce.flags = CE_START | CE_END | (e1!=j ? CE_FLIP : 0);
|
|
ce.next = -1;
|
|
|
|
bool insert = true;
|
|
int *exists = edgegroups.access(g);
|
|
if(exists)
|
|
{
|
|
int prev = -1, cur = *exists;
|
|
while(cur >= 0)
|
|
{
|
|
cubeedge &p = cubeedges[cur];
|
|
if(ce.offset <= p.offset+p.size)
|
|
{
|
|
if(ce.offset < p.offset) break;
|
|
if(p.flags&CE_DUP ?
|
|
ce.offset+ce.size <= p.offset+p.size :
|
|
ce.offset==p.offset && ce.size==p.size)
|
|
{
|
|
p.flags |= CE_DUP;
|
|
insert = false;
|
|
break;
|
|
}
|
|
if(ce.offset == p.offset+p.size) ce.flags &= ~CE_START;
|
|
}
|
|
prev = cur;
|
|
cur = p.next;
|
|
}
|
|
if(insert)
|
|
{
|
|
ce.next = cur;
|
|
while(cur >= 0)
|
|
{
|
|
cubeedge &p = cubeedges[cur];
|
|
if(ce.offset+ce.size==p.offset) { ce.flags &= ~CE_END; break; }
|
|
cur = p.next;
|
|
}
|
|
if(prev>=0) cubeedges[prev].next = cubeedges.length();
|
|
else *exists = cubeedges.length();
|
|
}
|
|
}
|
|
else edgegroups[g] = cubeedges.length();
|
|
|
|
if(insert) cubeedges.add(ce);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void gencubeedges(cube *c = worldroot, const ivec &co = ivec(0, 0, 0), int size = worldsize>>1)
|
|
{
|
|
progress("fixing t-joints...");
|
|
neighbourstack[++neighbourdepth] = c;
|
|
loopi(8)
|
|
{
|
|
ivec o(i, co, size);
|
|
if(c[i].ext) c[i].ext->tjoints = -1;
|
|
if(c[i].children) gencubeedges(c[i].children, o, size>>1);
|
|
else if(!isempty(c[i])) gencubeedges(c[i], o, size);
|
|
}
|
|
--neighbourdepth;
|
|
}
|
|
|
|
static void gencubeverts(cube &c, const ivec &co, int size, int csi)
|
|
{
|
|
if(!(c.visible&0xC0)) return;
|
|
|
|
int vismask = ~c.merged & 0x3F;
|
|
if(!(c.visible&0x80)) vismask &= c.visible;
|
|
if(!vismask) return;
|
|
|
|
int tj = filltjoints && c.ext ? c.ext->tjoints : -1, vis;
|
|
loopi(6) if(vismask&(1<<i) && (vis = visibletris(c, i, co, size)))
|
|
{
|
|
vec pos[MAXFACEVERTS];
|
|
vertinfo *verts = nullptr;
|
|
int numverts = c.ext ? c.ext->surfaces[i].numverts&MAXFACEVERTS : 0, convex = 0;
|
|
if(numverts)
|
|
{
|
|
verts = c.ext->verts() + c.ext->surfaces[i].verts;
|
|
vec vo(ivec(co).mask(~0xFFF));
|
|
loopj(numverts) pos[j] = vec(verts[j].getxyz()).mul(1.0f/8).add(vo);
|
|
if(!flataxisface(c, i)) convex = faceconvexity(verts, numverts, size);
|
|
}
|
|
else
|
|
{
|
|
ivec v[4];
|
|
genfaceverts(c, i, v);
|
|
if(!flataxisface(c, i)) convex = faceconvexity(v);
|
|
int order = vis&4 || convex < 0 ? 1 : 0;
|
|
vec vo(co);
|
|
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);
|
|
}
|
|
|
|
VSlot &vslot = lookupvslot(c.texture[i], true),
|
|
*layer = vslot.layer && !(c.material&MAT_ALPHA) ? &lookupvslot(vslot.layer, true) : nullptr;
|
|
ushort envmap = vslot.slot->shader->type&SHADER_ENVMAP ? (vslot.slot->texmask&(1<<TEX_ENVMAP) ? EMID_CUSTOM : closestenvmap(i, co, size)) : EMID_NONE,
|
|
envmap2 = layer && layer->slot->shader->type&SHADER_ENVMAP ? (layer->slot->texmask&(1<<TEX_ENVMAP) ? EMID_CUSTOM : closestenvmap(i, co, size)) : EMID_NONE;
|
|
while(tj >= 0 && tjoints[tj].edge < i*(MAXFACEVERTS+1)) tj = tjoints[tj].next;
|
|
int hastj = tj >= 0 && tjoints[tj].edge < (i+1)*(MAXFACEVERTS+1) ? tj : -1;
|
|
int grassy = vslot.slot->grass && i!=O_BOTTOM ? (vis!=3 || convex ? 1 : 2) : 0;
|
|
if(!c.ext)
|
|
addcubeverts(vslot, i, size, pos, convex, c.texture[i], nullptr, numverts, hastj, envmap, grassy, (c.material&MAT_ALPHA)!=0);
|
|
else
|
|
{
|
|
const surfaceinfo &surf = c.ext->surfaces[i];
|
|
if(!surf.numverts || surf.numverts&LAYER_TOP)
|
|
addcubeverts(vslot, i, size, pos, convex, c.texture[i], verts, numverts, hastj, envmap, grassy, (c.material&MAT_ALPHA)!=0, surf.numverts&LAYER_BLEND);
|
|
if(surf.numverts&LAYER_BOTTOM)
|
|
addcubeverts(layer ? *layer : vslot, i, size, pos, convex, vslot.layer, verts, numverts, hastj, envmap2, 0, false, surf.numverts&LAYER_TOP ? LAYER_BOTTOM : LAYER_TOP);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////// Vertex Arrays //////////////
|
|
|
|
int allocva = 0;
|
|
int wtris = 0, wverts = 0, vtris = 0, vverts = 0, glde = 0, gbatches = 0;
|
|
vector<vtxarray *> valist, varoot;
|
|
|
|
static vtxarray *newva(const ivec &o, int size)
|
|
{
|
|
vtxarray *va = new vtxarray;
|
|
va->parent = nullptr;
|
|
va->o = o;
|
|
va->size = size;
|
|
va->curvfc = VFC_NOT_VISIBLE;
|
|
va->occluded = OCCLUDE_NOTHING;
|
|
va->query = nullptr;
|
|
va->bbmin = va->alphamin = va->refractmin = va->skymin = ivec(-1, -1, -1);
|
|
va->bbmax = va->alphamax = va->refractmax = va->skymax = ivec(-1, -1, -1);
|
|
va->hasmerges = 0;
|
|
va->mergelevel = -1;
|
|
|
|
vc.setupdata(va);
|
|
|
|
if(va->alphatris)
|
|
{
|
|
va->alphamin = ivec(vec(vc.alphamin).mul(8)).shr(3);
|
|
va->alphamax = ivec(vec(vc.alphamax).mul(8)).add(7).shr(3);
|
|
}
|
|
|
|
if(va->refracttris)
|
|
{
|
|
va->refractmin = ivec(vec(vc.refractmin).mul(8)).shr(3);
|
|
va->refractmax = ivec(vec(vc.refractmax).mul(8)).add(7).shr(3);
|
|
}
|
|
|
|
if(va->sky && vc.skymax.x >= 0)
|
|
{
|
|
va->skymin = ivec(vec(vc.skymin).mul(8)).shr(3);
|
|
va->skymax = ivec(vec(vc.skymax).mul(8)).add(7).shr(3);
|
|
}
|
|
|
|
va->nogimin = vc.nogimin;
|
|
va->nogimax = vc.nogimax;
|
|
|
|
wverts += va->verts;
|
|
wtris += va->tris + va->blends + va->alphatris + va->decaltris;
|
|
allocva++;
|
|
valist.add(va);
|
|
|
|
return va;
|
|
}
|
|
|
|
void destroyva(vtxarray *va, bool reparent)
|
|
{
|
|
wverts -= va->verts;
|
|
wtris -= va->tris + va->blends + va->alphatris + va->decaltris;
|
|
allocva--;
|
|
valist.removeobj(va);
|
|
if(!va->parent) varoot.removeobj(va);
|
|
if(reparent)
|
|
{
|
|
if(va->parent) va->parent->children.removeobj(va);
|
|
loopv(va->children)
|
|
{
|
|
vtxarray *child = va->children[i];
|
|
child->parent = va->parent;
|
|
if(child->parent) child->parent->children.add(child);
|
|
}
|
|
}
|
|
if(va->vbuf) destroyvbo(va->vbuf);
|
|
if(va->ebuf) destroyvbo(va->ebuf);
|
|
if(va->skybuf) destroyvbo(va->skybuf);
|
|
if(va->decalbuf) destroyvbo(va->decalbuf);
|
|
if(va->texelems) delete[] va->texelems;
|
|
if(va->decalelems) delete[] va->decalelems;
|
|
if(va->matbuf) delete[] va->matbuf;
|
|
delete va;
|
|
}
|
|
|
|
void clearvas(cube *c)
|
|
{
|
|
loopi(8)
|
|
{
|
|
if(c[i].ext)
|
|
{
|
|
if(c[i].ext->va) destroyva(c[i].ext->va, false);
|
|
c[i].ext->va = nullptr;
|
|
c[i].ext->tjoints = -1;
|
|
}
|
|
if(c[i].children) clearvas(c[i].children);
|
|
}
|
|
}
|
|
|
|
ivec worldmin(0, 0, 0), worldmax(0, 0, 0), nogimin(0, 0, 0), nogimax(0, 0, 0);
|
|
|
|
void updatevabb(vtxarray *va, bool force)
|
|
{
|
|
if(!force && va->bbmin.x >= 0) return;
|
|
|
|
va->bbmin = va->geommin;
|
|
va->bbmax = va->geommax;
|
|
va->bbmin.min(va->lavamin);
|
|
va->bbmax.max(va->lavamax);
|
|
va->bbmin.min(va->watermin);
|
|
va->bbmax.max(va->watermax);
|
|
va->bbmin.min(va->glassmin);
|
|
va->bbmax.max(va->glassmax);
|
|
loopv(va->children)
|
|
{
|
|
vtxarray *child = va->children[i];
|
|
updatevabb(child, force);
|
|
va->bbmin.min(child->bbmin);
|
|
va->bbmax.max(child->bbmax);
|
|
}
|
|
loopv(va->mapmodels)
|
|
{
|
|
octaentities *oe = va->mapmodels[i];
|
|
va->bbmin.min(oe->bbmin);
|
|
va->bbmax.max(oe->bbmax);
|
|
}
|
|
loopv(va->decals)
|
|
{
|
|
octaentities *oe = va->decals[i];
|
|
va->bbmin.min(oe->bbmin);
|
|
va->bbmax.max(oe->bbmax);
|
|
}
|
|
va->bbmin.max(va->o);
|
|
va->bbmax.min(ivec(va->o).add(va->size));
|
|
worldmin.min(va->bbmin);
|
|
worldmax.max(va->bbmax);
|
|
nogimin.min(va->nogimin);
|
|
nogimax.max(va->nogimax);
|
|
}
|
|
|
|
void updatevabbs(bool force)
|
|
{
|
|
if(force)
|
|
{
|
|
worldmin = nogimin = ivec(worldsize, worldsize, worldsize);
|
|
worldmax = nogimax = ivec(0, 0, 0);
|
|
loopv(varoot) updatevabb(varoot[i], true);
|
|
if(worldmin.x >= worldmax.x)
|
|
{
|
|
worldmin = ivec(0, 0, 0);
|
|
worldmax = ivec(worldsize, worldsize, worldsize);
|
|
}
|
|
}
|
|
else loopv(varoot) updatevabb(varoot[i]);
|
|
}
|
|
|
|
struct mergedface
|
|
{
|
|
uchar orient, numverts;
|
|
ushort mat, tex, envmap;
|
|
vertinfo *verts;
|
|
int tjoints;
|
|
};
|
|
|
|
#define MAXMERGELEVEL 12
|
|
static int vahasmerges = 0, vamergemax = 0;
|
|
static vector<mergedface> vamerges[MAXMERGELEVEL+1];
|
|
|
|
static int genmergedfaces(cube &c, const ivec &co, int size, int minlevel = -1)
|
|
{
|
|
if(!c.ext || isempty(c)) return -1;
|
|
int tj = c.ext->tjoints, maxlevel = -1;
|
|
loopi(6) if(c.merged&(1<<i))
|
|
{
|
|
surfaceinfo &surf = c.ext->surfaces[i];
|
|
int numverts = surf.numverts&MAXFACEVERTS;
|
|
if(!numverts)
|
|
{
|
|
if(minlevel < 0) vahasmerges |= MERGE_PART;
|
|
continue;
|
|
}
|
|
mergedface mf;
|
|
mf.orient = i;
|
|
mf.mat = c.material;
|
|
mf.tex = c.texture[i];
|
|
mf.envmap = EMID_NONE;
|
|
mf.numverts = surf.numverts;
|
|
mf.verts = c.ext->verts() + surf.verts;
|
|
mf.tjoints = -1;
|
|
int level = calcmergedsize(i, co, size, mf.verts, mf.numverts&MAXFACEVERTS);
|
|
if(level > minlevel)
|
|
{
|
|
maxlevel = max(maxlevel, level);
|
|
|
|
while(tj >= 0 && tjoints[tj].edge < i*(MAXFACEVERTS+1)) tj = tjoints[tj].next;
|
|
if(tj >= 0 && tjoints[tj].edge < (i+1)*(MAXFACEVERTS+1)) mf.tjoints = tj;
|
|
|
|
VSlot &vslot = lookupvslot(mf.tex, true),
|
|
*layer = vslot.layer && !(c.material&MAT_ALPHA) ? &lookupvslot(vslot.layer, true) : nullptr;
|
|
if(vslot.slot->shader->type&SHADER_ENVMAP)
|
|
mf.envmap = vslot.slot->texmask&(1<<TEX_ENVMAP) ? EMID_CUSTOM : closestenvmap(i, co, size);
|
|
ushort envmap2 = layer && layer->slot->shader->type&SHADER_ENVMAP ? (layer->slot->texmask&(1<<TEX_ENVMAP) ? EMID_CUSTOM : closestenvmap(i, co, size)) : EMID_NONE;
|
|
|
|
if(surf.numverts&LAYER_TOP) vamerges[level].add(mf);
|
|
if(surf.numverts&LAYER_BOTTOM)
|
|
{
|
|
mf.tex = vslot.layer;
|
|
mf.envmap = envmap2;
|
|
mf.numverts &= ~LAYER_BLEND;
|
|
mf.numverts |= surf.numverts&LAYER_TOP ? LAYER_BOTTOM : LAYER_TOP;
|
|
vamerges[level].add(mf);
|
|
}
|
|
}
|
|
}
|
|
if(maxlevel >= 0)
|
|
{
|
|
vamergemax = max(vamergemax, maxlevel);
|
|
vahasmerges |= MERGE_ORIGIN;
|
|
}
|
|
return maxlevel;
|
|
}
|
|
|
|
static int findmergedfaces(cube &c, const ivec &co, int size, int csi, int minlevel)
|
|
{
|
|
if(c.ext && c.ext->va && !(c.ext->va->hasmerges&MERGE_ORIGIN)) return c.ext->va->mergelevel;
|
|
else if(c.children)
|
|
{
|
|
int maxlevel = -1;
|
|
loopi(8)
|
|
{
|
|
ivec o(i, co, size/2);
|
|
int level = findmergedfaces(c.children[i], o, size/2, csi-1, minlevel);
|
|
maxlevel = max(maxlevel, level);
|
|
}
|
|
return maxlevel;
|
|
}
|
|
else if(c.ext && c.merged) return genmergedfaces(c, co, size, minlevel);
|
|
else return -1;
|
|
}
|
|
|
|
static void addmergedverts(int level, const ivec &o)
|
|
{
|
|
vector<mergedface> &mfl = vamerges[level];
|
|
if(mfl.empty()) return;
|
|
vec vo(ivec(o).mask(~0xFFF));
|
|
vec pos[MAXFACEVERTS];
|
|
loopv(mfl)
|
|
{
|
|
mergedface &mf = mfl[i];
|
|
int numverts = mf.numverts&MAXFACEVERTS;
|
|
loopi(numverts)
|
|
{
|
|
vertinfo &v = mf.verts[i];
|
|
pos[i] = vec(v.x, v.y, v.z).mul(1.0f/8).add(vo);
|
|
}
|
|
VSlot &vslot = lookupvslot(mf.tex, true);
|
|
int grassy = vslot.slot->grass && mf.orient!=O_BOTTOM && mf.numverts&LAYER_TOP ? 2 : 0;
|
|
addcubeverts(vslot, mf.orient, 1<<level, pos, 0, mf.tex, mf.verts, numverts, mf.tjoints, mf.envmap, grassy, (mf.mat&MAT_ALPHA)!=0, mf.numverts&LAYER_BLEND);
|
|
vahasmerges |= MERGE_USE;
|
|
}
|
|
mfl.setsize(0);
|
|
}
|
|
|
|
static inline void finddecals(vtxarray *va)
|
|
{
|
|
if(va->hasmerges&(MERGE_ORIGIN|MERGE_PART))
|
|
{
|
|
loopv(va->decals) vc.extdecals.add(va->decals[i]);
|
|
loopv(va->children) finddecals(va->children[i]);
|
|
}
|
|
}
|
|
|
|
static void rendercube(cube &c, const ivec &co, int size, int csi, int &maxlevel) // creates vertices and indices ready to be put into a va
|
|
{
|
|
//if(size<=16) return;
|
|
if(c.ext && c.ext->va)
|
|
{
|
|
maxlevel = max(maxlevel, c.ext->va->mergelevel);
|
|
finddecals(c.ext->va);
|
|
return; // don't re-render
|
|
}
|
|
|
|
if(c.children)
|
|
{
|
|
neighbourstack[++neighbourdepth] = c.children;
|
|
c.escaped = 0;
|
|
loopi(8)
|
|
{
|
|
ivec o(i, co, size/2);
|
|
int level = -1;
|
|
rendercube(c.children[i], o, size/2, csi-1, level);
|
|
if(level >= csi)
|
|
c.escaped |= 1<<i;
|
|
maxlevel = max(maxlevel, level);
|
|
}
|
|
--neighbourdepth;
|
|
|
|
if(csi <= MAXMERGELEVEL && vamerges[csi].length()) addmergedverts(csi, co);
|
|
|
|
if(c.ext && c.ext->ents)
|
|
{
|
|
if(c.ext->ents->mapmodels.length()) vc.mapmodels.add(c.ext->ents);
|
|
if(c.ext->ents->decals.length()) vc.decals.add(c.ext->ents);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(!isempty(c))
|
|
{
|
|
gencubeverts(c, co, size, csi);
|
|
if(c.merged) maxlevel = max(maxlevel, genmergedfaces(c, co, size));
|
|
}
|
|
if(c.material != MAT_AIR)
|
|
{
|
|
genmatsurfs(c, co, size, vc.matsurfs);
|
|
if(c.material&MAT_NOGI)
|
|
{
|
|
vc.nogimin.min(co);
|
|
vc.nogimax.max(ivec(co).add(size));
|
|
}
|
|
}
|
|
|
|
if(c.ext && c.ext->ents)
|
|
{
|
|
if(c.ext->ents->mapmodels.length()) vc.mapmodels.add(c.ext->ents);
|
|
if(c.ext->ents->decals.length()) vc.decals.add(c.ext->ents);
|
|
}
|
|
|
|
if(csi <= MAXMERGELEVEL && vamerges[csi].length()) addmergedverts(csi, co);
|
|
}
|
|
|
|
static void calcgeombb(const ivec &co, int size, ivec &bbmin, ivec &bbmax)
|
|
{
|
|
vec vmin(co), vmax = vmin;
|
|
vmin.add(size);
|
|
|
|
loopv(vc.verts)
|
|
{
|
|
const vec &v = vc.verts[i].pos;
|
|
vmin.min(v);
|
|
vmax.max(v);
|
|
}
|
|
|
|
bbmin = ivec(vmin.mul(8)).shr(3);
|
|
bbmax = ivec(vmax.mul(8)).add(7).shr(3);
|
|
}
|
|
|
|
static int entdepth = -1;
|
|
static octaentities *entstack[32];
|
|
|
|
static void setva(cube &c, const ivec &co, int size, int csi)
|
|
{
|
|
assert(size <= 0x1000);
|
|
|
|
int vamergeoffset[MAXMERGELEVEL+1];
|
|
loopi(MAXMERGELEVEL+1) vamergeoffset[i] = vamerges[i].length();
|
|
|
|
vc.origin = co;
|
|
vc.size = size;
|
|
|
|
loopi(entdepth+1)
|
|
{
|
|
octaentities *oe = entstack[i];
|
|
if(oe->decals.length()) vc.extdecals.add(oe);
|
|
}
|
|
|
|
int maxlevel = -1;
|
|
rendercube(c, co, size, csi, maxlevel);
|
|
|
|
if(size == min(0x1000, worldsize/2) || !vc.emptyva())
|
|
{
|
|
vtxarray *va = newva(co, size);
|
|
ext(c).va = va;
|
|
calcgeombb(co, size, va->geommin, va->geommax);
|
|
calcmatbb(va, co, size, vc.matsurfs);
|
|
va->hasmerges = vahasmerges;
|
|
va->mergelevel = vamergemax;
|
|
}
|
|
else
|
|
{
|
|
loopi(MAXMERGELEVEL+1) vamerges[i].setsize(vamergeoffset[i]);
|
|
}
|
|
|
|
vc.clear();
|
|
}
|
|
|
|
static inline int setcubevisibility(cube &c, const ivec &co, int size)
|
|
{
|
|
if(isempty(c) && (c.material&MATF_CLIP) != MAT_CLIP) return 0;
|
|
int numvis = 0, vismask = 0, collidemask = 0, checkmask = 0;
|
|
loopi(6)
|
|
{
|
|
int facemask = classifyface(c, i, co, size);
|
|
if(facemask&1)
|
|
{
|
|
vismask |= 1<<i;
|
|
if(c.merged&(1<<i))
|
|
{
|
|
if(c.ext && c.ext->surfaces[i].numverts&MAXFACEVERTS) numvis++;
|
|
}
|
|
else
|
|
{
|
|
numvis++;
|
|
if(c.texture[i] != DEFAULT_SKY && !(c.ext && c.ext->surfaces[i].numverts&MAXFACEVERTS)) checkmask |= 1<<i;
|
|
}
|
|
}
|
|
if(facemask&2) collidemask |= 1<<i;
|
|
}
|
|
c.visible = collidemask | (vismask ? (vismask != collidemask ? (checkmask ? 0x80|0x40 : 0x80) : 0x40) : 0);
|
|
return numvis;
|
|
}
|
|
|
|
VARF(vafacemax, 64, 384, 256*256, allchanged());
|
|
VARF(vafacemin, 0, 96, 256*256, allchanged());
|
|
VARF(vacubesize, 32, 128, 0x1000, allchanged());
|
|
|
|
static int updateva(cube *c, const ivec &co, int size, int csi)
|
|
{
|
|
progress("recalculating geometry...");
|
|
int ccount = 0, cmergemax = vamergemax, chasmerges = vahasmerges;
|
|
neighbourstack[++neighbourdepth] = c;
|
|
loopi(8) // counting number of semi-solid/solid children cubes
|
|
{
|
|
int count = 0, childpos = varoot.length();
|
|
ivec o(i, co, size);
|
|
vamergemax = 0;
|
|
vahasmerges = 0;
|
|
if(c[i].ext && c[i].ext->va)
|
|
{
|
|
varoot.add(c[i].ext->va);
|
|
if(c[i].ext->va->hasmerges&MERGE_ORIGIN) findmergedfaces(c[i], o, size, csi, csi);
|
|
}
|
|
else
|
|
{
|
|
if(c[i].children)
|
|
{
|
|
if(c[i].ext && c[i].ext->ents) entstack[++entdepth] = c[i].ext->ents;
|
|
count += updateva(c[i].children, o, size/2, csi-1);
|
|
if(c[i].ext && c[i].ext->ents) --entdepth;
|
|
}
|
|
else count += setcubevisibility(c[i], o, size);
|
|
int tcount = count + (csi <= MAXMERGELEVEL ? vamerges[csi].length() : 0);
|
|
if(tcount > vafacemax || (tcount >= vafacemin && size >= vacubesize) || size == min(0x1000, worldsize/2))
|
|
{
|
|
loadprogress = std::clamp(recalcprogress/float(allocnodes), 0.0f, 1.0f);
|
|
setva(c[i], o, size, csi);
|
|
if(c[i].ext && c[i].ext->va)
|
|
{
|
|
while(varoot.length() > childpos)
|
|
{
|
|
vtxarray *child = varoot.pop();
|
|
c[i].ext->va->children.add(child);
|
|
child->parent = c[i].ext->va;
|
|
}
|
|
varoot.add(c[i].ext->va);
|
|
if(vamergemax > size)
|
|
{
|
|
cmergemax = max(cmergemax, vamergemax);
|
|
chasmerges |= vahasmerges&~MERGE_USE;
|
|
}
|
|
continue;
|
|
}
|
|
else count = 0;
|
|
}
|
|
}
|
|
if(csi+1 <= MAXMERGELEVEL && vamerges[csi].length()) vamerges[csi+1].move(vamerges[csi]);
|
|
cmergemax = max(cmergemax, vamergemax);
|
|
chasmerges |= vahasmerges;
|
|
ccount += count;
|
|
}
|
|
--neighbourdepth;
|
|
vamergemax = cmergemax;
|
|
vahasmerges = chasmerges;
|
|
|
|
return ccount;
|
|
}
|
|
|
|
static void addtjoint(const edgegroup &g, const cubeedge &e, int offset)
|
|
{
|
|
int vcoord = (g.slope[g.axis]*offset + g.origin[g.axis]) & 0x7FFF;
|
|
tjoint &tj = tjoints.add();
|
|
tj.offset = vcoord / g.slope[g.axis];
|
|
tj.edge = e.index;
|
|
|
|
int prev = -1, cur = ext(*e.c).tjoints;
|
|
while(cur >= 0)
|
|
{
|
|
tjoint &o = tjoints[cur];
|
|
if(tj.edge < o.edge || (tj.edge==o.edge && (e.flags&CE_FLIP ? tj.offset > o.offset : tj.offset < o.offset))) break;
|
|
prev = cur;
|
|
cur = o.next;
|
|
}
|
|
|
|
tj.next = cur;
|
|
if(prev < 0) e.c->ext->tjoints = tjoints.length()-1;
|
|
else tjoints[prev].next = tjoints.length()-1;
|
|
}
|
|
|
|
static void findtjoints(int cur, const edgegroup &g)
|
|
{
|
|
int active = -1;
|
|
while(cur >= 0)
|
|
{
|
|
cubeedge &e = cubeedges[cur];
|
|
int prevactive = -1, curactive = active;
|
|
while(curactive >= 0)
|
|
{
|
|
cubeedge &a = cubeedges[curactive];
|
|
if(a.offset+a.size <= e.offset)
|
|
{
|
|
if(prevactive >= 0) cubeedges[prevactive].next = a.next;
|
|
else active = a.next;
|
|
}
|
|
else
|
|
{
|
|
prevactive = curactive;
|
|
if(!(a.flags&CE_DUP))
|
|
{
|
|
if(e.flags&CE_START && e.offset > a.offset && e.offset < a.offset+a.size)
|
|
addtjoint(g, a, e.offset);
|
|
if(e.flags&CE_END && e.offset+e.size > a.offset && e.offset+e.size < a.offset+a.size)
|
|
addtjoint(g, a, e.offset+e.size);
|
|
}
|
|
if(!(e.flags&CE_DUP))
|
|
{
|
|
if(a.flags&CE_START && a.offset > e.offset && a.offset < e.offset+e.size)
|
|
addtjoint(g, e, a.offset);
|
|
if(a.flags&CE_END && a.offset+a.size > e.offset && a.offset+a.size < e.offset+e.size)
|
|
addtjoint(g, e, a.offset+a.size);
|
|
}
|
|
}
|
|
curactive = a.next;
|
|
}
|
|
int next = e.next;
|
|
e.next = active;
|
|
active = cur;
|
|
cur = next;
|
|
}
|
|
}
|
|
|
|
void findtjoints()
|
|
{
|
|
recalcprogress = 0;
|
|
gencubeedges();
|
|
tjoints.setsize(0);
|
|
enumeratekt(edgegroups, edgegroup, g, int, e, findtjoints(e, g));
|
|
cubeedges.setsize(0);
|
|
edgegroups.clear();
|
|
}
|
|
|
|
void octarender() // creates va s for all leaf cubes that don't already have them
|
|
{
|
|
int csi = 0;
|
|
while(1<<csi < worldsize) csi++;
|
|
|
|
recalcprogress = 0;
|
|
varoot.setsize(0);
|
|
updateva(worldroot, ivec(0, 0, 0), worldsize/2, csi-1);
|
|
loadprogress = 0;
|
|
flushvbo();
|
|
|
|
explicitsky = 0;
|
|
loopv(valist)
|
|
{
|
|
vtxarray *va = valist[i];
|
|
explicitsky += va->sky;
|
|
}
|
|
|
|
visibleva = nullptr;
|
|
}
|
|
|
|
static void precachetextures()
|
|
{
|
|
vector<int> texs;
|
|
loopv(valist)
|
|
{
|
|
vtxarray *va = valist[i];
|
|
loopj(va->texs + va->blends)
|
|
{
|
|
int tex = va->texelems[j].texture;
|
|
if(texs.find(tex) < 0)
|
|
{
|
|
texs.add(tex);
|
|
|
|
VSlot &vslot = lookupvslot(tex, false);
|
|
if(vslot.layer && texs.find(vslot.layer) < 0) texs.add(vslot.layer);
|
|
if(vslot.detail && texs.find(vslot.detail) < 0) texs.add(vslot.detail);
|
|
}
|
|
}
|
|
}
|
|
loopv(texs)
|
|
{
|
|
loadprogress = float(i+1)/texs.length();
|
|
lookupvslot(texs[i]);
|
|
}
|
|
loadprogress = 0;
|
|
}
|
|
|
|
void allchanged(bool load)
|
|
{
|
|
if(mainmenu && !isconnected()) load = false;
|
|
if(load) initlights();
|
|
renderprogress(0, "clearing vertex arrays...");
|
|
clearvas(worldroot);
|
|
resetqueries();
|
|
resetclipplanes();
|
|
if(load) initenvmaps();
|
|
entitiesinoctanodes();
|
|
tjoints.setsize(0);
|
|
if(filltjoints) findtjoints();
|
|
octarender();
|
|
if(load) precachetextures();
|
|
setupmaterials();
|
|
clearshadowcache();
|
|
updatevabbs(true);
|
|
if(load)
|
|
{
|
|
genshadowmeshes();
|
|
updateblendtextures();
|
|
seedparticles();
|
|
genenvmaps();
|
|
drawminimap();
|
|
}
|
|
}
|
|
|
|
static void recalc()
|
|
{
|
|
allchanged(true);
|
|
}
|
|
|
|
COMMAND(recalc, "");
|
|
|