wip(assembler): M1 round 1 -- manifest format, block contract, bound audit, native Heightfield2D
Luna, M1 round 1 of 4 (plan in Standalone/ASSEMBLER_M1_REPORT.md): - Versioned fixed-scale world manifest (the shipped plan format) and the Core block contract: point evaluation, interval-or-unknown, influence bound, detail envelope, channels. - Interval audit: 256 boxes / 6,912 samples, 0 missed, 0 unknown; a deliberately false bound is detected (negative control). - Native Heightfield2D with biome input: 4,096-point hash fae2721c052aec5c identical at W1/W8/W16; point queries 2.059 ms vs 62.115 ms legacy -- but NOT a match yet (mean density error 274 voxels, 2,261 sign mismatches vs the legacy surface). 45 mesh-free Core queries match direct evaluation exactly. Existing cave world unchanged (walkability, 64/64 tiles). - Open: collision readiness, a valid full-view timing (viewer captures were occluded/empty; top-down height maps are the only usable pair). Also carries the design doc's owner decisions (fixed water; player edits unrestricted, route law for automatic changes only) and three plugin files + BuildHost scripts that earlier round commits had missed (also fixed on unreal/core-integration as 2574b56). 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
d5ce5d675d
commit
874c796c65
20 files changed
+2880
-12
No files matched your search
@@ -281,11 +281,13 @@ public:
|
||||
bool bInRoundTunnels=false,bool bInProfileDetailBand=false,
|
||||
bool bValidatePassageRoutes=true,
|
||||
const std::vector<VoxelForgeCore::DetailBandTileRegion>& InGpuVisualDetailTiles={},
|
||||
const std::vector<VoxelForgeCore::DetailBandTileRegion>& InCpuVisualDetailTiles={})
|
||||
const std::vector<VoxelForgeCore::DetailBandTileRegion>& InCpuVisualDetailTiles={},
|
||||
const VoxelForgeCore::WorldAssemblyManifest* InWorldAssembly=nullptr)
|
||||
{
|
||||
using Clock=std::chrono::steady_clock;
|
||||
const auto TotalStart=Clock::now();
|
||||
Owner=&World;WorkerCount=std::clamp(Workers,1,16);DetailVariant=Variant;
|
||||
Owner=&World;WorldAssembly=InWorldAssembly;
|
||||
WorkerCount=std::clamp(Workers,1,16);DetailVariant=Variant;
|
||||
Scheduler=InScheduler;RoundTunnelsEnabled=bInRoundTunnels;
|
||||
bAnalyticOnly=false;
|
||||
bProfileDetailBand=bInProfileDetailBand;
|
||||
@@ -919,6 +921,9 @@ public:
|
||||
// edge interpolation and normals to the existing mesher.
|
||||
float DensityAt(float X,float Y,float Z) const
|
||||
{
|
||||
float NativeDensity=0.0f;
|
||||
if(NativeSurfaceDensityAt(X,Y,Z,NativeDensity))
|
||||
return ApplyEditBrushes(NativeDensity,X,Y,Z);
|
||||
if(UsesLegacySurfaceField(X,Y,Z))
|
||||
return LegacySurfaceDensityAt(X,Y,Z);
|
||||
int32 IX=0,IY=0,IZ=0;
|
||||
@@ -931,6 +936,10 @@ public:
|
||||
|
||||
float DensityAtLattice(int32 X,int32 Y,int32 Z) const
|
||||
{
|
||||
float NativeDensity=0.0f;
|
||||
if(NativeSurfaceDensityAt(static_cast<float>(X),static_cast<float>(Y),
|
||||
static_cast<float>(Z),NativeDensity))
|
||||
return ApplyEditBrushes(NativeDensity,X,Y,Z);
|
||||
if(UsesLegacySurfaceField(static_cast<float>(X),static_cast<float>(Y),
|
||||
static_cast<float>(Z)))
|
||||
return LegacySurfaceDensityAt(static_cast<float>(X),static_cast<float>(Y),
|
||||
@@ -945,6 +954,9 @@ public:
|
||||
|
||||
float DensityAtUnclamped(float X,float Y,float Z) const
|
||||
{
|
||||
float NativeDensity=0.0f;
|
||||
if(NativeSurfaceDensityAt(X,Y,Z,NativeDensity))
|
||||
return ApplyEditBrushes(NativeDensity,X,Y,Z);
|
||||
if(UsesLegacySurfaceField(X,Y,Z))
|
||||
return LegacySurfaceDensityAt(X,Y,Z);
|
||||
return AnalyticDensityAt(X,Y,Z,false);
|
||||
@@ -1066,6 +1078,9 @@ private:
|
||||
if(OutFloorInfluence)*OutFloorInfluence=0.0f;
|
||||
if(UsesLegacySurfaceField(X,Y,Z))
|
||||
{
|
||||
float NativeDensity=0.0f;
|
||||
if(NativeSurfaceDensityAt(X,Y,Z,NativeDensity))
|
||||
return bApplyEdits?ApplyEditBrushes(NativeDensity,X,Y,Z):NativeDensity;
|
||||
const float Density=Owner->Generator->GetDensityAt(X,Y,Z);
|
||||
return bApplyEdits?ApplyEditBrushes(Density,X,Y,Z):Density;
|
||||
}
|
||||
@@ -1119,10 +1134,8 @@ public:
|
||||
const int32 Pad=std::max(Step,1);
|
||||
int32 BMin[3]={Origin.X-Pad,Origin.Y-Pad,Origin.Z-Pad};
|
||||
int32 BMax[3]={Origin.X+(Cells+1)*Step,Origin.Y+(Cells+1)*Step,Origin.Z+(Cells+1)*Step};
|
||||
// A primitive-only proof cannot classify the biome heightfield. Keep overlapping
|
||||
// SurfaceWorld boxes mixed so generation samples the exact copied evaluator.
|
||||
// Une preuve fondee sur les primitives ne classe pas la hauteur par biomes; garder les
|
||||
// boites SurfaceWorld en Mixed force l'evaluation exacte lors du maillage.
|
||||
const VoxelForgeCore::WorldAssemblyStratumPlanV1* SurfacePlan=nullptr;
|
||||
bool bSurfaceOverlap=false,bOtherStrateOverlap=false;
|
||||
if(Owner&&Owner->Manager)
|
||||
{
|
||||
const int32 FirstChunk=FMath::FloorToInt(static_cast<float>(BMin[2])
|
||||
@@ -1130,10 +1143,24 @@ public:
|
||||
const int32 LastChunk=FMath::FloorToInt(static_cast<float>(BMax[2])
|
||||
/static_cast<float>(CHUNK_SIZE));
|
||||
for(int32 ChunkZ=FirstChunk;ChunkZ<=LastChunk;++ChunkZ)
|
||||
{
|
||||
if(Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ))
|
||||
==ECaveGeneratorType::SurfaceWorld)
|
||||
return EVoxelTileClass::Mixed;
|
||||
{
|
||||
bSurfaceOverlap=true;
|
||||
const int32 StrateIndex=Owner->Manager->GetStrateIndex(
|
||||
static_cast<float>((static_cast<double>(ChunkZ*CHUNK_SIZE+CHUNK_SIZE/2))*VOXEL_SIZE));
|
||||
const auto* Candidate=FindWorldAssemblyStratum(StrateIndex,ChunkZ);
|
||||
if(!Candidate||(SurfacePlan&&SurfacePlan!=Candidate))
|
||||
return EVoxelTileClass::Mixed;
|
||||
SurfacePlan=Candidate;
|
||||
}
|
||||
else bOtherStrateOverlap=true;
|
||||
}
|
||||
}
|
||||
// A box crossing recipes cannot use one field interval; unknown composition stays Mixed.
|
||||
// Une boite qui traverse plusieurs recettes reste Mixed, car l'intervalle d'un seul champ ne suffit pas.
|
||||
if(bSurfaceOverlap&&bOtherStrateOverlap)return EVoxelTileClass::Mixed;
|
||||
if(!EditBrushes.empty()&&EditBoundsMin.X<=BMax[0]&&EditBoundsMax.X>=BMin[0]
|
||||
&&EditBoundsMin.Y<=BMax[1]&&EditBoundsMax.Y>=BMin[1]
|
||||
&&EditBoundsMin.Z<=BMax[2]&&EditBoundsMax.Z>=BMin[2])
|
||||
@@ -1155,6 +1182,22 @@ public:
|
||||
if(R.Lo<=BMax[2]&&R.Hi>=BMin[2]
|
||||
&&(R.Lo>=BMin[2]||R.Hi<=BMax[2]))
|
||||
return EVoxelTileClass::Mixed;
|
||||
if(bSurfaceOverlap)
|
||||
{
|
||||
if(!WorldAssembly||!SurfacePlan)return EVoxelTileClass::Mixed;
|
||||
const VoxelForgeCore::Heightfield2D Block(WorldAssembly->recipe,*SurfacePlan);
|
||||
const auto Bounds=Block.interval(
|
||||
static_cast<int64>(BMin[0])*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,
|
||||
static_cast<int64>(BMin[1])*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,
|
||||
static_cast<int64>(BMin[2])*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,
|
||||
static_cast<int64>(BMax[0])*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,
|
||||
static_cast<int64>(BMax[1])*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,
|
||||
static_cast<int64>(BMax[2])*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
if(!Bounds.known)return EVoxelTileClass::Mixed;
|
||||
if(Bounds.minimumDensitySubvoxels>0)return EVoxelTileClass::AllAir;
|
||||
if(Bounds.maximumDensitySubvoxels<0)return EVoxelTileClass::AllSolid;
|
||||
return EVoxelTileClass::Mixed;
|
||||
}
|
||||
const bool bRock=EvaluateFast((BMin[0]+BMax[0])*0.5,
|
||||
(BMin[1]+BMax[1])*0.5,(BMin[2]+BMax[2])*0.5);
|
||||
return bRock?EVoxelTileClass::AllSolid:EVoxelTileClass::AllAir;
|
||||
@@ -1619,6 +1662,41 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
const VoxelForgeCore::WorldAssemblyStratumPlanV1* FindWorldAssemblyStratum(
|
||||
int32 StrateIndex,int32 ChunkZ) const
|
||||
{
|
||||
if(!WorldAssembly||StrateIndex<0)return nullptr;
|
||||
const auto It=std::find_if(WorldAssembly->plan.strates.begin(),WorldAssembly->plan.strates.end(),
|
||||
[StrateIndex,ChunkZ](const auto& Entry)
|
||||
{return Entry.strateIndex==StrateIndex&&ChunkZ<=Entry.topChunkZ&&ChunkZ>=Entry.bottomChunkZ;});
|
||||
return It==WorldAssembly->plan.strates.end()?nullptr:&*It;
|
||||
}
|
||||
|
||||
bool NativeSurfaceDensityAt(float X,float Y,float Z,float& OutDensity) const
|
||||
{
|
||||
if(!WorldAssembly||!Owner||!Owner->Manager||!Owner->Generator
|
||||
||!std::isfinite(X)||!std::isfinite(Y)||!std::isfinite(Z))return false;
|
||||
const int32 ChunkZ=FMath::FloorToInt(Z/static_cast<float>(CHUNK_SIZE));
|
||||
if(Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ))
|
||||
!=ECaveGeneratorType::SurfaceWorld)return false;
|
||||
const int32 StrateIndex=Owner->Manager->GetStrateIndex(
|
||||
static_cast<float>((static_cast<double>(ChunkZ*CHUNK_SIZE+CHUNK_SIZE/2))*VOXEL_SIZE));
|
||||
const auto* Plan=FindWorldAssemblyStratum(StrateIndex,ChunkZ);
|
||||
if(!Plan)return false;
|
||||
constexpr double Scale=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
const double MaxVoxel=static_cast<double>(std::numeric_limits<int64>::max())/Scale;
|
||||
if(std::abs(static_cast<double>(X))>MaxVoxel||std::abs(static_cast<double>(Y))>MaxVoxel
|
||||
||std::abs(static_cast<double>(Z))>MaxVoxel)return false;
|
||||
const auto ToFixed=[](float Value)
|
||||
{return static_cast<int64>(std::llround(static_cast<double>(Value)
|
||||
*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel));};
|
||||
const VoxelForgeCore::Heightfield2D Block(WorldAssembly->recipe,*Plan);
|
||||
if(!Block.isValid())return false;
|
||||
OutDensity=static_cast<float>(Block.point(ToFixed(X),ToFixed(Y),ToFixed(Z)).densitySubvoxels
|
||||
/Scale);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UsesLegacySurfaceField(float X,float Y,float Z) const
|
||||
{
|
||||
if(!Owner||!Owner->Manager||!Owner->Generator)return false;
|
||||
@@ -4850,6 +4928,7 @@ private:
|
||||
}
|
||||
|
||||
FStandaloneOwnerWorld* Owner=nullptr;
|
||||
const VoxelForgeCore::WorldAssemblyManifest* WorldAssembly=nullptr;
|
||||
int32 WorkerCount=6;
|
||||
const VoxelForgeCore::JobScheduler* Scheduler=nullptr;
|
||||
bool RoundTunnelsEnabled=false;
|
||||
|
||||
Reference in new issue
Block a user