wip(assembler): round L21 -- resolved strate layout snapshot + validator (no world change)
Core ResolvedLayoutSnapshotV1 (canonical JSON, stable identity): per strate stable ID, generator, recipe, interior bounds in metres, ceiling/bedrock bands as Core really applies them, transition mode/width, dispatch provenance. Validator: compat mode reports today's deviations; strict mode enforces ceiling -> content -> bedrock -> next ceiling (owner rule), two separate bands, nominal contiguity, interpenetration only within the declared EVoxelStrateTransition width; 7 negative controls rejected. Findings: SW built at -120..-632 m although the params say -60 m; a 60 m unowned gap filled by analytic FI fallback; no active bedrock bands; transitions ignored by Core. All L19 hashes unchanged (28 + 14 crossing runs). The Unreal adapter exports resolved_layout (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:
1 parent
5d5447dc32
commit
8753f6dbe4
12 files changed
+1347
-3
No files matched your search
@@ -63,6 +63,9 @@ namespace
|
||||
float CameraYawDegrees=0.0f;
|
||||
float CameraPitchDegrees=0.0f;
|
||||
std::filesystem::path MeshExportPath;
|
||||
std::filesystem::path ResolvedLayoutOutputPath;
|
||||
bool ResolvedLayoutOnly=false;
|
||||
bool AuditResolvedLayoutBoundaries=false;
|
||||
};
|
||||
|
||||
bool ParseInteger(std::string_view text,int& output)
|
||||
@@ -117,6 +120,9 @@ namespace
|
||||
" --mesh-export=FILE (write the exact generated tile meshes for offline rendering)\n"
|
||||
" --profile-column-overlap --far-horizon-lod8-step=N --force-generate\n"
|
||||
" --seed=N --recipe=surface-world|floating-islands\n"
|
||||
" --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"
|
||||
" --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"
|
||||
@@ -134,6 +140,8 @@ namespace
|
||||
return false;
|
||||
}
|
||||
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=="--stream-benchmark")options.StreamBenchmark=true;
|
||||
else if(arg=="--stream-pipeline")options.StreamPipeline=true;
|
||||
else if(arg=="--stream-hash")options.StreamHash=true;
|
||||
@@ -155,6 +163,8 @@ namespace
|
||||
else if(recipe=="floating-islands")options.FloatingIslandsRecipe=true;
|
||||
else {std::fprintf(stderr,"Invalid --recipe value: %.*s\n",static_cast<int>(recipe.size()),recipe.data());return false;}
|
||||
}
|
||||
else if(arg.starts_with("--resolved-layout-output="))
|
||||
options.ResolvedLayoutOutputPath=std::string(arg.substr(std::string_view("--resolved-layout-output=").size()));
|
||||
else if(arg.starts_with("--seed="))
|
||||
{if(!readInt("--seed=",options.SeedOverride))return false;options.HasSeedOverride=true;}
|
||||
else if(arg.starts_with("--render-distance-chunks="))
|
||||
@@ -314,6 +324,52 @@ 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)
|
||||
{
|
||||
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();
|
||||
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);
|
||||
if(!output){std::fprintf(stderr,"Could not write resolved-layout snapshot: %s\n",options.ResolvedLayoutOutputPath.c_str());return 2;}
|
||||
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;
|
||||
std::string compatibilityError;
|
||||
const bool compatibilityAccepted=resolvedLayout.validate(
|
||||
VoxelForgeCore::ResolvedLayoutValidationModeV1::compatibility,
|
||||
compatibilityIssues,compatibilityError);
|
||||
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",
|
||||
compatibilityAccepted?"true":"false",compatibilityIssues.size(),compatibilityError.c_str());
|
||||
for(const auto& issue:compatibilityIssues)
|
||||
std::printf("[ResolvedLayoutCompatibilityIssue] code=%s detail=%s\n",
|
||||
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("[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(options.ResolvedLayoutOnly)return 0;
|
||||
}
|
||||
if(options.AutoSurfaceCamera)
|
||||
{
|
||||
constexpr std::int64_t UnitsPerVoxel=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
|
||||
@@ -377,5 +433,6 @@ int main(int argc,char** argv)
|
||||
cameraStepVoxels,options.AuditUniformBlockProofs,options.ProfileLodWork,
|
||||
options.RegionCrossing,options.AuditTileShapeReadiness,
|
||||
options.DiagnosticSlowShapeCompileMs,options.AuditBasePageEquivalence,
|
||||
options.AuditRouteParity);
|
||||
options.AuditRouteParity,options.AuditResolvedLayoutBoundaries,
|
||||
buildResolvedLayout?&resolvedLayout:nullptr);
|
||||
}
|
||||
@@ -253,7 +253,9 @@ namespace StandaloneStreamBenchmark
|
||||
bool bAuditUniformBlockProofs=false,bool bReportLodWorkProfile=false,
|
||||
bool bRegionCrossing=false,
|
||||
bool bAuditTileShapeReadiness=false,int DiagnosticSlowShapeCompileMs=0,
|
||||
bool bAuditBasePageEquivalence=false,bool bAuditRouteParity=false)
|
||||
bool bAuditBasePageEquivalence=false,bool bAuditRouteParity=false,
|
||||
bool bAuditResolvedLayoutBoundaries=false,
|
||||
const VoxelForgeCore::ResolvedLayoutSnapshotV1* ResolvedLayout=nullptr)
|
||||
{
|
||||
using Clock=std::chrono::steady_clock;
|
||||
FStandaloneOwnerWorld& World=*OwnerWorld;
|
||||
@@ -438,6 +440,89 @@ namespace StandaloneStreamBenchmark
|
||||
Streamer.Stop();
|
||||
std::fprintf(stderr,"[StreamPipeline] full shape build failed: %s\n",BuildError.c_str());return 2;
|
||||
}
|
||||
if(bAuditResolvedLayoutBoundaries)
|
||||
{
|
||||
if(ResolvedLayout==nullptr)
|
||||
{Streamer.Stop();std::fprintf(stderr,"[ResolvedLayoutProbe] missing plan-time snapshot\n");return 2;}
|
||||
std::vector<std::int64_t> endpoints;
|
||||
for(const auto& Stratum:ResolvedLayout->strates)
|
||||
{
|
||||
endpoints.push_back(Stratum.interiorBottomMicrometres);
|
||||
endpoints.push_back(Stratum.interiorTopMicrometres);
|
||||
endpoints.push_back(Stratum.ceiling.bottomMicrometres);
|
||||
endpoints.push_back(Stratum.ceiling.topMicrometres);
|
||||
endpoints.push_back(Stratum.bedrock.bottomMicrometres);
|
||||
endpoints.push_back(Stratum.bedrock.topMicrometres);
|
||||
}
|
||||
for(std::size_t StratumIndex=0;StratumIndex+1<ResolvedLayout->strates.size();++StratumIndex)
|
||||
{
|
||||
const auto& Upper=ResolvedLayout->strates[StratumIndex];
|
||||
if(Upper.transitionMode=="Hard"||Upper.transitionWidthMicrometres<=0)continue;
|
||||
endpoints.push_back(Upper.interiorBottomMicrometres-Upper.transitionWidthMicrometres);
|
||||
endpoints.push_back(Upper.interiorBottomMicrometres+Upper.transitionWidthMicrometres);
|
||||
}
|
||||
std::sort(endpoints.begin(),endpoints.end());
|
||||
endpoints.erase(std::unique(endpoints.begin(),endpoints.end()),endpoints.end());
|
||||
std::uint64_t probes=0,disagreements=0;
|
||||
const double CoreVoxelMicrometres=std::max<std::uint32_t>(1,World.CoreVoxelSizeMicrons);
|
||||
const auto expectedAt=[&](std::int64_t zMicrometres)
|
||||
{
|
||||
std::vector<std::string> candidates;
|
||||
for(const auto& Stratum:ResolvedLayout->strates)
|
||||
{
|
||||
const auto contains=[zMicrometres](const VoxelForgeCore::ResolvedLayoutBandV1& Band)
|
||||
{return Band.presentInCurrentField&&zMicrometres>=Band.bottomMicrometres
|
||||
&&zMicrometres<Band.topMicrometres;};
|
||||
if(contains(Stratum.ceiling))candidates.push_back(Stratum.generatorKind+"/ceiling");
|
||||
if(contains(Stratum.content))candidates.push_back(Stratum.generatorKind+"/content");
|
||||
if(contains(Stratum.bedrock))candidates.push_back(Stratum.generatorKind+"/bedrock");
|
||||
}
|
||||
return candidates;
|
||||
};
|
||||
const std::array<std::int64_t,3> offsets{{
|
||||
-static_cast<std::int64_t>(World.CoreVoxelSizeMicrons),0,
|
||||
static_cast<std::int64_t>(World.CoreVoxelSizeMicrons)}};
|
||||
for(const std::int64_t Endpoint:endpoints)
|
||||
for(const std::int64_t Offset:offsets)
|
||||
{
|
||||
const std::int64_t zMicrometres=Endpoint+Offset;
|
||||
const double zVoxel=static_cast<double>(zMicrometres)/CoreVoxelMicrometres;
|
||||
const VoxelForgeCore::Vec3d position{0.0,0.0,zVoxel};
|
||||
const std::string actual=Shape.densityDispatchPathAt(position);
|
||||
const auto expected=expectedAt(zMicrometres);
|
||||
std::string expectedLabel;
|
||||
std::vector<std::string> expectedGenerators;
|
||||
for(const auto& Candidate:expected)
|
||||
{
|
||||
if(!expectedLabel.empty())expectedLabel+=',';
|
||||
expectedLabel+=Candidate;
|
||||
const auto Separator=Candidate.find('/');
|
||||
const std::string Kind=Candidate.substr(0,Separator);
|
||||
if(std::find(expectedGenerators.begin(),expectedGenerators.end(),Kind)==expectedGenerators.end())
|
||||
expectedGenerators.push_back(Kind);
|
||||
}
|
||||
if(expected.empty())expectedLabel="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"
|
||||
: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),
|
||||
static_cast<double>(zMicrometres)/1000000.0,expectedLabel.c_str(),actual.c_str(),density,
|
||||
solid?"solid":"air",mismatch?"YES":"no");
|
||||
}
|
||||
std::printf("[ResolvedLayoutProbeSummary] probes=%llu disagreements=%llu status=%s\n",
|
||||
static_cast<unsigned long long>(probes),static_cast<unsigned long long>(disagreements),
|
||||
disagreements==0?"PASS":"DEVIATIONS_RECORDED");
|
||||
Streamer.Stop();
|
||||
return 0;
|
||||
}
|
||||
const auto ShapeReady=Clock::now();
|
||||
const double ShapeSeconds=std::chrono::duration<double>(ShapeReady-ShapeBuildStart).count();
|
||||
if(bAuditTileShapeReadiness)
|
||||
|
||||
@@ -525,4 +525,161 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
return true;
|
||||
}
|
||||
|
||||
inline const char* GeneratorKind(ECaveGeneratorType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ECaveGeneratorType::TunnelNetwork:return "TunnelNetwork";
|
||||
case ECaveGeneratorType::FlatPlain:return "FlatPlain";
|
||||
case ECaveGeneratorType::CrystalChamber:return "CrystalChamber";
|
||||
case ECaveGeneratorType::Maze:return "Maze";
|
||||
case ECaveGeneratorType::SurfaceWorld:return "SurfaceWorld";
|
||||
case ECaveGeneratorType::VerticalShafts:return "VerticalShafts";
|
||||
case ECaveGeneratorType::FloatingIslands:return "FloatingIslands";
|
||||
case ECaveGeneratorType::Underwater:return "Underwater";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
inline const char* TransitionKind(EVoxelStrateTransition type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case EVoxelStrateTransition::Gradient:return "Gradient";
|
||||
case EVoxelStrateTransition::Hard:return "Hard";
|
||||
case EVoxelStrateTransition::Interleaved:return "Interleaved";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
inline bool BuildResolvedLayoutSnapshot(const FStandaloneOwnerWorld& owner,
|
||||
const VoxelForgeCore::WorldAssemblyManifest& assembly,std::string_view recipeVariant,
|
||||
VoxelForgeCore::ResolvedLayoutSnapshotV1& out,std::string& error)
|
||||
{
|
||||
using namespace VoxelForgeCore;
|
||||
if(!owner.Manager){error="resolved layout builder requires the initialized director";return false;}
|
||||
(void)recipeVariant;
|
||||
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);
|
||||
const auto fixedToMicrons=[&](std::int64_t fixed)
|
||||
{return static_cast<std::int64_t>(std::llround(static_cast<double>(fixed)
|
||||
*static_cast<double>(coreMicrons)/(WorldAssemblySubvoxelsPerVoxel)));};
|
||||
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);
|
||||
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;
|
||||
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())
|
||||
{
|
||||
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));}
|
||||
}
|
||||
}
|
||||
else 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);
|
||||
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);
|
||||
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));
|
||||
}
|
||||
out.strates.push_back(std::move(s));
|
||||
}
|
||||
return out.finalize(error);
|
||||
}
|
||||
|
||||
} // namespace StandaloneWorldAssemblyPrototype
|
||||
Reference in new issue
Block a user