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:
1 parent
8753f6dbe4
commit
55c26a05a7
28 files changed
+1432
-579
No files matched your search
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user