// Material-correctness oracle — verify the 4 avenues (level-mesh-architecture.md §Material).
// Two failing cases, measured by material-error rate at surface sample points vs ground truth:
// Case A: 3-way concave corner (floor mat1 + wallX mat2 + wallY mat3 meet at origin).
// Case B: coplanar split (two floors share z=0, mat1 x<0 / mat2 x>0, boundary line x=0).
// Approaches (per surface fragment, given the cell's captured candidate faces):
// CUR current: <=2 candidates, coplanar merged, pick by |dot(face_n, frag_n)| ; dominant fallback
// P1 point-location: <=N candidates (distinct planes), pick by min|n.p-d| gated by normal
// P3 3-candidate : like CUR but keep 3 candidates
// P4 un-merge+extent: keep coplanar faces, pick the face whose 2D EXTENT contains p
// P2 multi-material : boundary emitted as geometry => correct by construction (topology cost)
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
struct V3{float x,y,z;};
static V3 operator-(V3 a,V3 b){return{a.x-b.x,a.y-b.y,a.z-b.z};}
static float dot(V3 a,V3 b){return a.x*b.x+a.y*b.y+a.z*b.z;}
struct Plane{V3 n;float d;}; // n.p = d on face; inside brush = n.p <= d
struct Brush{std::vector<Plane> pl;int mat;};
static float bsdf(V3 p,const Brush&b){float m=-1e30f;for(auto&q:b.pl)m=fmaxf(m,dot(q.n,p)-q.d);return m;}
static float usdf(V3 p,const std::vector<Brush>&B){float m=1e30f;for(auto&b:B)m=fminf(m,bsdf(p,b));return m;}
static Brush box(V3 lo,V3 hi,int mat){
Brush b;b.mat=mat;
b.pl={{{1,0,0},hi.x},{{-1,0,0},-lo.x},{{0,1,0},hi.y},{{0,-1,0},-lo.y},{{0,0,1},hi.z},{{0,0,-1},-lo.z}};
return b;}
// GROUND TRUTH material at surface point p: the exposed brush face p lies on = the brush with
// min |bsdf| that p is on the boundary of AND not strictly inside another brush (unburied).
static int gt_material(V3 p,const std::vector<Brush>&B){
int best=-1;float bestd=1e30f;
for(size_t i=0;i<B.size();i++){
float s=fabsf(bsdf(p,B[i])); // ~0 => on this brush's surface
if(s>0.02f)continue;
// unburied: p not strictly inside a DIFFERENT brush
bool buried=false;
for(size_t j=0;j<B.size();j++){if(j==i)continue;if(bsdf(p,B[j])<-0.02f){buried=true;break;}}
if(buried)continue;
if(s<bestd){bestd=s;best=B[i].mat;}
}
return best;
}
// exact active face (brush,plane) at surface point p: argmax plane of the min-sdf brush
static void active_face(V3 p,const std::vector<Brush>&B,V3&n,int&mat,int&bi){
float best=1e30f;bi=0;for(size_t i=0;i<B.size();i++){float s=bsdf(p,B[i]);if(s<best){best=s;bi=(int)i;}}
float bp=-1e30f;n={0,0,1};for(auto&q:B[bi].pl){float dp=dot(q.n,p)-q.d;if(dp>bp){bp=dp;n=q.n;}}mat=B[bi].mat;}
struct Cand{V3 n;float d;int mat;int brush;}; // a captured candidate face
// approach id: 0 CUR, 1 P1, 2 P3, 3 P4 (P2 handled analytically)
static int select_mat(int ap,const std::vector<Cand>&C,V3 p,V3 fn,const std::vector<Brush>&B){
if(C.empty())return -1;
if(ap==0||ap==2){ // CUR / P3: pick by normal agreement among first K
int K=(ap==0)?2:3; int best=-1;float bd=-1;
for(int i=0;i<(int)C.size()&&i<K;i++){float a=fabsf(dot(C[i].n,fn));if(a>bd){bd=a;best=i;}}
return C[best].mat;
}
if(ap==1){ // P1: plane-distance, normal-gated
int best=-1;float bd=1e30f;
for(auto&c:C){float pd=fabsf(dot(c.n,p)-c.d);float na=fabsf(dot(c.n,fn));if(na<0.5f)continue;if(pd<bd){bd=pd;best=&c-&C[0];}}
if(best<0){for(auto&c:C){float pd=fabsf(dot(c.n,p)-c.d);if(pd<bd){bd=pd;best=&c-&C[0];}}}
return C[best].mat;
}
// P4: pick the candidate whose brush EXTENT contains p (p inside that brush's other planes)
int best=-1;float bd=1e30f;
for(auto&c:C){
const Brush&b=B[c.brush];bool inside=true;
for(auto&q:b.pl){if(dot(q.n,q.n)>1.5f)continue; if(dot(q.n,p)-q.d>0.03f){inside=false;break;}}
float pd=fabsf(dot(c.n,p)-c.d);
if(inside&&pd<bd){bd=pd;best=&c-&C[0];}
}
if(best<0)return select_mat(1,C,p,fn,B); // fall back to P1 if none contain
return C[best].mat;
}
// Capture candidate faces for the cell at center cc (mimics bake_planeset). merge_coplanar
// drops same-normal dupes (CUR/P1/P3); cap limits count (CUR=2,P3=3); P1/P4 use cap=8, P4 no-merge.
static std::vector<Cand> capture(V3 cc,float vs,const std::vector<Brush>&B,bool merge,int cap){
std::vector<Cand> C;float T=vs*1.2f;
for(size_t bi=0;bi<B.size();bi++){const Brush&b=B[bi];
for(auto&pl:b.pl){
float fd=dot(pl.n,cc)-pl.d;if(fabsf(fd)>=T)continue;
bool onb=true;for(auto&q:b.pl){if(&q==&pl)continue;if(dot(q.n,cc)-q.d>T){onb=false;break;}}
if(!onb)continue;
V3 fpt=cc; V3 qp={fpt.x+pl.n.x*vs*0.5f,fpt.y+pl.n.y*vs*0.5f,fpt.z+pl.n.z*vs*0.5f};
bool bur=false;for(size_t bk=0;bk<B.size();bk++){if(bk==bi)continue;if(bsdf(qp,B[bk])<0){bur=true;break;}}
if(bur)continue;
if(merge){bool dup=false;for(auto&c:C)if(fabsf(dot(c.n,pl.n))>0.999f){dup=true;break;}if(dup)continue;}
if((int)C.size()<cap)C.push_back({pl.n,pl.d,b.mat,(int)bi});
}
}
return C;
}
static void run_case(const char*name,std::vector<Brush>&B,float vs){
V3 org{-3,-3,-3};int G=(int)ceilf(6.0f/vs)+1;
auto D=[&](int i,int j,int k){return usdf({org.x+i*vs,org.y+j*vs,org.z+k*vs},B);};
// per approach error counters over boundary surface-samples
long tot=0,err[5]={0,0,0,0,0};
for(int k=0;k<G-1;k++)for(int j=0;j<G-1;j++)for(int i=0;i<G-1;i++){
bool neg=false,pos=false;for(int a=0;a<2;a++)for(int b=0;b<2;b++)for(int c=0;c<2;c++){float d=D(i+a,j+b,k+c);if(d<0)neg=true;else pos=true;}
if(!(neg&&pos))continue;
V3 cc{org.x+(i+.5f)*vs,org.y+(j+.5f)*vs,org.z+(k+.5f)*vs};
// GATE: only cells where quantization bites — full (unmerged) capture has MORE distinct
// materials than CUR's 2-candidate merged capture can represent (crease-oracle lesson).
auto distinct=[](std::vector<Cand>&C){std::vector<int>m;for(auto&c:C)m.push_back(c.mat);
std::sort(m.begin(),m.end());m.erase(std::unique(m.begin(),m.end()),m.end());return (int)m.size();};
{auto full=capture(cc,vs,B,false,8);auto cur=capture(cc,vs,B,true,2);
if(distinct(full)<=distinct(cur))continue;}
// CLEAN sampling: one fragment exactly ON each distinct exposed face present in the cell.
auto face_set=capture(cc,vs,B,false,8); // unmerged: includes coplanar dupes
for(size_t fi=0;fi<face_set.size();fi++){
Cand&f=face_set[fi];
bool dup=false; // skip a face already sampled (same mat+normal)
for(size_t q=0;q<fi;q++) if(face_set[q].mat==f.mat&&fabsf(dot(face_set[q].n,f.n))>0.999f){dup=true;break;}
if(dup)continue;
// project cell centre onto face f's plane
float off=dot(f.n,cc)-f.d;
V3 p{cc.x-f.n.x*off,cc.y-f.n.y*off,cc.z-f.n.z*off};
if(fabsf(p.x-cc.x)>0.75f*vs||fabsf(p.y-cc.y)>0.75f*vs||fabsf(p.z-cc.z)>0.75f*vs)continue; // in cell
int g=gt_material(p,B);
if(g!=f.mat)continue; // p must genuinely be on face f (unburied, in extent)
V3 fn=f.n;
auto cCUR=capture(cc,vs,B,true,2);
auto cP1 =capture(cc,vs,B,true,8);
auto cP3 =capture(cc,vs,B,true,3);
auto cP4 =capture(cc,vs,B,false,8);
tot++;
if(select_mat(0,cCUR,p,fn,B)!=g)err[0]++;
if(select_mat(1,cP1 ,p,fn,B)!=g)err[1]++;
if(select_mat(2,cP3 ,p,fn,B)!=g)err[2]++;
if(select_mat(3,cP4 ,p,fn,B)!=g)err[3]++;
// P2 multi-material DC: boundary emitted as geometry => material==g by construction (err 0)
}
}
if(!tot){printf("%-14s (no boundary samples)\n",name);return;}
printf("%-16s CUR=%.1f%% P1=%.1f%% P3=%.1f%% P4=%.1f%% P2=%.1f%% (material-error over %ld boundary frags)\n",
name,100.0*err[0]/tot,100.0*err[1]/tot,100.0*err[2]/tot,100.0*err[3]/tot,100.0*err[4]/tot,tot);
}
int main(){
printf("Material-error rate at boundary fragments (lower = correcter). vs=0.25\n");
printf(" CUR=current(2cand,merged) P1=point-loc P3=3cand P4=unmerge+extent P2=multi-material-DC\n");
float vs=0.25f;
{ // Case A: 3-way concave corner
std::vector<Brush> B={ box({-2,-2,-2},{2,2,0},1), box({0,-2,0},{2,2,3},2), box({-2,0,0},{2,2,3},3) };
run_case("A 3-way corner",B,vs);
}
{ // Case B: coplanar split (two floors share z=0, boundary x=0)
std::vector<Brush> B={ box({-2,-2,-2},{0,2,0},1), box({0,-2,-2},{2,2,0},2) };
run_case("B coplanar",B,vs);
}
return 0;
}