mirror of
https://github.com/spoopyghosty0/frameport.git
synced 2026-10-24 13:01:48 +02:00
113 lines
5.1 KiB
C
113 lines
5.1 KiB
C
/* SPDX-License-Identifier: GPL-3.0-only */
|
|
#include "scan.h"
|
|
|
|
#define TANGENT_RADIUS_SQUARED 2.25f
|
|
#define TANGENT_WEIGHT_RADIUS_SQUARED 2.5f
|
|
#define MAX_EXTRACTION_STRIDE 4
|
|
/* Marching cubes without a 256-case table: trace the zero contour on the six faces, then fan each closed loop.
|
|
* Ambiguous faces choose their diagonal from the face centre. Adjacent cubes therefore make the same choice. */
|
|
static const int corners[8][3]={{0,0,0},{1,0,0},{1,0,1},{0,0,1},{0,1,0},{1,1,0},{1,1,1},{0,1,1}};
|
|
static const int edges[12][2]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
|
|
static const int faces[6][4]={{0,1,2,3},{4,5,6,7},{0,9,4,8},{1,10,5,9},{2,11,6,10},{3,8,7,11}};
|
|
|
|
static void tsdf(Scan *s)
|
|
{
|
|
float v=s->opt.voxel,trunc=v*s->opt.truncation;
|
|
int radius=(int)s->opt.truncation+2;
|
|
for (int i=0;i<s->count;i++) {
|
|
Cell *c=&s->cells[i];
|
|
if (!c->weight) continue;
|
|
int x=i%s->nx,z=(i/s->nx)%s->nz,y=i/(s->nx*s->nz);
|
|
for (int dy=-radius;dy<=radius;dy++) for (int dz=-radius;dz<=radius;dz++) for (int dx=-radius;dx<=radius;dx++) {
|
|
int xx=x+dx,yy=y+dy,zz=z+dz;
|
|
if (xx<0 || yy<0 || zz<0 || xx>=s->nx || yy>=s->ny || zz>=s->nz) continue;
|
|
Vec delta=sub(grid_point(s,xx,yy,zz),c->p);
|
|
float d=dot(delta,c->n),tangent=dot(delta,delta)-d*d;
|
|
if (ab(d)>trunc || tangent>v*v*TANGENT_RADIUS_SQUARED) continue;
|
|
Cell *dest=&s->cells[(yy*s->nz+zz)*s->nx+xx];
|
|
float w=(1-tangent/(v*v*TANGENT_WEIGHT_RADIUS_SQUARED))*mn(c->weight/(v*v),1.f);
|
|
dest->sdf+=d*w;dest->sw+=w;
|
|
}
|
|
}
|
|
for (int i=0;i<s->count;i++) if(s->cells[i].sw) s->cells[i].sdf/=s->cells[i].sw;
|
|
}
|
|
static void connect(int links[12][2], int a, int b)
|
|
{
|
|
if(links[a][0]<0) links[a][0]=b;else links[a][1]=b;
|
|
if(links[b][0]<0) links[b][0]=a;else links[b][1]=a;
|
|
}
|
|
static int cube(Scan *s, int x, int y, int z, int stride)
|
|
{
|
|
float f[8];Vec p[8],normal=vec(0,0,0);
|
|
for(int k=0;k<8;k++) {
|
|
int xx=x+corners[k][0]*stride,yy=y+corners[k][1]*stride,zz=z+corners[k][2]*stride;
|
|
Cell *c=&s->cells[(yy*s->nz+zz)*s->nx+xx];
|
|
if (c->sw<=0) return 1; /* Unknown space never creates an invented surface. */
|
|
f[k]=c->sdf;p[k]=grid_point(s,xx,yy,zz);
|
|
}
|
|
Vec cuts[12];int crossing[12],links[12][2],visited[12]={0};
|
|
for(int e=0;e<12;e++) {
|
|
int a=edges[e][0],b=edges[e][1];
|
|
crossing[e]=(f[a]<0)!=(f[b]<0);links[e][0]=links[e][1]=-1;
|
|
if(crossing[e]) cuts[e]=add(p[a],mul(sub(p[b],p[a]),f[a]/(f[a]-f[b])));
|
|
}
|
|
/* Gradient points to positive TSDF: toward the observed free room, including furniture's exterior. */
|
|
normal.x=f[1]+f[2]+f[5]+f[6]-f[0]-f[3]-f[4]-f[7];
|
|
normal.y=f[4]+f[5]+f[6]+f[7]-f[0]-f[1]-f[2]-f[3];
|
|
normal.z=f[2]+f[3]+f[6]+f[7]-f[0]-f[1]-f[4]-f[5];
|
|
for(int face=0;face<6;face++) {
|
|
int hit[4],n=0;float centre=0;
|
|
for(int j=0;j<4;j++) {
|
|
int e=faces[face][j];
|
|
if(crossing[e]) hit[n++]=e;
|
|
centre+=f[edges[e][0]]+f[edges[e][1]];
|
|
}
|
|
if(n==2) connect(links,hit[0],hit[1]);
|
|
else if(n==4) {
|
|
/* The shared first edge's first corner fixes the phase on this face. */
|
|
int first=edges[hit[0]][0],second=edges[hit[0]][1];
|
|
int common=(edges[hit[1]][0]==second || edges[hit[1]][1]==second) ? second : first;
|
|
if((centre<0)==(f[common]<0)) {
|
|
connect(links,hit[0],hit[3]);connect(links,hit[1],hit[2]);
|
|
} else { connect(links,hit[0],hit[1]);connect(links,hit[2],hit[3]); }
|
|
}
|
|
}
|
|
for(int e=0;e<12;e++) if(crossing[e] && !visited[e]) {
|
|
int loop[12],n=0,prev=-1,cur=e;
|
|
while(cur>=0 && !visited[cur] && n<12) {
|
|
visited[cur]=1;loop[n++]=cur;
|
|
int next=links[cur][0]==prev ? links[cur][1] : links[cur][0];prev=cur;cur=next;
|
|
}
|
|
if(cur!=e) continue;
|
|
for(int k=1;k+1<n;k++) {
|
|
Vec a=cuts[loop[0]],b=cuts[loop[k]],c=cuts[loop[k+1]];
|
|
Vec norm=cross(sub(b,a),sub(c,a));
|
|
if(dot(norm,norm)<1e-14f) continue;
|
|
if(s->ni/3>=MAX_TRIANGLES) return 0;
|
|
if(dot(norm,normal)<0) { Vec t=b;b=c;c=t; }
|
|
s->vertices[s->nv]=a;s->vertices[s->nv+1]=b;s->vertices[s->nv+2]=c;
|
|
for(int j=0;j<3;j++) s->indices[s->ni++]=(u32)s->nv++;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
const char *make_mesh(Scan *s)
|
|
{
|
|
s->vertices=io_alloc((usize)MAX_MESH_VERTICES*sizeof(Vec));
|
|
s->indices=io_alloc((usize)MAX_TRIANGLES*3*sizeof(u32));
|
|
if(!s->vertices || !s->indices) return "mesh allocation failed";
|
|
tsdf(s);
|
|
for(int stride=1;stride<=MAX_EXTRACTION_STRIDE;stride++) {
|
|
s->nv=s->ni=0;int ok=1;
|
|
for(int y=0;y+stride<s->ny && ok;y+=stride)
|
|
for(int z=0;z+stride<s->nz && ok;z+=stride)
|
|
for(int x=0;x+stride<s->nx && ok;x+=stride) ok=cube(s,x,y,z,stride);
|
|
if(ok) {
|
|
if(stride>1) warn(s,"mesh decimated by coarsening extraction grid");
|
|
if(!s->ni) warn(s,"no mesh surface; scan denser views");
|
|
return 0;
|
|
}
|
|
}
|
|
return "mesh exceeds triangle budget";
|
|
}
|