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:
1 parent
f67b519e75
commit
61633cb492
18 files changed
+1708
-142
No files matched your search
@@ -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});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user