2020-04-16 18:28:40 +00:00
|
|
|
#include "engine.hh"
|
2020-04-15 16:39:17 +00:00
|
|
|
|
|
|
|
struct normalkey
|
|
|
|
{
|
|
|
|
vec pos;
|
|
|
|
int smooth;
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline uint hthash(const normalkey &k) { return hthash(k.pos); }
|
|
|
|
|
|
|
|
struct normalgroup
|
|
|
|
{
|
|
|
|
vec pos;
|
|
|
|
int smooth, flat, normals, tnormals;
|
|
|
|
|
|
|
|
normalgroup() : smooth(0), flat(0), normals(-1), tnormals(-1) {}
|
|
|
|
normalgroup(const normalkey &key) : pos(key.pos), smooth(key.smooth), flat(0), normals(-1), tnormals(-1) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline bool htcmp(const normalkey &k, const normalgroup &n) { return k.pos == n.pos && k.smooth == n.smooth; }
|
|
|
|
|
|
|
|
struct normal
|
|
|
|
{
|
|
|
|
int next;
|
|
|
|
vec surface;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct tnormal
|
|
|
|
{
|
|
|
|
int next;
|
|
|
|
float offset;
|
|
|
|
int normals[2];
|
|
|
|
normalgroup *groups[2];
|
|
|
|
};
|
|
|
|
|
|
|
|
hashset<normalgroup> normalgroups(1<<16);
|
|
|
|
vector<normal> normals;
|
|
|
|
vector<tnormal> tnormals;
|
|
|
|
vector<int> smoothgroups;
|
|
|
|
|
|
|
|
VARR(lerpangle, 0, 44, 180);
|
|
|
|
|
|
|
|
static bool usetnormals = true;
|
|
|
|
|
|
|
|
static int addnormal(const vec &pos, int smooth, const vec &surface)
|
|
|
|
{
|
|
|
|
normalkey key = { pos, smooth };
|
|
|
|
normalgroup &g = normalgroups.access(key, key);
|
|
|
|
normal &n = normals.add();
|
|
|
|
n.next = g.normals;
|
|
|
|
n.surface = surface;
|
|
|
|
return g.normals = normals.length()-1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void addtnormal(const vec &pos, int smooth, float offset, int normal1, int normal2, const vec &pos1, const vec &pos2)
|
|
|
|
{
|
|
|
|
normalkey key = { pos, smooth };
|
|
|
|
normalgroup &g = normalgroups.access(key, key);
|
|
|
|
tnormal &n = tnormals.add();
|
|
|
|
n.next = g.tnormals;
|
|
|
|
n.offset = offset;
|
|
|
|
n.normals[0] = normal1;
|
|
|
|
n.normals[1] = normal2;
|
|
|
|
normalkey key1 = { pos1, smooth }, key2 = { pos2, smooth };
|
|
|
|
n.groups[0] = normalgroups.access(key1);
|
|
|
|
n.groups[1] = normalgroups.access(key2);
|
|
|
|
g.tnormals = tnormals.length()-1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int addnormal(const vec &pos, int smooth, int axis)
|
|
|
|
{
|
|
|
|
normalkey key = { pos, smooth };
|
|
|
|
normalgroup &g = normalgroups.access(key, key);
|
|
|
|
g.flat += 1<<(4*axis);
|
|
|
|
return axis - 6;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void findnormal(const normalgroup &g, float lerpthreshold, const vec &surface, vec &v)
|
|
|
|
{
|
|
|
|
v = vec(0, 0, 0);
|
|
|
|
int total = 0;
|
|
|
|
if(surface.x >= lerpthreshold) { int n = (g.flat>>4)&0xF; v.x += n; total += n; }
|
|
|
|
else if(surface.x <= -lerpthreshold) { int n = g.flat&0xF; v.x -= n; total += n; }
|
|
|
|
if(surface.y >= lerpthreshold) { int n = (g.flat>>12)&0xF; v.y += n; total += n; }
|
|
|
|
else if(surface.y <= -lerpthreshold) { int n = (g.flat>>8)&0xF; v.y -= n; total += n; }
|
|
|
|
if(surface.z >= lerpthreshold) { int n = (g.flat>>20)&0xF; v.z += n; total += n; }
|
|
|
|
else if(surface.z <= -lerpthreshold) { int n = (g.flat>>16)&0xF; v.z -= n; total += n; }
|
|
|
|
for(int cur = g.normals; cur >= 0;)
|
|
|
|
{
|
|
|
|
normal &o = normals[cur];
|
|
|
|
if(o.surface.dot(surface) >= lerpthreshold)
|
|
|
|
{
|
|
|
|
v.add(o.surface);
|
|
|
|
total++;
|
|
|
|
}
|
|
|
|
cur = o.next;
|
|
|
|
}
|
|
|
|
if(total > 1) v.normalize();
|
|
|
|
else if(!total) v = surface;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool findtnormal(const normalgroup &g, float lerpthreshold, const vec &surface, vec &v)
|
|
|
|
{
|
|
|
|
float bestangle = lerpthreshold;
|
|
|
|
tnormal *bestnorm = NULL;
|
|
|
|
for(int cur = g.tnormals; cur >= 0;)
|
|
|
|
{
|
|
|
|
tnormal &o = tnormals[cur];
|
|
|
|
static const vec flats[6] = { vec(-1, 0, 0), vec(1, 0, 0), vec(0, -1, 0), vec(0, 1, 0), vec(0, 0, -1), vec(0, 0, 1) };
|
|
|
|
vec n1 = o.normals[0] < 0 ? flats[o.normals[0]+6] : normals[o.normals[0]].surface,
|
|
|
|
n2 = o.normals[1] < 0 ? flats[o.normals[1]+6] : normals[o.normals[1]].surface,
|
|
|
|
nt;
|
|
|
|
nt.lerp(n1, n2, o.offset).normalize();
|
|
|
|
float tangle = nt.dot(surface);
|
|
|
|
if(tangle >= bestangle)
|
|
|
|
{
|
|
|
|
bestangle = tangle;
|
|
|
|
bestnorm = &o;
|
|
|
|
}
|
|
|
|
cur = o.next;
|
|
|
|
}
|
|
|
|
if(!bestnorm) return false;
|
|
|
|
vec n1, n2;
|
|
|
|
findnormal(*bestnorm->groups[0], lerpthreshold, surface, n1);
|
|
|
|
findnormal(*bestnorm->groups[1], lerpthreshold, surface, n2);
|
|
|
|
v.lerp(n1, n2, bestnorm->offset).normalize();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void findnormal(const vec &pos, int smooth, const vec &surface, vec &v)
|
|
|
|
{
|
|
|
|
normalkey key = { pos, smooth };
|
|
|
|
const normalgroup *g = normalgroups.access(key);
|
|
|
|
if(g)
|
|
|
|
{
|
|
|
|
int angle = smoothgroups.inrange(smooth) && smoothgroups[smooth] >= 0 ? smoothgroups[smooth] : lerpangle;
|
|
|
|
float lerpthreshold = cos360(angle) - 1e-5f;
|
|
|
|
if(g->tnormals < 0 || !findtnormal(*g, lerpthreshold, surface, v))
|
|
|
|
findnormal(*g, lerpthreshold, surface, v);
|
|
|
|
}
|
|
|
|
else v = surface;
|
|
|
|
}
|
|
|
|
|
|
|
|
VARR(lerpsubdiv, 0, 2, 4);
|
|
|
|
VARR(lerpsubdivsize, 4, 4, 128);
|
|
|
|
|
|
|
|
static uint normalprogress = 0;
|
|
|
|
|
|
|
|
void show_addnormals_progress()
|
|
|
|
{
|
|
|
|
float bar1 = float(normalprogress) / float(allocnodes);
|
|
|
|
renderprogress(bar1, "computing normals...");
|
|
|
|
}
|
|
|
|
|
|
|
|
void addnormals(cube &c, const ivec &o, int size)
|
|
|
|
{
|
|
|
|
CHECK_CALCLIGHT_PROGRESS(return, show_addnormals_progress);
|
|
|
|
|
|
|
|
if(c.children)
|
|
|
|
{
|
|
|
|
normalprogress++;
|
|
|
|
size >>= 1;
|
|
|
|
loopi(8) addnormals(c.children[i], ivec(i, o, size), size);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if(isempty(c)) return;
|
|
|
|
|
|
|
|
vec pos[MAXFACEVERTS];
|
|
|
|
int norms[MAXFACEVERTS];
|
|
|
|
int tj = usetnormals && c.ext ? c.ext->tjoints : -1, vis;
|
|
|
|
loopi(6) if((vis = visibletris(c, i, o, size)))
|
|
|
|
{
|
|
|
|
CHECK_CALCLIGHT_PROGRESS(return, show_addnormals_progress);
|
|
|
|
if(c.texture[i] == DEFAULT_SKY) continue;
|
|
|
|
|
|
|
|
vec planes[2];
|
|
|
|
int numverts = c.ext ? c.ext->surfaces[i].numverts&MAXFACEVERTS : 0, convex = 0, numplanes = 0;
|
|
|
|
if(numverts)
|
|
|
|
{
|
|
|
|
vertinfo *verts = c.ext->verts() + c.ext->surfaces[i].verts;
|
|
|
|
vec vo(ivec(o).mask(~0xFFF));
|
|
|
|
loopj(numverts)
|
|
|
|
{
|
|
|
|
vertinfo &v = verts[j];
|
|
|
|
pos[j] = vec(v.x, v.y, v.z).mul(1.0f/8).add(vo);
|
|
|
|
}
|
|
|
|
if(!(c.merged&(1<<i)) && !flataxisface(c, i)) convex = faceconvexity(verts, numverts, size);
|
|
|
|
}
|
|
|
|
else if(c.merged&(1<<i)) continue;
|
|
|
|
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(o);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!flataxisface(c, i))
|
|
|
|
{
|
|
|
|
planes[numplanes++].cross(pos[0], pos[1], pos[2]).normalize();
|
|
|
|
if(convex) planes[numplanes++].cross(pos[0], pos[2], pos[3]).normalize();
|
|
|
|
}
|
|
|
|
|
|
|
|
VSlot &vslot = lookupvslot(c.texture[i], false);
|
|
|
|
int smooth = vslot.slot->smooth;
|
|
|
|
|
|
|
|
if(!numplanes) loopk(numverts) norms[k] = addnormal(pos[k], smooth, i);
|
|
|
|
else if(numplanes==1) loopk(numverts) norms[k] = addnormal(pos[k], smooth, planes[0]);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vec avg = vec(planes[0]).add(planes[1]).normalize();
|
|
|
|
norms[0] = addnormal(pos[0], smooth, avg);
|
|
|
|
norms[1] = addnormal(pos[1], smooth, planes[0]);
|
|
|
|
norms[2] = addnormal(pos[2], smooth, avg);
|
|
|
|
for(int k = 3; k < numverts; k++) norms[k] = addnormal(pos[k], smooth, planes[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
while(tj >= 0 && tjoints[tj].edge < i*(MAXFACEVERTS+1)) tj = tjoints[tj].next;
|
|
|
|
while(tj >= 0 && tjoints[tj].edge < (i+1)*(MAXFACEVERTS+1))
|
|
|
|
{
|
|
|
|
int edge = tjoints[tj].edge, e1 = edge%(MAXFACEVERTS+1), e2 = (e1+1)%numverts;
|
|
|
|
const vec &v1 = pos[e1], &v2 = pos[e2];
|
|
|
|
ivec d(vec(v2).sub(v1).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[axis], v2[axis])*8)&~0x7FFF,
|
|
|
|
offset1 = (int(v1[axis]*8) - origin) / d[axis],
|
|
|
|
offset2 = (int(v2[axis]*8) - origin) / d[axis];
|
|
|
|
vec o = vec(v1).sub(vec(d).mul(offset1/8.0f)), n1, n2;
|
|
|
|
float doffset = 1.0f / (offset2 - offset1);
|
|
|
|
|
|
|
|
while(tj >= 0)
|
|
|
|
{
|
|
|
|
tjoint &t = tjoints[tj];
|
|
|
|
if(t.edge != edge) break;
|
|
|
|
float offset = (t.offset - offset1) * doffset;
|
|
|
|
vec tpos = vec(d).mul(t.offset/8.0f).add(o);
|
|
|
|
addtnormal(tpos, smooth, offset, norms[e1], norms[e2], v1, v2);
|
|
|
|
tj = t.next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void calcnormals(bool lerptjoints)
|
|
|
|
{
|
|
|
|
usetnormals = lerptjoints;
|
|
|
|
if(usetnormals) findtjoints();
|
|
|
|
normalprogress = 1;
|
|
|
|
loopi(8) addnormals(worldroot[i], ivec(i, ivec(0, 0, 0), worldsize/2), worldsize/2);
|
|
|
|
}
|
|
|
|
|
|
|
|
void clearnormals()
|
|
|
|
{
|
|
|
|
normalgroups.clear();
|
|
|
|
normals.setsize(0);
|
|
|
|
tnormals.setsize(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void resetsmoothgroups()
|
|
|
|
{
|
|
|
|
smoothgroups.setsize(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int smoothangle(int id, int angle)
|
|
|
|
{
|
|
|
|
if(id < 0) id = smoothgroups.length();
|
|
|
|
if(id >= 10000) return -1;
|
|
|
|
while(smoothgroups.length() <= id) smoothgroups.add(-1);
|
|
|
|
if(angle >= 0) smoothgroups[id] = min(angle, 180);
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
ICOMMAND(smoothangle, "ib", (int *id, int *angle), intret(smoothangle(*id, *angle)));
|
|
|
|
|