wip(assembler): round L22 -- one 8 m layout chunk for every strate; real ceiling -> content -> bedrock

StrateLayoutChunkSizeMeters (world setting, default 8 m; owner decision) is resolved once in
the director/adapter and standalone loader; every generator reads the resolved width (no
CHUNK_SIZE / Core-tile inference). Every strate gets solid ceiling and bedrock bands from its
BoundarySealThickness in the shared composition path (diggable by default); faces come from the
shared boundary path. Owner world: FloatingIslands +8..-120 m (128 m, now with a bedrock floor),
SurfaceWorld -120..-632 m (unchanged; full-detail and exact-query hashes equal L21); the 60 m gap is
gone. The strict layout validator passes on the real world, with 0 endpoint dispatch
disagreements. Spawn/benchmark camera derived from route.front. Ledges under the islands gone
(reviewer capture). Evaluator 7, recipe schema 2, layout snapshot 2, FI geometry 4.
Open: SW cap profiles exceed the ceiling band (owner call); transitions still Hard; Unreal
setting/manager/adapter edits uncompiled here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-20 12:03:14 +02:00
1 parent 8753f6dbe4
commit 55c26a05a7
28 files changed
+1432 -579

No files matched your search

+18
View File
@@ -43,6 +43,8 @@ struct FStandaloneOwnerWorld
int32 LODOctaveDrop = 0;
std::uint32_t OwnerVoxelSizeMicrons = 125000;
std::uint32_t CoreVoxelSizeMicrons = 250000;
std::uint32_t StrateLayoutChunkSizeMicrometres = 8000000;
std::int64_t StrateLayoutChunkWidthSubvoxels = 8192;
};
namespace StandaloneOwnerLoader
@@ -378,6 +380,21 @@ namespace StandaloneOwnerLoader
ReadField(Root,"strate_content_cut_min_level",Out.Settings->StrateContentCutMinLevel);
ReadField(SettingsJson,"TotalStrates",Out.Settings->TotalStrates);
ReadField(SettingsJson,"InterStrateGapChunks",Out.Settings->InterStrateGapChunks);
ReadField(SettingsJson,"StrateLayoutChunkSizeMeters",Out.Settings->StrateLayoutChunkSizeMeters);
const VFJson::Value& LayoutChunkMicrometres=Root.Get("strate_layout_chunk_size_um");
const double LayoutChunkSizeMicrometres=LayoutChunkMicrometres.IsNumber()
?LayoutChunkMicrometres.Number()
:static_cast<double>(Out.Settings->StrateLayoutChunkSizeMeters)*1000000.0;
if(!std::isfinite(LayoutChunkSizeMicrometres)||LayoutChunkSizeMicrometres<250000.0
||LayoutChunkSizeMicrometres>64000000.0
||std::abs(LayoutChunkSizeMicrometres-std::round(LayoutChunkSizeMicrometres))>0.001)
{Error="strate layout chunk size must be an integral micrometre value between 0.25 m and 64 m";return false;}
Out.StrateLayoutChunkSizeMicrometres=static_cast<std::uint32_t>(std::llround(LayoutChunkSizeMicrometres));
Out.Settings->StrateLayoutChunkSizeMeters=static_cast<float>(LayoutChunkSizeMicrometres/1000000.0);
Out.StrateLayoutChunkWidthSubvoxels=static_cast<std::int64_t>(std::llround(
LayoutChunkSizeMicrometres*256.0/CoreMicrons));
if(Out.StrateLayoutChunkWidthSubvoxels<=0)
{Error="strate layout chunk size resolves to no Core subvoxels";return false;}
Out.Settings->StratePool.Reset();
Out.Settings->FixedStrates.Empty();
const auto& Strates = Root.Get("strates").AsArray();
@@ -398,6 +415,7 @@ namespace StandaloneOwnerLoader
Error = "copied strate manager rejected the owner settings snapshot";
return false;
}
Out.Manager->SetHardTransitionFallbackForAssembly(true);
Out.Generator = std::make_unique<UVoxelGenerator>();
Out.Generator->InitializeSettings(Out.Settings.get());
Out.Generator->SetStrateManager(Out.Manager.get());
+148 -34
View File
@@ -17,6 +17,7 @@
#include <iostream>
#include <iterator>
#include <limits>
#include <map>
#include <memory>
#include <string>
#include <string_view>
@@ -66,6 +67,7 @@ namespace
std::filesystem::path ResolvedLayoutOutputPath;
bool ResolvedLayoutOnly=false;
bool AuditResolvedLayoutBoundaries=false;
bool AuditSurfaceClearance=false;
};
bool ParseInteger(std::string_view text,int& output)
@@ -123,6 +125,7 @@ namespace
" --resolved-layout-output=FILE (canonical plan-time layout snapshot)\n"
" --resolved-layout-only (exit after plan and snapshot validation)\n"
" --audit-resolved-layout-boundaries (query Core at each recorded endpoint)\n"
" --audit-surface-clearance (sample biome terrain/caps against the resolved ceiling)\n"
" --auto-surface-camera (place the 27-tile cube on this seed's surface)\n"
" --mesh-coverage (report non-empty tiles and indexed triangles)\n"
" --screen-space-gate (measure display error and historical negative controls)\n"
@@ -142,6 +145,7 @@ namespace
if(arg=="--world-assembler-prototype")options.WorldAssemblerPrototype=true;
else if(arg=="--resolved-layout-only")options.ResolvedLayoutOnly=true;
else if(arg=="--audit-resolved-layout-boundaries")options.AuditResolvedLayoutBoundaries=true;
else if(arg=="--audit-surface-clearance")options.AuditSurfaceClearance=true;
else if(arg=="--stream-benchmark")options.StreamBenchmark=true;
else if(arg=="--stream-pipeline")options.StreamPipeline=true;
else if(arg=="--stream-hash")options.StreamHash=true;
@@ -313,7 +317,11 @@ int main(int argc,char** argv)
FVector cameraVoxels=ownerWorld->ViewerStartVoxels;
if(options.HasCamera)
cameraVoxels=FVector(options.CameraMeters.X*4.0f,options.CameraMeters.Y*4.0f,options.CameraMeters.Z*4.0f);
{
const float VoxelsPerMetre=1000000.0f/std::max<std::uint32_t>(1,ownerWorld->CoreVoxelSizeMicrons);
cameraVoxels=FVector(options.CameraMeters.X*VoxelsPerMetre,
options.CameraMeters.Y*VoxelsPerMetre,options.CameraMeters.Z*VoxelsPerMetre);
}
const auto planStart=Clock::now();
VoxelForgeCore::WorldAssemblyManifest worldAssembly;
@@ -324,22 +332,19 @@ int main(int argc,char** argv)
&&!StandaloneWorldAssemblyPrototype::AttachFloatingIslandWorldAssembly(
*ownerWorld,ownerJsonRoot,worldAssembly,error))
{std::fprintf(stderr,"Floating-island assembly setup failed: %s\n",error.c_str());return 2;}
VoxelForgeCore::ResolvedLayoutSnapshotV1 resolvedLayout;
const bool buildResolvedLayout=!options.ResolvedLayoutOutputPath.empty()
||options.AuditResolvedLayoutBoundaries||options.ResolvedLayoutOnly;
if(buildResolvedLayout)
VoxelForgeCore::ResolvedLayoutSnapshotV2 resolvedLayout;
{
const std::string_view variant=options.FloatingIslandsRecipe?"floating-islands":"surface-world";
if(!StandaloneWorldAssemblyPrototype::BuildResolvedLayoutSnapshot(
*ownerWorld,worldAssembly,variant,resolvedLayout,error))
{std::fprintf(stderr,"Resolved-layout snapshot failed: %s\n",error.c_str());return 2;}
const std::string layoutJson=resolvedLayout.ToJson();
const auto roundTrip=VoxelForgeCore::ResolvedLayoutSnapshotV2::FromJson(layoutJson,error);
if(!roundTrip||roundTrip->ToJson()!=layoutJson||roundTrip->stableIdentity!=resolvedLayout.stableIdentity)
{if(error.empty())error="resolved-layout canonical serialization did not round-trip";
std::fprintf(stderr,"Resolved-layout round trip failed: %s\n",error.c_str());return 2;}
if(!options.ResolvedLayoutOutputPath.empty())
{
const auto roundTrip=VoxelForgeCore::ResolvedLayoutSnapshotV1::FromJson(layoutJson,error);
if(!roundTrip||roundTrip->ToJson()!=layoutJson||roundTrip->stableIdentity!=resolvedLayout.stableIdentity)
{if(error.empty())error="resolved-layout canonical serialization did not round-trip";
std::fprintf(stderr,"Resolved-layout round trip failed: %s\n",error.c_str());return 2;}
if(!options.ResolvedLayoutOutputPath.parent_path().empty())
std::filesystem::create_directories(options.ResolvedLayoutOutputPath.parent_path());
std::ofstream output(options.ResolvedLayoutOutputPath,std::ios::binary);
@@ -347,12 +352,16 @@ int main(int argc,char** argv)
output.write(layoutJson.data(),static_cast<std::streamsize>(layoutJson.size()));
if(!output){std::fprintf(stderr,"Resolved-layout snapshot write failed: %s\n",options.ResolvedLayoutOutputPath.c_str());return 2;}
}
const auto controls=VoxelForgeCore::AuditResolvedLayoutValidatorV1();
std::vector<VoxelForgeCore::ResolvedLayoutIssueV1> compatibilityIssues;
const auto controls=VoxelForgeCore::AuditResolvedLayoutValidatorV2();
std::vector<VoxelForgeCore::ResolvedLayoutIssueV2> compatibilityIssues;
std::string compatibilityError;
const bool compatibilityAccepted=resolvedLayout.validate(
VoxelForgeCore::ResolvedLayoutValidationModeV1::compatibility,
VoxelForgeCore::ResolvedLayoutValidationModeV2::compatibility,
compatibilityIssues,compatibilityError);
std::vector<VoxelForgeCore::ResolvedLayoutIssueV2> strictIssues;
std::string strictError;
const bool strictAccepted=resolvedLayout.validate(
VoxelForgeCore::ResolvedLayoutValidationModeV2::strict,strictIssues,strictError);
std::printf("[ResolvedLayoutSnapshot] schema=%u stable_identity=%s strata=%zu bytes=%zu round_trip=PASS\n",
resolvedLayout.schemaVersion,resolvedLayout.stableIdentity.c_str(),resolvedLayout.strates.size(),layoutJson.size());
std::printf("[ResolvedLayoutCompatibility] accepted=%s issues=%zu error=%s\n",
@@ -362,12 +371,124 @@ int main(int argc,char** argv)
issue.code.c_str(),issue.detail.c_str());
if(!compatibilityAccepted)
{std::fprintf(stderr,"Resolved-layout compatibility validation failed: %s\n",compatibilityError.c_str());return 2;}
std::printf("[ResolvedLayoutStrict] accepted=%s issues=%zu error=%s\n",
strictAccepted?"true":"false",strictIssues.size(),strictError.c_str());
for(const auto& issue:strictIssues)
std::printf("[ResolvedLayoutStrictIssue] code=%s detail=%s\n",issue.code.c_str(),issue.detail.c_str());
if(!strictAccepted)
{std::fprintf(stderr,"Resolved-layout strict validation failed: %s\n",strictError.c_str());return 2;}
std::printf("[ResolvedLayoutValidatorControls] rejected=%u/%u status=%s\n",
controls.controlsRejectedAsExpected,controls.controlsRun,controls.passed?"PASS":"FAIL");
if(!controls.passed)
{for(const auto& failure:controls.failures)std::printf("[ResolvedLayoutValidatorControlFailure] %s %s\n",failure.code.c_str(),failure.detail.c_str());return 2;}
if(!options.ResolvedLayoutOutputPath.empty())
std::printf("[ResolvedLayoutSnapshotOutput] path=%s\n",options.ResolvedLayoutOutputPath.c_str());
if(auto* root=std::get_if<VFJson::Value::Object>(&ownerJsonRoot.Data))
{VFJson::Value layoutText;layoutText.Data=layoutJson;(*root)["resolved_layout_json"]=std::move(layoutText);}
else
{std::fprintf(stderr,"Owner snapshot root cannot carry its resolved layout.\n");return 2;}
if(options.AuditSurfaceClearance)
{
if(options.FloatingIslandsRecipe)
{std::fprintf(stderr,"--audit-surface-clearance requires --recipe=surface-world\n");return 2;}
const auto surface=std::find_if(resolvedLayout.strates.begin(),resolvedLayout.strates.end(),
[](const auto& Stratum){return Stratum.generatorKind=="SurfaceWorld";});
const auto plan=surface==resolvedLayout.strates.end()?worldAssembly.plan.strates.end()
:std::find_if(worldAssembly.plan.strates.begin(),worldAssembly.plan.strates.end(),
[&](const auto& Stratum){return Stratum.strateIndex==surface->strateIndex;});
if(surface==resolvedLayout.strates.end()||plan==worldAssembly.plan.strates.end()
||!surface->ceiling.presentInCurrentField)
{std::fprintf(stderr,"SurfaceWorld clearance audit could not resolve its plan/ceiling\n");return 2;}
const VoxelForgeCore::Heightfield2D Field(worldAssembly.recipe,*plan);
if(!Field.isValid())
{std::fprintf(stderr,"SurfaceWorld clearance audit field is invalid\n");return 2;}
constexpr std::int64_t HalfExtentMetres=2048,StepMetres=1;
const double CoreMicrometres=std::max<std::uint32_t>(1,ownerWorld->CoreVoxelSizeMicrons);
const double MetresPerSubvoxel=CoreMicrometres
/(1000000.0*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
double CenterX=0.0,CenterY=0.0;
if(!plan->route.empty())
{CenterX=plan->route.front().positionSubvoxels[0]*MetresPerSubvoxel;
CenterY=plan->route.front().positionSubvoxels[1]*MetresPerSubvoxel;}
const std::int64_t CenterXQ=static_cast<std::int64_t>(std::llround(
CenterX/MetresPerSubvoxel));
const std::int64_t CenterYQ=static_cast<std::int64_t>(std::llround(
CenterY/MetresPerSubvoxel));
const std::int64_t StepQ=static_cast<std::int64_t>(std::llround(
StepMetres/MetresPerSubvoxel));
struct FBiomeClearance
{
std::uint64_t Samples=0;
double MaximumGround=-std::numeric_limits<double>::infinity();
double MaximumCap=-std::numeric_limits<double>::infinity();
double MaximumSurface=-std::numeric_limits<double>::infinity();
double MinimumClearance=std::numeric_limits<double>::infinity();
double AtX=0.0,AtY=0.0,AtHeight=0.0;
std::int64_t MaximumCapQ=std::numeric_limits<std::int64_t>::min();
};
std::map<std::uint32_t,FBiomeClearance> ByBiome;
const double CeilingFace=static_cast<double>(surface->ceiling.bottomMicrometres)/1000000.0;
for(std::int64_t Y=-HalfExtentMetres;Y<=HalfExtentMetres;Y+=StepMetres)
for(std::int64_t X=-HalfExtentMetres;X<=HalfExtentMetres;X+=StepMetres)
{
const auto Sample=Field.point(CenterXQ+(X/StepMetres)*StepQ,
CenterYQ+(Y/StepMetres)*StepQ,0);
const double Ground=Sample.groundHeightSubvoxels*MetresPerSubvoxel;
const double Cap=Sample.capHeightSubvoxels*MetresPerSubvoxel;
const double SurfaceHeight=std::max(Ground,Cap);
auto& Stats=ByBiome[Sample.biomeId];
++Stats.Samples;
Stats.MaximumGround=std::max(Stats.MaximumGround,Ground);
Stats.MaximumCap=std::max(Stats.MaximumCap,Cap);
Stats.MaximumCapQ=std::max(Stats.MaximumCapQ,Sample.capHeightSubvoxels);
Stats.MaximumSurface=std::max(Stats.MaximumSurface,SurfaceHeight);
const double Clearance=CeilingFace-SurfaceHeight;
if(Clearance<Stats.MinimumClearance)
{Stats.MinimumClearance=Clearance;Stats.AtX=CenterX+X;Stats.AtY=CenterY+Y;
Stats.AtHeight=SurfaceHeight;}
}
double MaximumRouteZ=-std::numeric_limits<double>::infinity();
for(const auto& Point:plan->route)
MaximumRouteZ=std::max(MaximumRouteZ,Point.positionSubvoxels[2]*MetresPerSubvoxel);
const auto CapBounds=Field.capHeightBounds();
const auto RouteSample=plan->route.empty()?VoxelForgeCore::HeightfieldPointSample{}
:Field.point(plan->route.front().positionSubvoxels[0],
plan->route.front().positionSubvoxels[1],0);
std::printf("[SurfaceClearancePlan] has_recipe_override=%s layout_chunk_q=%lld plan_cap_q=%lld cap_bounds_q=(%lld,%lld)\n",
plan->hasRecipeOverride?"true":"false",
static_cast<long long>(worldAssembly.recipe.layoutChunkWidthSubvoxels),
static_cast<long long>(plan->capHeightSubvoxels),
static_cast<long long>(CapBounds[0]),static_cast<long long>(CapBounds[1]));
for(const auto& Profile:worldAssembly.recipe.biomes)
std::printf("[SurfaceClearanceProfile] biome_id=%u has_cap=%s cap_q=%lld undulation_q=%lld detail_q=%lld ridge_q=%lld\n",
Profile.stableId,Profile.hasCapProfile?"true":"false",
static_cast<long long>(Profile.capHeightSubvoxels),
static_cast<long long>(Profile.capUndulationSubvoxels),
static_cast<long long>(Profile.capDetailAmplitudeSubvoxels),
static_cast<long long>(Profile.capRidgeAmplitudeSubvoxels));
std::printf("[SurfaceClearanceAudit] box_m=4096x4096 step_m=%lld samples=%lld ceiling_inner_face_m=%.3f unmodified_field=true\n",
static_cast<long long>(StepMetres),
static_cast<long long>((2*HalfExtentMetres/StepMetres+1)*(2*HalfExtentMetres/StepMetres+1)),
CeilingFace);
std::printf("[SurfaceClearanceCapRange] plan_min_m=%.3f plan_max_m=%.3f spawn_cap_m=%.3f spawn_ground_m=%.3f\n",
CapBounds[0]*MetresPerSubvoxel,CapBounds[1]*MetresPerSubvoxel,
RouteSample.capHeightSubvoxels*MetresPerSubvoxel,
RouteSample.groundHeightSubvoxels*MetresPerSubvoxel);
for(const auto& Feature:plan->features)
std::printf("[SurfaceClearanceFeature] type=%u params_q=(%lld,%lld,%lld,%lld)\n",
Feature.type,static_cast<long long>(Feature.parametersSubvoxels[0]),
static_cast<long long>(Feature.parametersSubvoxels[1]),
static_cast<long long>(Feature.parametersSubvoxels[2]),
static_cast<long long>(Feature.parametersSubvoxels[3]));
for(const auto& [BiomeId,Stats]:ByBiome)
std::printf("[SurfaceClearanceBiome] biome_id=%u samples=%llu max_ground_m=%.3f max_cap_m=%.3f max_cap_q=%lld max_surface_m=%.3f min_clearance_m=%.3f at_m=(%.1f,%.1f,%.3f)\n",
BiomeId,static_cast<unsigned long long>(Stats.Samples),Stats.MaximumGround,
Stats.MaximumCap,static_cast<long long>(Stats.MaximumCapQ),Stats.MaximumSurface,Stats.MinimumClearance,
Stats.AtX,Stats.AtY,Stats.AtHeight);
std::printf("[SurfaceClearanceRoute] route_points=%zu max_route_z_m=%.3f clearance_m=%.3f status=%s\n",
plan->route.size(),MaximumRouteZ,CeilingFace-MaximumRouteZ,
MaximumRouteZ<CeilingFace?"PASS":"COLLISION");
}
if(options.ResolvedLayoutOnly)return 0;
}
if(options.AutoSurfaceCamera)
@@ -391,25 +512,17 @@ int main(int argc,char** argv)
}
else
{
const auto xSubvoxels=static_cast<std::int64_t>(std::llround(cameraVoxels.X*UnitsPerVoxel));
const auto ySubvoxels=static_cast<std::int64_t>(std::llround(cameraVoxels.Y*UnitsPerVoxel));
const auto currentZSubvoxels=static_cast<std::int64_t>(std::llround(cameraVoxels.Z*UnitsPerVoxel));
std::int64_t selectedGround=0,bestDistance=std::numeric_limits<std::int64_t>::max();
for(const auto& stratum:worldAssembly.plan.strates)
{
VoxelForgeCore::Heightfield2D field(worldAssembly.recipe,stratum);
if(!field.isValid())continue;
const auto column=field.point(xSubvoxels,ySubvoxels,0);
const std::int64_t distance=std::abs(column.groundHeightSubvoxels-currentZSubvoxels);
if(distance<bestDistance)
{bestDistance=distance;selectedGround=column.groundHeightSubvoxels;}
}
if(bestDistance==std::numeric_limits<std::int64_t>::max())
{std::fprintf(stderr,"[SeedSurfacePlacement] FAIL: no valid SurfaceWorld heightfield\n");return 2;}
cameraVoxels.Z=static_cast<float>(static_cast<double>(selectedGround)/UnitsPerVoxel);
std::printf("[SeedSurfacePlacement] recipe=surface-world seed=%d ground_voxel=%.6f camera_voxel=(%.6f,%.6f,%.6f) status=PASS\n",
seed,static_cast<double>(selectedGround)/UnitsPerVoxel,
cameraVoxels.X,cameraVoxels.Y,cameraVoxels.Z);
if(worldAssembly.plan.strates.empty()||worldAssembly.plan.strates.front().route.empty())
{std::fprintf(stderr,"[SeedSurfacePlacement] FAIL: route-derived spawn is missing\n");return 2;}
const auto& spawn=worldAssembly.plan.strates.front().route.front();
cameraVoxels={static_cast<float>(static_cast<double>(spawn.positionSubvoxels[0])/UnitsPerVoxel),
static_cast<float>(static_cast<double>(spawn.positionSubvoxels[1])/UnitsPerVoxel),
static_cast<float>(static_cast<double>(spawn.positionSubvoxels[2])/UnitsPerVoxel)};
std::printf("[SeedSurfacePlacement] recipe=surface-world seed=%d source=route.front position_voxel=(%.6f,%.6f,%.6f) position_m=(%.3f,%.3f,%.3f) status=PASS\n",
seed,cameraVoxels.X,cameraVoxels.Y,cameraVoxels.Z,
cameraVoxels.X*ownerWorld->CoreVoxelSizeMicrons/1000000.0f,
cameraVoxels.Y*ownerWorld->CoreVoxelSizeMicrons/1000000.0f,
cameraVoxels.Z*ownerWorld->CoreVoxelSizeMicrons/1000000.0f);
}
}
std::printf("[WorldAssemblerPlanTiming] total_ms=%.3f status=PASS\n",
@@ -422,8 +535,9 @@ int main(int argc,char** argv)
FStandaloneCoreScheduler scheduler;
const FVector cameraStepVoxels=options.HasCameraStep
?FVector(options.CameraStepMeters.X*4.0f,options.CameraStepMeters.Y*4.0f,
options.CameraStepMeters.Z*4.0f):FVector::ZeroVector;
?FVector(options.CameraStepMeters.X*1000000.0f/std::max<std::uint32_t>(1,ownerWorld->CoreVoxelSizeMicrons),
options.CameraStepMeters.Y*1000000.0f/std::max<std::uint32_t>(1,ownerWorld->CoreVoxelSizeMicrons),
options.CameraStepMeters.Z*1000000.0f/std::max<std::uint32_t>(1,ownerWorld->CoreVoxelSizeMicrons)):FVector::ZeroVector;
return StandaloneStreamBenchmark::RunPipelinedStreamingBenchmark(ownerWorld,snapshot,
cameraVoxels,options.WorkerCount,options.MaxLevel,VoxelForgeCore::DetailVariant::coherent,
scheduler,options.StreamHash,false,false,false,false,coldStart,
@@ -434,5 +548,5 @@ int main(int argc,char** argv)
options.RegionCrossing,options.AuditTileShapeReadiness,
options.DiagnosticSlowShapeCompileMs,options.AuditBasePageEquivalence,
options.AuditRouteParity,options.AuditResolvedLayoutBoundaries,
buildResolvedLayout?&resolvedLayout:nullptr);
&resolvedLayout);
}
+15 -6
View File
@@ -255,7 +255,7 @@ namespace StandaloneStreamBenchmark
bool bAuditTileShapeReadiness=false,int DiagnosticSlowShapeCompileMs=0,
bool bAuditBasePageEquivalence=false,bool bAuditRouteParity=false,
bool bAuditResolvedLayoutBoundaries=false,
const VoxelForgeCore::ResolvedLayoutSnapshotV1* ResolvedLayout=nullptr)
const VoxelForgeCore::ResolvedLayoutSnapshotV2* ResolvedLayout=nullptr)
{
using Clock=std::chrono::steady_clock;
FStandaloneOwnerWorld& World=*OwnerWorld;
@@ -470,7 +470,7 @@ namespace StandaloneStreamBenchmark
std::vector<std::string> candidates;
for(const auto& Stratum:ResolvedLayout->strates)
{
const auto contains=[zMicrometres](const VoxelForgeCore::ResolvedLayoutBandV1& Band)
const auto contains=[zMicrometres](const VoxelForgeCore::ResolvedLayoutBandV2& Band)
{return Band.presentInCurrentField&&zMicrometres>=Band.bottomMicrometres
&&zMicrometres<Band.topMicrometres;};
if(contains(Stratum.ceiling))candidates.push_back(Stratum.generatorKind+"/ceiling");
@@ -482,6 +482,14 @@ namespace StandaloneStreamBenchmark
const std::array<std::int64_t,3> offsets{{
-static_cast<std::int64_t>(World.CoreVoxelSizeMicrons),0,
static_cast<std::int64_t>(World.CoreVoxelSizeMicrons)}};
const std::int64_t stackMinimum=std::min_element(ResolvedLayout->strates.begin(),
ResolvedLayout->strates.end(),[](const auto& A,const auto& B)
{return A.bedrock.bottomMicrometres<B.bedrock.bottomMicrometres;})
->bedrock.bottomMicrometres;
const std::int64_t stackMaximum=std::max_element(ResolvedLayout->strates.begin(),
ResolvedLayout->strates.end(),[](const auto& A,const auto& B)
{return A.ceiling.topMicrometres<B.ceiling.topMicrometres;})
->ceiling.topMicrometres;
for(const std::int64_t Endpoint:endpoints)
for(const std::int64_t Offset:offsets)
{
@@ -501,16 +509,17 @@ namespace StandaloneStreamBenchmark
if(std::find(expectedGenerators.begin(),expectedGenerators.end(),Kind)==expectedGenerators.end())
expectedGenerators.push_back(Kind);
}
if(expected.empty())expectedLabel="uncovered";
const float density=Shape.densityAt(position);
const bool solid=density<0.0f;
const bool outsideStack=zMicrometres<stackMinimum||zMicrometres>=stackMaximum;
if(expected.empty())expectedLabel=outsideStack?"outside-stack/air":"uncovered";
std::string actualGenerator;
if(actual=="SurfaceWorld-native"||actual=="SurfaceWorld-legacy")actualGenerator="SurfaceWorld";
else if(actual=="FloatingIslands-native")actualGenerator="FloatingIslands";
else if(actual.rfind("compiled-analytic/",0)==0)actualGenerator=actual.substr(18);
const bool mismatch=expected.empty()
?actual!="compiled-analytic/fallback"
?(!outsideStack||solid)
:std::find(expectedGenerators.begin(),expectedGenerators.end(),actualGenerator)==expectedGenerators.end();
const float density=Shape.densityAt(position);
const bool solid=density<0.0f;
++probes;disagreements+=mismatch?1u:0u;
std::printf("[ResolvedLayoutProbe] endpoint_um=%lld offset_um=%lld z_m=%.3f expected=%s actual=%s density=%.7g occupancy=%s disagreement=%s\n",
static_cast<long long>(Endpoint),static_cast<long long>(Offset),
@@ -322,7 +322,7 @@ namespace
const FVector ChunkCenter(
(ChunkCoord.X + 0.5f) * (float)CHUNK_SIZE,
(ChunkCoord.Y + 0.5f) * (float)CHUNK_SIZE,
(ChunkCoord.Z + 0.5f) * (float)CHUNK_SIZE);
(ChunkCoord.Z + 0.5f) * Manager->GetStrateLayoutChunkWidthVoxels());
const float ChunkRadius = (float)CHUNK_SIZE * 0.8660254f + 3.0f;
const TArray<FVoxelPassage>& Passages = Manager->GetPassages();
Cache.FloorProjections.SetNum(Passages.Num());
@@ -1104,7 +1104,7 @@ static bool VF_FindWalkableTunnelAir(
const FIntVector ChunkCoord(
FMath::FloorToInt(Position.X / (float)CHUNK_SIZE),
FMath::FloorToInt(Position.Y / (float)CHUNK_SIZE),
FMath::FloorToInt(Position.Z / (float)CHUNK_SIZE));
FMath::FloorToInt(Position.Z / Manager->GetStrateLayoutChunkWidthVoxels()));
const TArray<int32>& Nearby = VF_GetNearbyPassages(Manager, ChunkCoord);
const TArray<FVoxelPassage>& Passages = Manager->GetPassages();
const FVoxelPassage* PassageData = Passages.GetData();
@@ -1139,7 +1139,7 @@ static bool VF_IsAnyPassageFloorAt(
const FIntVector ChunkCoord(
FMath::FloorToInt(Position.X / (float)CHUNK_SIZE),
FMath::FloorToInt(Position.Y / (float)CHUNK_SIZE),
FMath::FloorToInt(Position.Z / (float)CHUNK_SIZE));
FMath::FloorToInt(Position.Z / Manager->GetStrateLayoutChunkWidthVoxels()));
const TArray<int32>& Nearby = VF_GetNearbyPassages(Manager, ChunkCoord);
const TArray<FVoxelPassage>& Passages = Manager->GetPassages();
const FVoxelPassage* PassageData = Passages.GetData();
@@ -1170,6 +1170,17 @@ bool UVoxelStrateManager::Initialize(UVoxelSettings* Settings, int32 WorldSeed)
UE_LOG(LogTemp, Error, TEXT("[StrateManager] No settings provided!"));
return false;
}
if (!FMath::IsFinite(Settings->StrateLayoutChunkSizeMeters)
|| Settings->StrateLayoutChunkSizeMeters < 0.25f
|| Settings->StrateLayoutChunkSizeMeters > 64.0f)
{
UE_LOG(LogTemp, Error, TEXT("[StrateManager] StrateLayoutChunkSizeMeters must be in [0.25, 64] m."));
return false;
}
StrateLayoutChunkSizeMicrometres = static_cast<uint32>(std::llround(
static_cast<double>(Settings->StrateLayoutChunkSizeMeters) * 1000000.0));
StrateLayoutChunkWidthVoxels = static_cast<float>(
static_cast<double>(StrateLayoutChunkSizeMicrometres) / (10000.0 * VOXEL_SIZE));
FVoxelSeasonManifest SeasonManifest;
bool bUseSeason = false;
@@ -1343,10 +1354,10 @@ bool UVoxelStrateManager::Initialize(UVoxelSettings* Settings, int32 WorldSeed)
Slot.Definition->FloatingIslandParams = SeasonStrate.Params.FloatingIslandParams;
Slot.HeightInChunks = SeasonStrate.HeightInChunks;
Slot.TopChunkZ = SeasonStrate.TopWorldZ / CHUNK_SIZE - 1;
Slot.BottomChunkZ = SeasonStrate.BottomWorldZ / CHUNK_SIZE;
if (SeasonStrate.TopWorldZ % CHUNK_SIZE != 0
|| SeasonStrate.BottomWorldZ % CHUNK_SIZE != 0
Slot.TopChunkZ = SeasonStrate.TopWorldZ / FMath::RoundToInt(StrateLayoutChunkWidthVoxels) - 1;
Slot.BottomChunkZ = SeasonStrate.BottomWorldZ / FMath::RoundToInt(StrateLayoutChunkWidthVoxels);
if (SeasonStrate.TopWorldZ % FMath::RoundToInt(StrateLayoutChunkWidthVoxels) != 0
|| SeasonStrate.BottomWorldZ % FMath::RoundToInt(StrateLayoutChunkWidthVoxels) != 0
|| Slot.TopChunkZ != CurrentTopZ
|| Slot.BottomChunkZ != CurrentTopZ - (Slot.HeightInChunks - 1))
{
@@ -1526,8 +1537,8 @@ bool UVoxelStrateManager::SetComposerOverrideForStrate(
}
VF_SetComposerRuntimeBounds(
Override->Params,
(float)(TargetSlot->TopChunkZ + 1) * CHUNK_SIZE,
(float)TargetSlot->BottomChunkZ * CHUNK_SIZE);
(float)(TargetSlot->TopChunkZ + 1) * StrateLayoutChunkWidthVoxels,
(float)TargetSlot->BottomChunkZ * StrateLayoutChunkWidthVoxels);
Override->bUseRecipe = bUseRecipe;
if (Recipe != nullptr)
{
@@ -1546,8 +1557,8 @@ bool UVoxelStrateManager::SetComposerOverrideForStrate(
Override->bUseRegions = true;
Override->Regions = *InRegions;
VF_RekeyStrateRegionManifest(Override->Regions, CandidateSeed, StrateIndex);
Override->Regions.StrateTopWorldZ = (float)(TargetSlot->TopChunkZ + 1) * CHUNK_SIZE;
Override->Regions.StrateBottomWorldZ = (float)TargetSlot->BottomChunkZ * CHUNK_SIZE;
Override->Regions.StrateTopWorldZ = (float)(TargetSlot->TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
Override->Regions.StrateBottomWorldZ = (float)TargetSlot->BottomChunkZ * StrateLayoutChunkWidthVoxels;
VF_SetStrateArchetypeRuntimeBounds(Override->Params,
Override->Regions.StrateTopWorldZ,
Override->Regions.StrateBottomWorldZ);
@@ -1797,11 +1808,11 @@ void UVoxelStrateManager::GeneratePassages()
const FStratePassageConfig& Cfg = UpperDef->PassageConfig;
// Upper strate floor and lower strate ceiling (differ when there's a bedrock gap).
const float UpperTopZ = (float)(Upper.TopChunkZ + 1) * CHUNK_SIZE;
const float UpperBottomZ = (float)(Upper.BottomChunkZ) * CHUNK_SIZE;
const float LowerTopZ = (float)(Lower.TopChunkZ + 1) * CHUNK_SIZE;
const float UpperMax = (float)Upper.HeightInChunks * CHUNK_SIZE * 0.9f;
const float LowerMax = (float)Lower.HeightInChunks * CHUNK_SIZE * 0.9f;
const float UpperTopZ = (float)(Upper.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
const float UpperBottomZ = (float)(Upper.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
const float LowerTopZ = (float)(Lower.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
const float UpperMax = (float)Upper.HeightInChunks * StrateLayoutChunkWidthVoxels * 0.9f;
const float LowerMax = (float)Lower.HeightInChunks * StrateLayoutChunkWidthVoxels * 0.9f;
const float DistLo = FMath::Min(Cfg.DistanceMin, Cfg.DistanceMax);
const float DistHi = FMath::Max(Cfg.DistanceMin, Cfg.DistanceMax);
@@ -1931,7 +1942,7 @@ void UVoxelStrateManager::GeneratePassages()
LowerDef->FloatingIslandParams,
LowerQuerySeed,
LowerTopZ,
(float)(Lower.BottomChunkZ) * CHUNK_SIZE,
(float)(Lower.BottomChunkZ) * StrateLayoutChunkWidthVoxels,
PX,
PY,
MaxLateralSnap,
@@ -1991,7 +2002,7 @@ void UVoxelStrateManager::GeneratePassages()
if (bAimedLowerAtPlayerFitPoint)
{
const float LowerSealThickness = BoundarySealThicknessFor(*LowerDef);
const float LowerBottomZ = (float)(Lower.BottomChunkZ) * CHUNK_SIZE;
const float LowerBottomZ = (float)(Lower.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
const float InnerBottomZ = LowerBottomZ + LowerSealThickness;
const float InnerTopZ = LowerTopZ - LowerSealThickness;
if (VoxelMath::IsFinite(SuggestedLowerPoint.X)
@@ -2159,7 +2170,7 @@ void UVoxelStrateManager::GeneratePassages()
Cfg.MouthRadius,
LowerDoorDirection,
LowerTopZ,
(float)(Lower.BottomChunkZ) * CHUNK_SIZE,
(float)(Lower.BottomChunkZ) * StrateLayoutChunkWidthVoxels,
LowerDef ? BoundarySealThicknessFor(*LowerDef) : 0.0f,
bAimedLowerAtPlayerFitPoint);
Passage.UpperPoint = Passage.UpperLanding.StandingPoint;
@@ -2584,8 +2595,8 @@ void UVoxelStrateManager::GeneratePassages()
if (bOpenSurfaceEntry && OriginSpineRadius > 0.0f && StrateLayout.Num() > 0)
{
const FStrateSlot& Top = StrateLayout[0];
const float TopZ = (float)(Top.TopChunkZ + 1) * CHUNK_SIZE;
const float BottomZ = (float)Top.BottomChunkZ * CHUNK_SIZE;
const float TopZ = (float)(Top.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
const float BottomZ = (float)Top.BottomChunkZ * StrateLayoutChunkWidthVoxels;
const float TopSeal = Top.Definition
? BoundarySealThicknessFor(*Top.Definition) : 0.0f;
const VoxelPassageGeometry::FOriginLandingGeometry OriginLanding =
@@ -2608,7 +2619,7 @@ void UVoxelStrateManager::GeneratePassages()
// the top seal into the finite landing room; lower strate seals remain untouched.
Entry.Radius = FMath::Max(OriginSpineRadius,
VoxelPassageGeometry::MetersToVoxels(0.25f));
Entry.UpperPoint = FVector(0.0f, 0.0f, TopZ + CHUNK_SIZE);
Entry.UpperPoint = FVector(0.0f, 0.0f, TopZ + StrateLayoutChunkWidthVoxels);
// The capsule's lower tangent meets the room ceiling; it never bores through the
// room's floor or creates an origin column in strate 0.
Entry.LowerPoint = FVector(
@@ -2672,7 +2683,7 @@ float UVoxelStrateManager::EvaluateModifierSDF(float WorldX, float WorldY, float
const FIntVector ChunkCoord(
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldZ / (float)CHUNK_SIZE));
FMath::FloorToInt(WorldZ / StrateLayoutChunkWidthVoxels));
// The shortlist is rebuilt once for a (manager, version, chunk) and is shared by the tube,
// landing, and floor paths. No source-fit stencil or topology search is allowed below it.
@@ -2818,7 +2829,7 @@ void UVoxelStrateManager::ApplyPassageModifier(
const FIntVector ChunkCoord(
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldZ / (float)CHUNK_SIZE));
FMath::FloorToInt(WorldZ / StrateLayoutChunkWidthVoxels));
const TArray<int32>& Nearby = VF_GetNearbyPassages(this, ChunkCoord);
const FVoxelPassage* PassageData = Passages.GetData();
const FVector Position(WorldX, WorldY, WorldZ);
@@ -2911,7 +2922,7 @@ void UVoxelStrateManager::ApplyPassageLandingAir(
const FIntVector ChunkCoord(
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldZ / (float)CHUNK_SIZE));
FMath::FloorToInt(WorldZ / StrateLayoutChunkWidthVoxels));
const TArray<int32>& Nearby = VF_GetNearbyPassages(this, ChunkCoord);
if (Nearby.Num() == 0) return;
@@ -3022,7 +3033,7 @@ void UVoxelStrateManager::ApplyPassageLandingFloorMC(
const FIntVector ChunkCoord(
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldZ / (float)CHUNK_SIZE));
FMath::FloorToInt(WorldZ / StrateLayoutChunkWidthVoxels));
const TArray<int32>& Nearby = VF_GetNearbyPassages(this, ChunkCoord);
const FVoxelPassage* PassageData = Passages.GetData();
const FVector Position(WorldX, WorldY, WorldZ);
@@ -3118,7 +3129,7 @@ void UVoxelStrateManager::ApplyPassageStructuralPostsMC(
const FIntVector ChunkCoord(
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldZ / (float)CHUNK_SIZE));
FMath::FloorToInt(WorldZ / StrateLayoutChunkWidthVoxels));
const TArray<int32>& Nearby = VF_GetNearbyPassages(this, ChunkCoord);
if (Nearby.Num() == 0) return;
@@ -3245,7 +3256,7 @@ void UVoxelStrateManager::ApplyPassageNativeFloorMC(
const FIntVector ChunkCoord(
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldZ / (float)CHUNK_SIZE));
FMath::FloorToInt(WorldZ / StrateLayoutChunkWidthVoxels));
const TArray<int32>& Nearby = VF_GetNearbyPassages(this, ChunkCoord);
if (Nearby.Num() == 0
|| VoxelPassageGeometry::VerticalShaftConnectorAirMarker())
@@ -3436,7 +3447,7 @@ void UVoxelStrateManager::ApplyPassageLandingRoomFloorMC(
const FIntVector ChunkCoord(
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldZ / (float)CHUNK_SIZE));
FMath::FloorToInt(WorldZ / StrateLayoutChunkWidthVoxels));
const TArray<int32>& Nearby = VF_GetNearbyPassages(this, ChunkCoord);
const FVoxelPassage* PassageData = Passages.GetData();
const FVector Position(WorldX, WorldY, WorldZ);
@@ -5910,8 +5921,8 @@ bool UVoxelStrateManager::AnyLandingFloorAtLattice(
for (const FStrateSlot& Slot : StrateLayout)
{
if (Slot.Definition == nullptr) continue;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * CHUNK_SIZE;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * CHUNK_SIZE;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * StrateLayoutChunkWidthVoxels;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
const float Seal = VF_BoundarySealThicknessForDefinition(*Slot.Definition);
const VoxelPassageGeometry::FOriginLandingGeometry Geometry =
VoxelPassageGeometry::BuildOriginLandingGeometry(
@@ -5953,8 +5964,8 @@ bool UVoxelStrateManager::AnyOriginLandingNearBox(
for (const FStrateSlot& Slot : StrateLayout)
{
if (Slot.Definition == nullptr) continue;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * CHUNK_SIZE;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * CHUNK_SIZE;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * StrateLayoutChunkWidthVoxels;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
if (VoxelPassageGeometry::OriginLandingRoomTouchesBox(
VoxelBox, TopZ, BottomZ,
VF_BoundarySealThicknessForDefinition(*Slot.Definition),
@@ -5974,8 +5985,8 @@ bool UVoxelStrateManager::AnyOriginLandingFloorNearBox(
for (const FStrateSlot& Slot : StrateLayout)
{
if (Slot.Definition == nullptr) continue;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * CHUNK_SIZE;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * CHUNK_SIZE;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * StrateLayoutChunkWidthVoxels;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
if (VoxelPassageGeometry::OriginLandingFloorTouchesBox(
VoxelBox, TopZ, BottomZ,
VF_BoundarySealThicknessForDefinition(*Slot.Definition),
@@ -5999,8 +6010,8 @@ bool UVoxelStrateManager::AnyOriginLandingNearLattice(
for (const FStrateSlot& Slot : StrateLayout)
{
if (Slot.Definition == nullptr) continue;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * CHUNK_SIZE;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * CHUNK_SIZE;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * StrateLayoutChunkWidthVoxels;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
const float Seal = VF_BoundarySealThicknessForDefinition(*Slot.Definition);
if (VoxelPassageGeometry::OriginLandingRoomTouchesLattice(
VoxelBox, LatticeOrigin, Step, TopZ, BottomZ, Seal, OriginSpineRadius,
@@ -6037,8 +6048,8 @@ bool UVoxelStrateManager::AnyOriginLandingAirNearLattice(
{
continue;
}
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * CHUNK_SIZE;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * CHUNK_SIZE;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * StrateLayoutChunkWidthVoxels;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
const float Seal = VF_BoundarySealThicknessForDefinition(*Slot.Definition);
const VoxelPassageGeometry::FOriginLandingGeometry Geometry =
VoxelPassageGeometry::BuildOriginLandingGeometry(
@@ -6114,8 +6125,8 @@ bool UVoxelStrateManager::AnyOriginLandingFloorNearLattice(
for (const FStrateSlot& Slot : StrateLayout)
{
if (Slot.Definition == nullptr) continue;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * CHUNK_SIZE;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * CHUNK_SIZE;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * StrateLayoutChunkWidthVoxels;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
const float Seal = VF_BoundarySealThicknessForDefinition(*Slot.Definition);
if (VoxelPassageGeometry::OriginLandingFloorTouchesLattice(
VoxelBox, LatticeOrigin, Step, TopZ, BottomZ, Seal, OriginSpineRadius))
@@ -6149,7 +6160,7 @@ int32 UVoxelStrateManager::FindSlotIndexForChunkZ(int32 ChunkZ) const
UVoxelStrateDefinition* UVoxelStrateManager::GetStrateAt(float WorldZ) const
{
// Convert world Z to chunk Z coordinate
int32 ChunkZ = FMath::FloorToInt((WorldZ / VOXEL_SIZE) / CHUNK_SIZE);
int32 ChunkZ = FMath::FloorToInt((WorldZ / VOXEL_SIZE) / StrateLayoutChunkWidthVoxels);
int32 SlotIdx = FindSlotIndexForChunkZ(ChunkZ);
if (SlotIdx >= 0)
{
@@ -6160,7 +6171,7 @@ UVoxelStrateDefinition* UVoxelStrateManager::GetStrateAt(float WorldZ) const
int32 UVoxelStrateManager::GetStrateIndex(float WorldZ) const
{
int32 ChunkZ = FMath::FloorToInt((WorldZ / VOXEL_SIZE) / CHUNK_SIZE);
int32 ChunkZ = FMath::FloorToInt((WorldZ / VOXEL_SIZE) / StrateLayoutChunkWidthVoxels);
int32 SlotIdx = FindSlotIndexForChunkZ(ChunkZ);
if (SlotIdx >= 0)
{
@@ -6319,9 +6330,9 @@ FSlabGenerationParams UVoxelStrateManager::GetSlabParamsForChunk(const FIntVecto
// Fill in the runtime Z bounds (voxel coordinates, same convention as
// FStrateGenerationParams::StrateTopWorldZ / StrateBottomWorldZ).
// TopChunkZ+1 because the top chunk's CEILING is at (TopChunkZ+1)*CHUNK_SIZE.
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
Result.StrateBottomWorldZ = (float)(Slot.BottomChunkZ) * CHUNK_SIZE;
// TopChunkZ+1 because the top chunk's CEILING is at (TopChunkZ+1)*StrateLayoutChunkWidthVoxels.
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
Result.StrateBottomWorldZ = (float)(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
return Result;
}
@@ -6354,8 +6365,8 @@ FMazeGenerationParams UVoxelStrateManager::GetMazeParamsForChunk(const FIntVecto
}
}
#endif
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * CHUNK_SIZE;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * StrateLayoutChunkWidthVoxels;
return Result;
}
@@ -6381,8 +6392,8 @@ FSurfaceGenerationParams UVoxelStrateManager::GetSurfaceParamsForChunk(const FIn
}
}
#endif
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * CHUNK_SIZE;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * StrateLayoutChunkWidthVoxels;
return Result;
}
@@ -6408,8 +6419,8 @@ FVerticalShaftParams UVoxelStrateManager::GetVerticalShaftParamsForChunk(const F
}
}
#endif
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * CHUNK_SIZE;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * StrateLayoutChunkWidthVoxels;
return Result;
}
@@ -6435,8 +6446,8 @@ FFloatingIslandParams UVoxelStrateManager::GetFloatingIslandParamsForChunk(const
}
}
#endif
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * CHUNK_SIZE;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * StrateLayoutChunkWidthVoxels;
return Result;
}
@@ -6470,14 +6481,14 @@ FBiomeContext UVoxelStrateManager::GetBiomeContextForChunk(const FIntVector& Chu
bool UVoxelStrateManager::GetStrateUnrealZRange(float WorldZ, float& OutTopZ, float& OutBottomZ) const
{
const int32 ChunkZ = FMath::FloorToInt((WorldZ / VOXEL_SIZE) / CHUNK_SIZE);
const int32 ChunkZ = FMath::FloorToInt((WorldZ / VOXEL_SIZE) / StrateLayoutChunkWidthVoxels);
const int32 SlotIdx = FindSlotIndexForChunkZ(ChunkZ);
if (SlotIdx < 0) return false;
const FStrateSlot& Slot = StrateLayout[SlotIdx];
// Voxel-space Z bounds → Unreal units. Ceiling = top chunk's upper edge.
OutTopZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE * VOXEL_SIZE;
OutBottomZ = (float)(Slot.BottomChunkZ) * CHUNK_SIZE * VOXEL_SIZE;
OutTopZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels * VOXEL_SIZE;
OutBottomZ = (float)(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels * VOXEL_SIZE;
return true;
}
@@ -6490,8 +6501,8 @@ FStrateDisturbanceParams UVoxelStrateManager::GetDisturbanceParamsForChunk(const
}
const FStrateSlot& Slot = StrateLayout[SlotIdx];
FStrateDisturbanceParams Result = Slot.Definition->Disturbances;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
Result.StrateBottomWorldZ = (float)(Slot.BottomChunkZ) * CHUNK_SIZE;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
Result.StrateBottomWorldZ = (float)(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
return Result;
}
@@ -6523,8 +6534,8 @@ float UVoxelStrateManager::GetWaterLevelWorldZForChunk(const FIntVector& ChunkCo
}
if (Rel <= 0.0f) return -FLT_MAX;
const float BottomZ = (float)(Slot.BottomChunkZ) * CHUNK_SIZE;
const float TopZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
const float BottomZ = (float)(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
const float TopZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
return FMath::Lerp(BottomZ, TopZ, FMath::Clamp(Rel, 0.0f, 1.0f));
}
@@ -6568,8 +6579,8 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
FindComposerOverride(Slot.StrateIndex))
{
FStrateGenerationParams Result = Override->Params.TunnelNetworkParams;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * CHUNK_SIZE;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * StrateLayoutChunkWidthVoxels;
// A candidate is a hard replacement of this slot. Do not blend its rolled vector with an
// authored neighbour at a boundary; this is also how the offline fixture measures it.
return ResolveFinalParams(Result);
@@ -6579,8 +6590,8 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
if (SeasonStrates.IsValidIndex(SlotIdx))
{
FStrateGenerationParams Result = SeasonStrates[SlotIdx].Params.TunnelNetworkParams;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * CHUNK_SIZE;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * StrateLayoutChunkWidthVoxels;
// Season vectors are already final, measured slot records. Vertical structural posts own
// their boundary; blending them with another selected recipe would describe neither one.
return ResolveFinalParams(Result);
@@ -6595,10 +6606,17 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
// to seal the top and bottom with solid rock. This prevents caves from
// carving through strate boundaries.
//
// TopChunkZ=0, CHUNK_SIZE=32: top of the strate = chunk 0's top edge = voxel Z=32
// TopChunkZ=0: top of the strate is one resolved layout chunk above zero.
// BottomChunkZ=-3: bottom of the strate = chunk -3's bottom edge = voxel Z=-3*32 = -96
BaseParams.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
BaseParams.StrateBottomWorldZ = (float)(Slot.BottomChunkZ) * CHUNK_SIZE;
BaseParams.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
BaseParams.StrateBottomWorldZ = (float)(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
// Core composes one field per strate. For this assembly, Gradient and Interleaved
// metadata use the declared Hard fallback across legacy parameter lookups too.
if (bHardTransitionFallbackForAssembly)
{
return ResolveFinalParams(BaseParams);
}
//=========================================================================
// BOUNDARY BLENDING (transition-type aware)
@@ -6671,8 +6689,8 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
if (EffectiveBlend > 0 && DistFromBottom < EffectiveBlend)
{
FStrateGenerationParams BelowParams = BuildParamsFromDefinition(BelowSlot.Definition);
BelowParams.StrateTopWorldZ = (float)(BelowSlot.TopChunkZ + 1) * CHUNK_SIZE;
BelowParams.StrateBottomWorldZ = (float)(BelowSlot.BottomChunkZ) * CHUNK_SIZE;
BelowParams.StrateTopWorldZ = (float)(BelowSlot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
BelowParams.StrateBottomWorldZ = (float)(BelowSlot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
// Linear alpha: 0 at EffectiveBlend chunks away, 1 at the boundary
float Alpha = 1.0f - ((float)DistFromBottom / (float)EffectiveBlend);
@@ -6709,8 +6727,8 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
if (DistFromBottom < CheckRange)
{
FStrateGenerationParams BelowParams = BuildParamsFromDefinition(BelowSlot.Definition);
BelowParams.StrateTopWorldZ = (float)(BelowSlot.TopChunkZ + 1) * CHUNK_SIZE;
BelowParams.StrateBottomWorldZ = (float)(BelowSlot.BottomChunkZ) * CHUNK_SIZE;
BelowParams.StrateTopWorldZ = (float)(BelowSlot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
BelowParams.StrateBottomWorldZ = (float)(BelowSlot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
// Sample 3D Perlin noise to warp the boundary position.
// XY frequency 0.15 gives medium-scale variation (~6-7 chunks per cycle).
@@ -6770,8 +6788,8 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
if (EffectiveBlend > 0 && DistFromTop < EffectiveBlend)
{
FStrateGenerationParams AboveParams = BuildParamsFromDefinition(AboveSlot.Definition);
AboveParams.StrateTopWorldZ = (float)(AboveSlot.TopChunkZ + 1) * CHUNK_SIZE;
AboveParams.StrateBottomWorldZ = (float)(AboveSlot.BottomChunkZ) * CHUNK_SIZE;
AboveParams.StrateTopWorldZ = (float)(AboveSlot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
AboveParams.StrateBottomWorldZ = (float)(AboveSlot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
float Alpha = 1.0f - ((float)DistFromTop / (float)EffectiveBlend);
Alpha = FMath::Clamp(Alpha, 0.0f, 1.0f);
@@ -6792,8 +6810,8 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
if (DistFromTop < CheckRange)
{
FStrateGenerationParams AboveParams = BuildParamsFromDefinition(AboveSlot.Definition);
AboveParams.StrateTopWorldZ = (float)(AboveSlot.TopChunkZ + 1) * CHUNK_SIZE;
AboveParams.StrateBottomWorldZ = (float)(AboveSlot.BottomChunkZ) * CHUNK_SIZE;
AboveParams.StrateTopWorldZ = (float)(AboveSlot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
AboveParams.StrateBottomWorldZ = (float)(AboveSlot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
// Same noise function but with a different seed offset to avoid
// symmetry between top and bottom boundaries of adjacent strates
@@ -391,6 +391,11 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Strates", meta = (ClampMin = "0", ClampMax = "16"))
int32 InterStrateGapChunks = 0;
// Physical width of one vertical strate-layout chunk, independent of Core's 32^3 tile.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Strates|World Assembly",
meta = (ClampMin = "0.25", ClampMax = "64.0", Units = "m"))
float StrateLayoutChunkSizeMeters = 8.0f;
//=========================================================================
// SPINE & INTER-STRATE CONNECTIONS (the (0,0) descent axis)
//=========================================================================
@@ -234,6 +234,10 @@ public:
*/
UFUNCTION(BlueprintCallable, Category = "Strate")
UVoxelStrateDefinition* GetStrateAt(float WorldZ) const;
float GetStrateLayoutChunkWidthVoxels() const { return StrateLayoutChunkWidthVoxels; }
uint32 GetStrateLayoutChunkSizeMicrometres() const { return StrateLayoutChunkSizeMicrometres; }
void SetHardTransitionFallbackForAssembly(bool bEnabled)
{ bHardTransitionFallbackForAssembly = bEnabled; }
/**
* Get the strate index at a world Z coordinate.
@@ -492,6 +496,10 @@ protected:
// The stacked strate layout (index 0 = topmost strate)
UPROPERTY()
TArray<FStrateSlot> StrateLayout;
// Resolved once from VoxelSettings::StrateLayoutChunkSizeMeters during Initialize().
float StrateLayoutChunkWidthVoxels = 32.0f;
uint32 StrateLayoutChunkSizeMicrometres = 8000000;
bool bHardTransitionFallbackForAssembly = false;
// Parallel to StrateLayout only while a cooked season is active. Plain immutable data is
// copied into worker-local caches on the existing layout-versioned refetch path.
+122 -88
View File
@@ -208,14 +208,18 @@ namespace StandaloneWorldAssemblyPrototype
const FIntVector chunk(0,0,surfaceChunkZ);
const FSurfaceGenerationParams surface=owner.Manager->GetSurfaceParamsForChunk(chunk);
const FBiomeContext biomeContext=owner.Manager->GetBiomeContextForChunk(chunk);
const std::int64_t bottomVoxel=static_cast<std::int64_t>(bottomChunkZ)*CHUNK_SIZE;
const std::int64_t heightVoxels=static_cast<std::int64_t>(topChunkZ-bottomChunkZ+1)*CHUNK_SIZE;
const double layoutChunkWidthVoxels=static_cast<double>(owner.StrateLayoutChunkWidthSubvoxels)
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
const std::int64_t bottomVoxel=static_cast<std::int64_t>(std::llround(bottomChunkZ*layoutChunkWidthVoxels));
const std::int64_t heightVoxels=static_cast<std::int64_t>(std::llround(
(topChunkZ-bottomChunkZ+1)*layoutChunkWidthVoxels));
const FBiomeMapParams& map=biomeContext.Map;
const UVoxelStrateDefinition* definition=owner.Manager->GetStrateForChunk(chunk);
VoxelForgeCore::WorldAssemblyManifest manifest;
auto& recipe=manifest.recipe;
recipe.seed=static_cast<uint32>(owner.Seed);
recipe.layoutChunkWidthSubvoxels=owner.StrateLayoutChunkWidthSubvoxels;
recipe.biomeCellSizeSubvoxels=WorldAssemblyFixed(map.CellSize);
recipe.biomeWarpSubvoxels=WorldAssemblyFixed(map.WarpStrength);
recipe.biomeWarpWavelengthSubvoxels=WorldAssemblyWavelength(map.WarpFrequency,556.0);
@@ -291,8 +295,9 @@ namespace StandaloneWorldAssemblyPrototype
if(recipe.biomes.empty())recipe.biomes.push_back(makeProfile(0x1001u,surface,nullptr));
VoxelForgeCore::WorldAssemblyStratumPlanV1 stratum;
stratum.strateIndex=owner.Manager->GetStrateIndex(
static_cast<float>((static_cast<double>(surfaceChunkZ*CHUNK_SIZE+CHUNK_SIZE/2))*VOXEL_SIZE));
stratum.strateIndex=owner.Manager->GetStrateIndex(static_cast<float>(
(static_cast<double>(surfaceChunkZ)+0.5)
*owner.StrateLayoutChunkSizeMicrometres/10000.0));
stratum.topChunkZ=topChunkZ;
stratum.bottomChunkZ=bottomChunkZ;
const double strateBottom=static_cast<double>(bottomVoxel);
@@ -370,7 +375,7 @@ namespace StandaloneWorldAssemblyPrototype
shelf.minimumSubvoxels={WorldAssemblyFixed(minX),WorldAssemblyFixed(minY),
WorldAssemblyFixed(std::max<double>(bottomVoxel,lowestGround-4.0))};
shelf.maximumSubvoxels={WorldAssemblyFixed(maxX),WorldAssemblyFixed(maxY),
WorldAssemblyFixed(std::min<double>((topChunkZ+1)*CHUNK_SIZE,
WorldAssemblyFixed(std::min<double>((topChunkZ+1)*layoutChunkWidthVoxels,
highestGround+height+4.0))};
shelf.parametersSubvoxels={WorldAssemblyFixed(dirX),WorldAssemblyFixed(dirY),
WorldAssemblyFixed(height),WorldAssemblyFixed(reach)};
@@ -418,6 +423,20 @@ namespace StandaloneWorldAssemblyPrototype
if(!rootObject)
{error="owner snapshot root is not a JSON object";return false;}
(*rootObject)["world_assembly"]=std::move(manifestJson);
if(!manifest.plan.strates.empty()&&!manifest.plan.strates.front().route.empty())
{
const auto& spawn=manifest.plan.strates.front().route.front();
VFJson::Value::Object viewer;VFJson::Value found;found.Data=true;viewer["found_open_space"]=std::move(found);
const auto add=[&](const char* key,std::int64_t q)
{VFJson::Value coordinate;coordinate.Data=static_cast<double>(q)/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;viewer[key]=std::move(coordinate);};
add("x",spawn.positionSubvoxels[0]);add("y",spawn.positionSubvoxels[1]);add("z",spawn.positionSubvoxels[2]);
VFJson::Value viewerValue;viewerValue.Data=std::move(viewer);(*rootObject)["viewer_start"]=std::move(viewerValue);
owner.ViewerStartVoxels=FVector(
static_cast<float>(static_cast<double>(spawn.positionSubvoxels[0])/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel),
static_cast<float>(static_cast<double>(spawn.positionSubvoxels[1])/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel),
static_cast<float>(static_cast<double>(spawn.positionSubvoxels[2])/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel));
owner.ViewerStartFound=true;
}
std::printf("[WorldAssemblerPrototype] recipe_version=%u evaluator_version=%u strata=%zu biomes=%zu strate_index=%d chunk_z=%d..%d serialized_bytes=%zu recipe_hash=%016llx plan_hash=%016llx manifest_hash=%016llx source=owner_surface_params_and_biome_map route_points=%zu features=%zu\n",
manifest.recipe.schemaVersion,manifest.recipe.evaluatorVersion,manifest.plan.strates.size(),
manifest.recipe.biomes.size(),manifest.plan.strates.front().strateIndex,topChunkZ,bottomChunkZ,
@@ -486,25 +505,50 @@ namespace StandaloneWorldAssemblyPrototype
{error="owner/Core voxel import ratio is invalid for the floating-island recipe";return false;}
VoxelForgeCore::FloatingIslandRecipeV1 recipe;
const FFloatingIslandParams params=owner.Manager->GetFloatingIslandParamsForChunk(
FIntVector(0,0,islandSlot->TopChunkZ));
recipe.seed=static_cast<std::uint32_t>(owner.Seed);
recipe.geometryVersion=4;
recipe.ownerVoxelSizeMicrons=owner.OwnerVoxelSizeMicrons;
recipe.coreVoxelSizeMicrons=owner.CoreVoxelSizeMicrons;
recipe.ownerToCoreScaleNumerator=owner.OwnerVoxelSizeMicrons/static_cast<std::uint32_t>(divisor);
recipe.ownerToCoreScaleDenominator=owner.CoreVoxelSizeMicrons/static_cast<std::uint32_t>(divisor);
const std::int64_t coreVoxelsPerOwnerChunk=static_cast<std::int64_t>(CHUNK_SIZE)
*recipe.ownerToCoreScaleNumerator/recipe.ownerToCoreScaleDenominator;
const std::int64_t minimumZ=static_cast<std::int64_t>(islandSlot->BottomChunkZ)
*coreVoxelsPerOwnerChunk*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
*owner.StrateLayoutChunkWidthSubvoxels;
const std::int64_t maximumZ=static_cast<std::int64_t>(islandSlot->TopChunkZ+1)
*coreVoxelsPerOwnerChunk*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
*owner.StrateLayoutChunkWidthSubvoxels;
if(maximumZ<=minimumZ)
{error="FloatingIslands strate is too short for a bounded Core recipe";return false;}
recipe.layoutMinimumZSubvoxels=minimumZ;
recipe.layoutMaximumZSubvoxels=maximumZ;
recipe.layoutCenterTopZSubvoxels=minimumZ+(maximumZ-minimumZ)/2;
const double ownerToCoreScale=static_cast<double>(recipe.ownerToCoreScaleNumerator)
/recipe.ownerToCoreScaleDenominator;
recipe.islandDensityQ16=WorldAssemblyQ16(std::clamp<double>(params.IslandDensity,0.0,1.0));
recipe.spacingSubvoxels=WorldAssemblyFixed(params.IslandSpacing*ownerToCoreScale);
recipe.minimumRadiusSubvoxels=WorldAssemblyFixed(params.IslandMinRadius*ownerToCoreScale);
recipe.maximumRadiusSubvoxels=WorldAssemblyFixed(params.IslandMaxRadius*ownerToCoreScale);
recipe.thicknessRatioQ16=WorldAssemblyQ16(params.ThicknessRatio);
recipe.verticalJitterQ16=WorldAssemblyQ16(params.VerticalJitter);
recipe.topFlattenQ16=WorldAssemblyQ16(params.TopFlatten);
recipe.blendRadiusSubvoxels=WorldAssemblyFixed(params.SDFBlendRadius*ownerToCoreScale);
recipe.roughnessSubvoxels=WorldAssemblyFixed(params.SurfaceRoughness*ownerToCoreScale);
recipe.boundarySealSubvoxels=WorldAssemblyFixed(params.BoundarySealThickness*ownerToCoreScale);
auto planned=VoxelForgeCore::PlanFloatingIslandCluster(recipe,error);
if(!planned)return false;
const double metresPerSubvoxel=static_cast<double>(owner.CoreVoxelSizeMicrons)
/(1000000.0*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
const double bedrockTopMetres=(minimumZ+recipe.boundarySealSubvoxels)*metresPerSubvoxel;
const double ceilingBottomMetres=(maximumZ-recipe.boundarySealSubvoxels)*metresPerSubvoxel;
for(const auto& island:planned->plan.islands)
std::printf("[FloatingIslandPlacement] ordinal=%u x_m=%.3f y_m=%.3f bottom_m=%.3f top_m=%.3f bedrock_clearance_m=%.3f ceiling_clearance_m=%.3f\n",
island.ordinal,island.centerSubvoxels[0]*metresPerSubvoxel,
island.centerSubvoxels[1]*metresPerSubvoxel,
island.bottomZSubvoxels*metresPerSubvoxel,
island.topZSubvoxels*metresPerSubvoxel,
island.bottomZSubvoxels*metresPerSubvoxel-bedrockTopMetres,
ceilingBottomMetres-island.topZSubvoxels*metresPerSubvoxel);
VoxelForgeCore::WorldAssemblyFloatingIslandPlanV1 islands;
islands.strateIndex=islandSlot->StrateIndex;
islands.topChunkZ=islandSlot->TopChunkZ;
@@ -554,7 +598,7 @@ namespace StandaloneWorldAssemblyPrototype
inline bool BuildResolvedLayoutSnapshot(const FStandaloneOwnerWorld& owner,
const VoxelForgeCore::WorldAssemblyManifest& assembly,std::string_view recipeVariant,
VoxelForgeCore::ResolvedLayoutSnapshotV1& out,std::string& error)
VoxelForgeCore::ResolvedLayoutSnapshotV2& out,std::string& error)
{
using namespace VoxelForgeCore;
if(!owner.Manager){error="resolved layout builder requires the initialized director";return false;}
@@ -562,120 +606,110 @@ namespace StandaloneWorldAssemblyPrototype
out={};out.worldIdentity="owner-world/seed/"+std::to_string(owner.Seed);
out.ownerVoxelSizeMicrometres=owner.OwnerVoxelSizeMicrons;
out.coreVoxelSizeMicrometres=owner.CoreVoxelSizeMicrons;
const std::int64_t coreMicrons=std::max<std::uint32_t>(1,owner.CoreVoxelSizeMicrons);
const std::int64_t managerChunkMicrons=CHUNK_SIZE*static_cast<std::int64_t>(owner.OwnerVoxelSizeMicrons);
out.strateLayoutChunkSizeMicrometres=owner.StrateLayoutChunkSizeMicrometres;
out.strateLayoutChunkWidthSubvoxels=owner.StrateLayoutChunkWidthSubvoxels;
const auto fixedToMicrons=[&](std::int64_t fixed)
{return static_cast<std::int64_t>(std::llround(static_cast<double>(fixed)
*static_cast<double>(coreMicrons)/(WorldAssemblySubvoxelsPerVoxel)));};
*owner.CoreVoxelSizeMicrons/WorldAssemblySubvoxelsPerVoxel));};
const auto sealAndDensity=[](const UVoxelStrateDefinition& d,ECaveGeneratorType type)
{
std::array<float,2> result{d.GenerationParams.BoundarySealThickness,d.GenerationParams.BaseDensity};
switch(type)
{
case ECaveGeneratorType::SurfaceWorld:result={d.SurfaceParams.BoundarySealThickness,d.SurfaceParams.BaseDensity};break;
case ECaveGeneratorType::FloatingIslands:result={d.FloatingIslandParams.BoundarySealThickness,d.FloatingIslandParams.BaseDensity};break;
case ECaveGeneratorType::Maze:result={d.MazeParams.BoundarySealThickness,d.MazeParams.BaseDensity};break;
case ECaveGeneratorType::VerticalShafts:result={d.VerticalShaftParams.BoundarySealThickness,d.VerticalShaftParams.BaseDensity};break;
default:break;
}
return result;
};
for(const FStrateSlot& slot:owner.Manager->GetLayout())
{
if(!slot.Definition)continue;
const auto type=owner.Manager->GetGeneratorTypeForChunk(FIntVector(0,0,slot.TopChunkZ));
ResolvedLayoutStratumV1 s;s.strateIndex=slot.StrateIndex;s.generatorKind=GeneratorKind(type);
s.stableId=out.worldIdentity+"/strate/"+std::to_string(slot.StrateIndex)
+"/generator/"+GeneratorKind(type);
const auto authored=sealAndDensity(*slot.Definition,type);
const std::int64_t bottom=static_cast<std::int64_t>(slot.BottomChunkZ)*owner.StrateLayoutChunkSizeMicrometres;
const std::int64_t top=static_cast<std::int64_t>(slot.TopChunkZ+1)*owner.StrateLayoutChunkSizeMicrometres;
const std::int64_t seal=static_cast<std::int64_t>(std::llround(
std::max(0.0f,authored[0])*owner.OwnerVoxelSizeMicrons));
if(seal<=0||2*seal>=top-bottom)
{error="strate "+std::to_string(slot.StrateIndex)+" has an invalid BoundarySealThickness for two boundary bands";return false;}
ResolvedLayoutStratumV2 s;s.strateIndex=slot.StrateIndex;s.generatorKind=GeneratorKind(type);
s.stableId=out.worldIdentity+"/strate/"+std::to_string(slot.StrateIndex)+"/generator/"+GeneratorKind(type);
s.recipeId=type==ECaveGeneratorType::SurfaceWorld?"SurfaceWorld.Heightfield2D"
:type==ECaveGeneratorType::FloatingIslands?"FloatingIslands.ClusterField":"legacy."+std::string(GeneratorKind(type));
s.recipeVersion=type==ECaveGeneratorType::SurfaceWorld?assembly.recipe.schemaVersion
:type==ECaveGeneratorType::FloatingIslands&&!assembly.floatingIslands.empty()
?assembly.floatingIslands.front().manifest.recipe.geometryVersion:0;
s.voxelSizeMicrometres=owner.CoreVoxelSizeMicrons;
s.latticeOriginMicrometres={0,0,0};
std::int64_t bottom=static_cast<std::int64_t>(slot.BottomChunkZ)*CHUNK_SIZE*coreMicrons;
std::int64_t top=static_cast<std::int64_t>(slot.TopChunkZ+1)*CHUNK_SIZE*coreMicrons;
s.voxelSizeMicrometres=owner.CoreVoxelSizeMicrons;s.latticeOriginMicrometres={0,0,0};
if(type==ECaveGeneratorType::FloatingIslands)
{
const auto it=std::find_if(assembly.floatingIslands.begin(),assembly.floatingIslands.end(),
[&](const auto& p){return p.strateIndex==slot.StrateIndex;});
if(it!=assembly.floatingIslands.end())
{bottom=fixedToMicrons(it->manifest.recipe.layoutMinimumZSubvoxels);
top=fixedToMicrons(it->manifest.recipe.layoutMaximumZSubvoxels);}
}
s.interiorBottomMicrometres=bottom;s.interiorTopMicrometres=top;
s.content={"content",bottom,top,true,true,false,"current generator interior","none","none"};
s.transitionMode=TransitionKind(slot.Definition->TransitionType);
s.transitionBlendChunks=slot.Definition->TransitionBlendChunks;
s.transitionWidthMicrometres=static_cast<std::int64_t>(s.transitionBlendChunks)*managerChunkMicrons;
s.transitionWarpAmplitudeMicrometres=s.transitionMode=="Interleaved"
?2*managerChunkMicrons:0;
s.transitionInterpenetrationMicrometres=0;
s.transitionHonouredInCore=false;
s.transitionDispatchPath="upper strate owns lower interface; UVoxelStrateManager::GetGenerationParams can blend legacy vectors, but Core's compiled field selects per-strate/native recipe fields without transition blending";
s.managerLookup="UVoxelStrateManager::GetGeneratorTypeForChunk; upper strate TransitionType owns lower interface";
s.managerChunkWidthMicrometres=managerChunkMicrons;
s.dispatchChunkWidthMicrometres=type==ECaveGeneratorType::FloatingIslands
?static_cast<std::int64_t>(CHUNK_SIZE)*owner.OwnerVoxelSizeMicrons
:static_cast<std::int64_t>(CHUNK_SIZE)*coreMicrons;
if(type==ECaveGeneratorType::SurfaceWorld)
{s.boundDispatchPath="BuildSurfaceStratumPlan: (chunkZ*CHUNK_SIZE) Core voxels; 32 Core voxels per chunk";
s.queryDispatchPath="FStandaloneShapeWorld::NativeSurfaceDensityAt: floor(Z/CHUNK_SIZE), manager lookup; 32 Core voxels per chunk";}
else if(type==ECaveGeneratorType::FloatingIslands)
{s.boundDispatchPath="BuildFloatingIslandStratumPlan: owner chunk * 32 * owner/Core voxel ratio";
s.queryDispatchPath="FStandaloneShapeWorld::NativeFloatingIslandDensityAt: floor(Z/(32*owner/Core ratio)), manager lookup";}
else
{s.boundDispatchPath="legacy strata slot bounds scaled by Core chunk width";
s.queryDispatchPath="legacy Core field; manager lookup uses floor(Z/CHUNK_SIZE)";}
const std::int64_t legacySealMicrons=static_cast<std::int64_t>(std::llround(
std::max(0.0f,slot.Definition->GenerationParams.BoundarySealThickness)
*owner.OwnerVoxelSizeMicrons));
if(type==ECaveGeneratorType::FloatingIslands)
{
const auto it=std::find_if(assembly.floatingIslands.begin(),assembly.floatingIslands.end(),
[&](const auto& p){return p.strateIndex==slot.StrateIndex;});
const std::int64_t seal=it==assembly.floatingIslands.end()?0:fixedToMicrons(
it->manifest.recipe.boundarySealSubvoxels);
s.ceiling={"ceiling",top-seal,top,seal>0,true,false,
"v3 shared upper boundary density seal; one sided; inset from upper edge","upper/interior side","VF_ApplyUpperBoundarySeal"};
s.bedrock={"bedrock",bottom,bottom,false,true,false,
"no lower boundary seal in geometryVersion 3","none","none"};
if(it!=assembly.floatingIslands.end())
{
const auto recipeBottom=fixedToMicrons(it->manifest.recipe.layoutMinimumZSubvoxels);
const auto recipeTop=fixedToMicrons(it->manifest.recipe.layoutMaximumZSubvoxels);
if(recipeBottom!=bottom||recipeTop!=top)
{error="FloatingIslands recipe bounds differ from the resolved 8 m layout slot";return false;}
s.recipeVersion=it->manifest.recipe.geometryVersion;
for(std::size_t n=0;n<it->manifest.plan.islands.size();++n)
{const auto& a=it->manifest.plan.islands[n];ResolvedLayoutAnchorV1 anchor;
anchor.kind="island_anchor";anchor.stableId="island/"+std::to_string(n);anchor.units="1/256 Core voxel";
anchor.values={a.centerSubvoxels[0],a.centerSubvoxels[1],a.centerSubvoxels[2],a.radiusSubvoxels,a.topHalfSubvoxels};
s.anchors.push_back(std::move(anchor));}
{
const auto& a=it->manifest.plan.islands[n];ResolvedLayoutAnchorV2 anchor;
anchor.kind="island_anchor";anchor.stableId="island/"+std::to_string(n);anchor.units="1/256 Core voxel";
anchor.values={a.centerSubvoxels[0],a.centerSubvoxels[1],a.centerSubvoxels[2],a.radiusSubvoxels,a.topHalfSubvoxels};
s.anchors.push_back(std::move(anchor));
}
}
}
else if(type==ECaveGeneratorType::SurfaceWorld)
s.interiorBottomMicrometres=bottom;s.interiorTopMicrometres=top;
s.transitionMode=TransitionKind(slot.Definition->TransitionType);
s.transitionBlendChunks=slot.Definition->TransitionBlendChunks;
s.transitionWidthMicrometres=static_cast<std::int64_t>(s.transitionBlendChunks)*owner.StrateLayoutChunkSizeMicrometres;
s.transitionWarpAmplitudeMicrometres=s.transitionMode=="Interleaved"
?2*static_cast<std::int64_t>(owner.StrateLayoutChunkSizeMicrometres):0;
s.transitionInterpenetrationMicrometres=0;
s.transitionHonouredInCore=s.transitionMode=="Hard";
s.transitionFallsBackToHard=s.transitionMode!="Hard";
s.transitionDispatchPath=s.transitionFallsBackToHard
?"declared Gradient/Interleaved recorded; Core uses explicit Hard fallback with zero interpenetration"
:"declared Hard transition applied at the strate interface";
s.managerLookup="UVoxelStrateManager selects the slot from the director-resolved layout chunk width";
s.managerChunkWidthMicrometres=owner.StrateLayoutChunkSizeMicrometres;
s.dispatchChunkWidthMicrometres=owner.StrateLayoutChunkSizeMicrometres;
s.boundDispatchPath="all generator plans use ResolvedLayoutSnapshotV2 layoutChunkWidthSubvoxels";
s.queryDispatchPath="all generator dispatch uses floor(CoreZ / ResolvedLayoutSnapshotV2 layoutChunkWidthSubvoxels)";
const std::int64_t solidDensity=std::max<std::int64_t>(1,WorldAssemblyQ16(std::max(0.05f,authored[1])));
s.ceiling={"ceiling",top-seal,top,true,true,false,"solid ceiling from strate BoundarySealThickness; breakable by default",
"interior-facing lower face","shared resolved-layout density band",solidDensity};
s.content={"content",bottom+seal,top-seal,true,true,false,"resolved generator content","none","none",solidDensity};
s.bedrock={"bedrock",bottom,bottom+seal,true,true,false,"solid floor from strate BoundarySealThickness; breakable by default",
"interior-facing upper face","shared resolved-layout density band",solidDensity};
if(type==ECaveGeneratorType::SurfaceWorld)
{
s.ceiling={"ceiling",top,top,false,true,false,
"no separate volumetric ceiling band; cap profile is internal to content","none",
"Heightfield2D cap profile + AppendStratumBoundaryCeilings fallback mesh"};
s.bedrock={"bedrock",bottom,bottom,false,true,false,"no lower bedrock band","none","none"};
for(const auto& p:assembly.recipe.biomes)
if(p.hasCapProfile)
{ResolvedLayoutAnchorV1 a;a.kind="cap_profile";a.stableId="biome/"+std::to_string(p.stableId);
{ResolvedLayoutAnchorV2 a;a.kind="cap_profile";a.stableId="biome/"+std::to_string(p.stableId);
a.units="1/256 Core voxel";a.values={p.capHeightSubvoxels,p.capUndulationSubvoxels,
p.capWavelengthSubvoxels,p.capDetailAmplitudeSubvoxels,p.capRidgeAmplitudeSubvoxels};s.anchors.push_back(std::move(a));}
const auto plan=std::find_if(assembly.plan.strates.begin(),assembly.plan.strates.end(),
[&](const auto& p){return p.strateIndex==slot.StrateIndex;});
if(plan!=assembly.plan.strates.end())
{
for(std::size_t n=0;n<plan->route.size();++n)
{const auto& p=plan->route[n];ResolvedLayoutAnchorV1 a;a.kind="route_point";a.stableId="route/"+std::to_string(n);
{const auto& p=plan->route[n];ResolvedLayoutAnchorV2 a;a.kind="route_point";a.stableId="route/"+std::to_string(n);
a.units="1/256 Core voxel";a.values={p.positionSubvoxels[0],p.positionSubvoxels[1],p.positionSubvoxels[2],p.radiusSubvoxels};
s.anchors.push_back(std::move(a));}
}
}
else
{
s.ceiling={"ceiling",top,top+legacySealMicrons,false,true,false,
"manager configures a two-sided legacy seal, but Core's compiled analytic field does not apply this density band",
"configured upper side","legacy VF_ApplyBoundarySeal; absent from current Core density path"};
s.bedrock={"bedrock",bottom-legacySealMicrons,bottom,false,true,false,
"manager configures a two-sided legacy seal, but Core's compiled analytic field does not apply this density band; breakable by default",
"configured lower side","legacy VF_ApplyBoundarySeal; absent from current Core density path"};
}
for(std::size_t n=0;n<owner.Manager->GetPassages().Num();++n)
{
const auto& passage=owner.Manager->GetPassages()[static_cast<int32>(n)];
if(passage.UpperStrateIndex!=slot.StrateIndex&&passage.LowerStrateIndex!=slot.StrateIndex)continue;
ResolvedLayoutAnchorV1 a;a.kind="passage";a.stableId="passage/"+std::to_string(n);
a.units="milli owner voxels";a.values={std::llround(passage.UpperPoint.X*1000.0),
std::llround(passage.UpperPoint.Y*1000.0),std::llround(passage.UpperPoint.Z*1000.0),
std::llround(passage.LowerPoint.X*1000.0),std::llround(passage.LowerPoint.Y*1000.0),
std::llround(passage.LowerPoint.Z*1000.0)};s.anchors.push_back(std::move(a));
ResolvedLayoutAnchorV2 a;a.kind="passage";a.stableId="passage/"+std::to_string(n);a.units="milli owner voxels";
a.values={std::llround(passage.UpperPoint.X*1000.0),std::llround(passage.UpperPoint.Y*1000.0),
std::llround(passage.UpperPoint.Z*1000.0),std::llround(passage.LowerPoint.X*1000.0),
std::llround(passage.LowerPoint.Y*1000.0),std::llround(passage.LowerPoint.Z*1000.0)};
s.anchors.push_back(std::move(a));
}
out.strates.push_back(std::move(s));
}