OctaCore/src/engine/normal.cc

288 lines
9.1 KiB
C++

#include <shared/command.hh>
#include "light.hh"
#include "main.hh" // renderprogress
#include "octarender.hh"
#include "texture.hh"
#include "world.hh"
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 = nullptr;
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)));