wip(assembler): M1 round 6 -- owner walk fixes: 25 cm geometry scale, one height datum, carve/fill sign, faster relief meshing

Owner saw content floating above native terrain, spiky triangles, reversed carve/fill and late relief.
Causes: shared mesher placed Core tiles on a 12.5 cm grid; content/atmosphere asked strate ranges in
12.5 cm voxels; Core edits add density where the legacy diff subtracts. Horizon LOD8 bridge edge
5.657 -> 2.265 cells. Relief mesh p95 60.8 -> 42.0 ms (LOD0). New BuildHost checks for scale, height,
blueprint carve/fill and per-kind streaming lag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-09 20:39:24 +02:00
1 parent f67b519e75
commit 61633cb492
18 files changed
+1708 -142

No files matched your search

+44 -31
View File
@@ -976,47 +976,60 @@ inline void AppendFarStratumShells(const FStandaloneOwnerWorld& World,const FSta
const double Offset=-(GX*(PX-X)+GY*(PY-Y))/GZ;
return std::clamp(Crossing+Offset,ZMin,ZMax);
};
const int32 Base=static_cast<int32>(NewVertices.size());
const double Xs[4]={X0,X1,X1,X0},Ys[4]={Y0,Y0,Y1,Y1};
std::array<double,4> CornerZ{};
std::array<FVector,4> CornerNormals{};
// Les correctifs lointains suivent chaque cellule de la grille, sans grande nappe.
// Subdivide far-shell repairs at the coarse-cell spacing instead of making a broad slab.
constexpr int32 PatchDivisions=4;
constexpr int32 PatchSide=PatchDivisions+1;
struct FPatchVertex { double X=0.0,Y=0.0,Z=0.0;FVector Normal=FVector(0,0,1); };
std::array<FPatchVertex,PatchSide*PatchSide> Patch{};
bool bValidPatch=true;
for(int32 Corner=0;Corner<4;++Corner)
for(int32 PY=0;PY<PatchSide&&bValidPatch;++PY)
for(int32 PX=0;PX<PatchSide;++PX)
{
const double PredictedZ=SurfaceZ(Xs[Corner],Ys[Corner]);
const std::vector<double> CornerCrossings=FindFarStratumCrossings(
Shape,Xs[Corner],Ys[Corner],ZMin,ZMax);
if(CornerCrossings.empty()){bValidPatch=false;break;}
CornerZ[Corner]=*std::min_element(CornerCrossings.begin(),CornerCrossings.end(),
FPatchVertex& Vertex=Patch[static_cast<std::size_t>(PY*PatchSide+PX)];
Vertex.X=X0+(X1-X0)*static_cast<double>(PX)/PatchDivisions;
Vertex.Y=Y0+(Y1-Y0)*static_cast<double>(PY)/PatchDivisions;
const double PredictedZ=SurfaceZ(Vertex.X,Vertex.Y);
const std::vector<double> Crossings=FindFarStratumCrossings(
Shape,Vertex.X,Vertex.Y,ZMin,ZMax);
if(Crossings.empty()){bValidPatch=false;break;}
Vertex.Z=*std::min_element(Crossings.begin(),Crossings.end(),
[PredictedZ](double A,double B)
{return std::abs(A-PredictedZ)<std::abs(B-PredictedZ);});
const float CornerGX=Density(Xs[Corner]+2,Ys[Corner],CornerZ[Corner])
-Density(Xs[Corner]-2,Ys[Corner],CornerZ[Corner]);
const float CornerGY=Density(Xs[Corner],Ys[Corner]+2,CornerZ[Corner])
-Density(Xs[Corner],Ys[Corner]-2,CornerZ[Corner]);
const float CornerGZ=Density(Xs[Corner],Ys[Corner],CornerZ[Corner]+2)
-Density(Xs[Corner],Ys[Corner],CornerZ[Corner]-2);
CornerNormals[Corner]=FVector(CornerGX,CornerGY,CornerGZ);
if(CornerNormals[Corner].SizeSquared()<1.0e-8)
CornerNormals[Corner]=Normal;
else CornerNormals[Corner].Normalize();
const float VertexGX=Density(Vertex.X+2,Vertex.Y,Vertex.Z)
-Density(Vertex.X-2,Vertex.Y,Vertex.Z);
const float VertexGY=Density(Vertex.X,Vertex.Y+2,Vertex.Z)
-Density(Vertex.X,Vertex.Y-2,Vertex.Z);
const float VertexGZ=Density(Vertex.X,Vertex.Y,Vertex.Z+2)
-Density(Vertex.X,Vertex.Y,Vertex.Z-2);
Vertex.Normal=FVector(VertexGX,VertexGY,VertexGZ);
if(Vertex.Normal.SizeSquared()<1.0e-8)Vertex.Normal=Normal;
else Vertex.Normal.Normalize();
}
// A center crossing alone cannot justify a large tangent quad: it can
// bridge air over an isolated far-LOD feature and draw as a horizon slab.
// Keep the fallback only when the source field has a matching crossing
// at every corner, and place each corner on that source crossing.
// Une seule traversee centrale ne justifie pas une grande nappe de terrain.
// A center crossing alone cannot justify a broad patch that bridges horizon relief.
// Require a source-field crossing at every coarse-cell sample before emitting it.
if(!bValidPatch)continue;
for(int32 Corner=0;Corner<4;++Corner)
const int32 Base=static_cast<int32>(NewVertices.size());
for(const FPatchVertex& Vertex:Patch)
{
NewVertices.emplace_back(Xs[Corner]*25.0,Ys[Corner]*25.0,CornerZ[Corner]*25.0);
NewNormals.push_back(CornerNormals[Corner]);
if(bHasUVs)NewUVs.emplace_back(static_cast<float>((Xs[Corner]-Origin.X)/256.0),
static_cast<float>((Ys[Corner]-Origin.Y)/256.0));
NewVertices.emplace_back(Vertex.X*25.0,Vertex.Y*25.0,Vertex.Z*25.0);
NewNormals.push_back(Vertex.Normal);
if(bHasUVs)NewUVs.emplace_back(static_cast<float>((Vertex.X-Origin.X)/256.0),
static_cast<float>((Vertex.Y-Origin.Y)/256.0));
if(bHasColors)NewColors.push_back(Color);
}
const bool bBelowSolid=Density(X,Y,Crossing-1.0)<0.0f;
if(bBelowSolid)NewTriangles.insert(NewTriangles.end(),{Base,Base+1,Base+3,Base+1,Base+2,Base+3});
else NewTriangles.insert(NewTriangles.end(),{Base,Base+3,Base+1,Base+1,Base+3,Base+2});
for(int32 PY=0;PY<PatchDivisions;++PY)
for(int32 PX=0;PX<PatchDivisions;++PX)
{
const int32 I0=Base+PY*PatchSide+PX;
const int32 I1=I0+1;
const int32 I2=I1+PatchSide;
const int32 I3=I0+PatchSide;
if(bBelowSolid)NewTriangles.insert(NewTriangles.end(),{I0,I1,I3,I1,I2,I3});
else NewTriangles.insert(NewTriangles.end(),{I0,I3,I1,I1,I3,I2});
}
}
}
}