wip: opt-in landing-pad contract + Core.Liminal.Field v2 pads (L145)
Layers may publish certified arrival/departure pads; the passage planner binds endpoints and tangents to them; no-pad layouts serialize unchanged (owner canonical hashes match v227). Pads removed the endpoint grade failures; seeds 0-4 passed at a 45 m margin, but seed 5 still has 289 blocked passage-capsule samples ~29 m before the departure pad, where the composed field stays solid (-0.162) inside the tube over native ground (-3.6). Ripple audit now keeps true route distance across masked gaps (with a synthetic control). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
a45ba9bb10
commit
7d654ff861
14 files changed
+1228
-66
No files matched your search
@@ -1842,6 +1842,9 @@ int main(int argc,char** argv)
|
||||
}
|
||||
VoxelForgeCore::ResolvedLayoutSnapshotV3 resolvedLayout;
|
||||
{
|
||||
const VFJson::Value& coreLiminalConfig=ownerJsonRoot.Get("core_liminal_field");
|
||||
const bool hasCoreLiminalField=!std::holds_alternative<std::nullptr_t>(
|
||||
coreLiminalConfig.Data);
|
||||
const std::string variant=options.LatticeRecipe
|
||||
?"lattice/"+std::to_string(options.LatticeStrateIndex)
|
||||
:options.CavesRecipe?"caves"
|
||||
@@ -1852,16 +1855,14 @@ int main(int argc,char** argv)
|
||||
?StandaloneWorldAssemblyPrototype::ImportExportedResolvedLayout(
|
||||
ownerJsonRoot,resolvedLayout,error)
|
||||
:StandaloneWorldAssemblyPrototype::BuildResolvedLayoutSnapshot(
|
||||
*ownerWorld,worldAssembly,variant,resolvedLayout,error);
|
||||
*ownerWorld,worldAssembly,variant,resolvedLayout,error,
|
||||
!hasCoreLiminalField);
|
||||
if(!layoutResolved)
|
||||
{std::fprintf(stderr,"Resolved-layout snapshot failed: %s\n",error.c_str());return 2;}
|
||||
if(useExportedCorePlan&&!options.NativeOwnerFloatingIslandsRecipe
|
||||
&&!StandaloneWorldAssemblyPrototype::ValidateExportedCorePlanMatchesOwner(
|
||||
*ownerWorld,worldAssembly,resolvedLayout,error))
|
||||
{std::fprintf(stderr,"Unreal-resolved Core plan/owner slot validation failed: %s\n",error.c_str());return 2;}
|
||||
const VFJson::Value& coreLiminalConfig=ownerJsonRoot.Get("core_liminal_field");
|
||||
const bool hasCoreLiminalField=!std::holds_alternative<std::nullptr_t>(
|
||||
coreLiminalConfig.Data);
|
||||
if(hasCoreLiminalField)
|
||||
{
|
||||
const VFJson::Value& versionValue=coreLiminalConfig.Get("version");
|
||||
@@ -1871,7 +1872,7 @@ int main(int argc,char** argv)
|
||||
const auto* policy=std::get_if<std::string>(&coreLiminalConfig.Get("variant_policy").Data);
|
||||
if(!coreLiminalConfig.IsObject()
|
||||
||!versionValue.IsNumber()
|
||||
||versionValue.Number()!=VoxelForgeCore::CoreLiminalFieldRecipeVersionV1
|
||||
||versionValue.Number()!=VoxelForgeCore::CoreLiminalFieldRecipeVersionV2
|
||||
||!strateValue.IsNumber()
|
||||
||std::trunc(strateValue.Number())!=strateValue.Number()
|
||||
||strateValue.Number()<0.0
|
||||
@@ -1896,7 +1897,34 @@ int main(int argc,char** argv)
|
||||
liminalPlan.strateIndex=static_cast<std::int32_t>(strateIndex);
|
||||
liminalPlan.topChunkZ=slot->TopChunkZ;
|
||||
liminalPlan.bottomChunkZ=slot->BottomChunkZ;
|
||||
if(!VoxelForgeCore::PlanCoreLiminalFieldV1(worldAssembly.recipe,
|
||||
resolvedLayout.passages.clear();
|
||||
if(!VoxelForgeCore::PrepareCoreLiminalLandingPadsV2(worldAssembly.recipe,
|
||||
liminalPlan,resolvedLayout,error))
|
||||
{std::fprintf(stderr,"Core.Liminal.Field landing-pad preparation failed: %s\n",error.c_str());return 2;}
|
||||
const auto passageConfigs=
|
||||
StandaloneWorldAssemblyPrototype::BuildResolvedLayoutPassageConfigs(*ownerWorld);
|
||||
auto uncertifiedControl=resolvedLayout;
|
||||
const auto controlStratum=std::find_if(uncertifiedControl.strates.begin(),
|
||||
uncertifiedControl.strates.end(),[&](const auto& value)
|
||||
{return value.strateIndex==strateIndex;});
|
||||
bool landingPadNegativeControlRejected=false;
|
||||
if(controlStratum!=uncertifiedControl.strates.end()
|
||||
&&!controlStratum->landingPads.empty())
|
||||
{
|
||||
controlStratum->landingPads.front().certifiedWalkableWithSupport=false;
|
||||
std::string controlError;
|
||||
landingPadNegativeControlRejected=
|
||||
!VoxelForgeCore::PlanResolvedLayoutPassagesV1(uncertifiedControl,
|
||||
ownerWorld->Seed,passageConfigs,controlError);
|
||||
}
|
||||
std::printf("[LandingPadNegativeControl] uncertified_rejected=%s status=%s\n",
|
||||
landingPadNegativeControlRejected?"true":"false",
|
||||
landingPadNegativeControlRejected?"PASS":"FAIL");
|
||||
if(!landingPadNegativeControlRejected)return 2;
|
||||
if(!VoxelForgeCore::PlanResolvedLayoutPassagesV1(resolvedLayout,
|
||||
ownerWorld->Seed,passageConfigs,error))
|
||||
{std::fprintf(stderr,"Core.Liminal.Field pad-bound passage planning failed: %s\n",error.c_str());return 2;}
|
||||
if(!VoxelForgeCore::PlanCoreLiminalFieldV2(worldAssembly.recipe,
|
||||
liminalPlan,resolvedLayout,error))
|
||||
{std::fprintf(stderr,"Core.Liminal.Field planning failed: %s\n",error.c_str());return 2;}
|
||||
worldAssembly.plan.strates.push_back(std::move(liminalPlan));
|
||||
@@ -1910,17 +1938,19 @@ int main(int argc,char** argv)
|
||||
{std::fprintf(stderr,"Core.Liminal.Field manifest failed canonical JSON round-trip: %s\n",error.c_str());return 2;}
|
||||
const auto plan=std::find_if(worldAssembly.plan.strates.begin(),
|
||||
worldAssembly.plan.strates.end(),[&](const auto& value)
|
||||
{return value.strateIndex==strateIndex&&value.liminalField.version!=0;});
|
||||
{return value.strateIndex==strateIndex
|
||||
&&value.liminalField.version==VoxelForgeCore::CoreLiminalFieldRecipeVersionV2;});
|
||||
if(plan==worldAssembly.plan.strates.end())
|
||||
{std::fprintf(stderr,"Core.Liminal.Field finalized plan is missing\n");return 2;}
|
||||
auto& resolvedLiminal=*std::find_if(resolvedLayout.strates.begin(),
|
||||
resolvedLayout.strates.end(),[&](const auto& value)
|
||||
{return value.strateIndex==strateIndex;});
|
||||
resolvedLiminal.recipeId=std::string(VoxelForgeCore::CoreLiminalFieldRecipeIdV1);
|
||||
resolvedLiminal.recipeVersion=VoxelForgeCore::CoreLiminalFieldRecipeVersionV1;
|
||||
resolvedLiminal.recipeVersion=VoxelForgeCore::CoreLiminalFieldRecipeVersionV2;
|
||||
resolvedLiminal.anchors.erase(std::remove_if(resolvedLiminal.anchors.begin(),
|
||||
resolvedLiminal.anchors.end(),[](const auto& anchor)
|
||||
{return anchor.kind=="heightfield_profile";}),resolvedLiminal.anchors.end());
|
||||
{return anchor.kind=="heightfield_profile"||anchor.kind=="route_point";}),
|
||||
resolvedLiminal.anchors.end());
|
||||
VoxelForgeCore::ResolvedLayoutAnchorV2 profileAnchor;
|
||||
profileAnchor.kind="heightfield_profile";
|
||||
profileAnchor.stableId="heightfield-profile/liminal/"
|
||||
|
||||
@@ -3074,7 +3074,8 @@ namespace StandaloneStreamBenchmark
|
||||
const auto plan=Assembly?std::find_if(Assembly->plan.strates.begin(),
|
||||
Assembly->plan.strates.end(),[&](const auto& value)
|
||||
{return value.strateIndex==coreLiminalStratum->strateIndex
|
||||
&&value.liminalField.version==VoxelForgeCore::CoreLiminalFieldRecipeVersionV1;})
|
||||
&&(value.liminalField.version==VoxelForgeCore::CoreLiminalFieldRecipeVersionV1
|
||||
||value.liminalField.version==VoxelForgeCore::CoreLiminalFieldRecipeVersionV2);})
|
||||
:std::vector<VoxelForgeCore::WorldAssemblyStratumPlanV1>::const_iterator{};
|
||||
if(!Assembly||plan==Assembly->plan.strates.end())fixedMouthBinding=false;
|
||||
else
|
||||
@@ -4124,8 +4125,9 @@ namespace StandaloneStreamBenchmark
|
||||
}
|
||||
return best;
|
||||
};
|
||||
const auto appendDetrendedProfile=[&](const std::vector<double>& values,
|
||||
double startDistance,std::vector<std::array<double,2>>& output)
|
||||
const auto appendDetrendedProfile=[&](
|
||||
const std::vector<std::array<double,2>>& values,
|
||||
std::vector<std::array<double,2>>& output)
|
||||
{
|
||||
if(values.size()<64)return;
|
||||
// Remove a smooth mouth taper across each 32 m window while
|
||||
@@ -4134,11 +4136,12 @@ namespace StandaloneStreamBenchmark
|
||||
// 0.5–8 m periods that this audit is meant to detect.
|
||||
constexpr std::size_t Degree=3;
|
||||
std::array<std::array<double,Degree+2>,Degree+1> normal{};
|
||||
const double meanIndex=0.5*static_cast<double>(values.size()-1);
|
||||
const double halfSpan=std::max(1.0,meanIndex);
|
||||
for(std::size_t i=0;i<values.size();++i)
|
||||
const double meanDistance=0.5*(values.front()[0]+values.back()[0]);
|
||||
const double halfSpan=std::max(1.0,
|
||||
0.5*(values.back()[0]-values.front()[0]));
|
||||
for(const auto& value:values)
|
||||
{
|
||||
const double x=(static_cast<double>(i)-meanIndex)/halfSpan;
|
||||
const double x=(value[0]-meanDistance)/halfSpan;
|
||||
std::array<double,2*Degree+1> powers{};
|
||||
powers[0]=1.0;
|
||||
for(std::size_t power=1;power<powers.size();++power)
|
||||
@@ -4147,7 +4150,7 @@ namespace StandaloneStreamBenchmark
|
||||
{
|
||||
for(std::size_t column=0;column<=Degree;++column)
|
||||
normal[row][column]+=powers[row+column];
|
||||
normal[row][Degree+1]+=values[i]*powers[row];
|
||||
normal[row][Degree+1]+=value[1]*powers[row];
|
||||
}
|
||||
}
|
||||
for(std::size_t column=0;column<=Degree;++column)
|
||||
@@ -4169,14 +4172,13 @@ namespace StandaloneStreamBenchmark
|
||||
normal[row][value]-=scale*normal[column][value];
|
||||
}
|
||||
}
|
||||
for(std::size_t i=0;i<values.size();++i)
|
||||
for(const auto& value:values)
|
||||
{
|
||||
const double x=(static_cast<double>(i)-meanIndex)/halfSpan;
|
||||
const double x=(value[0]-meanDistance)/halfSpan;
|
||||
double trend=normal[Degree][Degree+1];
|
||||
for(std::size_t coefficient=Degree;coefficient-->0;)
|
||||
trend=trend*x+normal[coefficient][Degree+1];
|
||||
output.push_back({startDistance+static_cast<double>(i)*SampleSpacingMetres,
|
||||
values[i]-trend});
|
||||
output.push_back({value[0],value[1]-trend});
|
||||
}
|
||||
};
|
||||
const auto appendDetrendedPairs=[](
|
||||
@@ -4199,7 +4201,7 @@ namespace StandaloneStreamBenchmark
|
||||
output.push_back({value[0],value[1]-meanY
|
||||
-slope*(value[0]-meanX)});
|
||||
};
|
||||
std::vector<double> beadControl;
|
||||
std::vector<std::array<double,2>> beadControl;
|
||||
beadControl.reserve(static_cast<std::size_t>(WindowLengthMetres/SampleSpacingMetres)+1);
|
||||
constexpr double BeadSpacingMetres=1.5;
|
||||
constexpr double BeadSphereRadiusMetres=2.0;
|
||||
@@ -4207,14 +4209,49 @@ namespace StandaloneStreamBenchmark
|
||||
{
|
||||
const double x=static_cast<double>(i)*SampleSpacingMetres;
|
||||
const double nearest=std::abs(std::remainder(x,BeadSpacingMetres));
|
||||
beadControl.push_back(std::sqrt(std::max(0.0,
|
||||
BeadSphereRadiusMetres*BeadSphereRadiusMetres-nearest*nearest)));
|
||||
beadControl.push_back({x,std::sqrt(std::max(0.0,
|
||||
BeadSphereRadiusMetres*BeadSphereRadiusMetres-nearest*nearest))});
|
||||
}
|
||||
std::vector<std::array<double,2>> beadSamples;
|
||||
appendDetrendedProfile(beadControl,0.0,beadSamples);
|
||||
appendDetrendedProfile(beadControl,beadSamples);
|
||||
double ignoredPeriod=0.0;
|
||||
audit.controlAmplitudeMetres=sinusoidAmplitude(beadSamples,ignoredPeriod);
|
||||
audit.controlDetected=audit.controlAmplitudeMetres>=RippleAmplitudeLimitMetres;
|
||||
std::vector<std::array<double,2>> maskedSineControl,maskedSmoothControl;
|
||||
constexpr double SamplingControlGapStartMetres=10.0;
|
||||
constexpr double SamplingControlGapEndMetres=15.0;
|
||||
for(std::size_t sample=0;
|
||||
sample<=static_cast<std::size_t>(WindowLengthMetres/SampleSpacingMetres);
|
||||
++sample)
|
||||
{
|
||||
const double distance=static_cast<double>(sample)*SampleSpacingMetres;
|
||||
if(distance>=SamplingControlGapStartMetres
|
||||
&&distance<SamplingControlGapEndMetres)continue;
|
||||
const double phase=6.2831853071795864769*distance/4.0;
|
||||
maskedSineControl.push_back({distance,0.060*std::sin(phase)});
|
||||
const double x=(distance-16.0)/16.0;
|
||||
maskedSmoothControl.push_back({distance,0.4*x*x*x-0.15*x*x+0.08*x});
|
||||
}
|
||||
std::vector<std::array<double,2>> maskedSineResiduals,maskedSmoothResiduals;
|
||||
appendDetrendedProfile(maskedSineControl,maskedSineResiduals);
|
||||
appendDetrendedProfile(maskedSmoothControl,maskedSmoothResiduals);
|
||||
double maskedSinePeriod=0.0,maskedSmoothPeriod=0.0;
|
||||
const double maskedSineAmplitude=sinusoidAmplitude(
|
||||
maskedSineResiduals,maskedSinePeriod);
|
||||
const double maskedSmoothAmplitude=sinusoidAmplitude(
|
||||
maskedSmoothResiduals,maskedSmoothPeriod);
|
||||
const bool maskedSamplingControlPassed=
|
||||
maskedSineAmplitude>=RippleAmplitudeLimitMetres
|
||||
&&maskedSmoothAmplitude<RippleAmplitudeLimitMetres;
|
||||
std::printf("[PassageRippleSamplingControl] missing_samples=%zu "
|
||||
"sine_amplitude_m=%.4f sine_detected=%s smooth_amplitude_m=%.4f "
|
||||
"smooth_rejected=%s status=%s\n",
|
||||
static_cast<std::size_t>((SamplingControlGapEndMetres
|
||||
-SamplingControlGapStartMetres)/SampleSpacingMetres),
|
||||
maskedSineAmplitude,maskedSineAmplitude>=RippleAmplitudeLimitMetres
|
||||
?"true":"false",maskedSmoothAmplitude,
|
||||
maskedSmoothAmplitude<RippleAmplitudeLimitMetres?"true":"false",
|
||||
maskedSamplingControlPassed?"PASS":"FAIL");
|
||||
audit.floorControlAmplitudeMetres=audit.controlAmplitudeMetres;
|
||||
audit.floorControlDetected=audit.floorControlAmplitudeMetres
|
||||
>=FloorRippleAmplitudeLimitMetres;
|
||||
@@ -4268,7 +4305,7 @@ namespace StandaloneStreamBenchmark
|
||||
for(const double fraction:windowStarts)
|
||||
{
|
||||
const double start=(total-WindowLengthMetres)*fraction;
|
||||
std::vector<double> radii;
|
||||
std::vector<std::array<double,2>> radii;
|
||||
radii.reserve(static_cast<std::size_t>(WindowLengthMetres/SampleSpacingMetres)+1);
|
||||
std::size_t wallProbeNonfinite=0,wallProbeOutOfBand=0;
|
||||
double wallProbeMinimum=std::numeric_limits<double>::infinity();
|
||||
@@ -4368,7 +4405,7 @@ namespace StandaloneStreamBenchmark
|
||||
// The probes sit 0.18 m inside the authored radius. Add that
|
||||
// radius back to the signed wall distance so radius-carried ribs
|
||||
// remain visible; the cubic detrend removes the smooth mouth flare.
|
||||
radii.push_back(sampleRadius+0.5*(left+right));
|
||||
radii.push_back({distance,sampleRadius+0.5*(left+right)});
|
||||
++audit.samples;
|
||||
}
|
||||
if(radii.size()<static_cast<std::size_t>(WindowLengthMetres/
|
||||
@@ -4389,7 +4426,7 @@ namespace StandaloneStreamBenchmark
|
||||
audit.wallWindowExpectedSamples+=static_cast<std::size_t>(
|
||||
WindowLengthMetres/SampleSpacingMetres)+1;
|
||||
std::vector<std::array<double,2>> windowSamples;
|
||||
appendDetrendedProfile(radii,start,windowSamples);
|
||||
appendDetrendedProfile(radii,windowSamples);
|
||||
double localPeriod=0.0;
|
||||
const double localAmplitude=sinusoidAmplitude(windowSamples,localPeriod);
|
||||
std::printf("[PassageWallRippleWindow] id=%s start_m=%.3f "
|
||||
@@ -4459,6 +4496,7 @@ namespace StandaloneStreamBenchmark
|
||||
audit.passed=audit.controlDetected&&audit.wallQualifiedWindows>0
|
||||
&&wallWindowCoverage>=0.75
|
||||
&&audit.localPeakAmplitudeMetres<RippleAmplitudeLimitMetres
|
||||
&&maskedSamplingControlPassed
|
||||
&&audit.floorControlDetected&&audit.floorStepControlDetected
|
||||
&&(audit.floorOverlapOnly||(floorCoverage>=0.75
|
||||
&&audit.floorAmplitudeMetres<FloorRippleAmplitudeLimitMetres));
|
||||
|
||||
@@ -834,7 +834,8 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
if(owner.CoreVoxelSizeMicrons==0)
|
||||
{error="Core.Liminal.Field stamp planning needs the resolved Core voxel size";return false;}
|
||||
const auto plan=std::find_if(assembly.plan.strates.begin(),assembly.plan.strates.end(),
|
||||
[](const auto& value){return value.liminalField.version==CoreLiminalFieldRecipeVersionV1;});
|
||||
[](const auto& value){return value.liminalField.version==CoreLiminalFieldRecipeVersionV1
|
||||
||value.liminalField.version==CoreLiminalFieldRecipeVersionV2;});
|
||||
if(plan==assembly.plan.strates.end()||plan->route.size()<2)
|
||||
{error="Core.Liminal.Field has no planned route for stamp resolution";return false;}
|
||||
const auto strate=std::find_if(layout.strates.begin(),layout.strates.end(),
|
||||
@@ -860,7 +861,7 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
const std::int64_t profileReliefUm=plan->liminalField.variant
|
||||
==WorldAssemblyLiminalVariantV1::repeatingHills
|
||||
?2*definition.hillAmplitudeMicrometres:0;
|
||||
const std::int64_t supportBaseUm=floorUm-profileReliefUm+100000;
|
||||
const std::int64_t supportBaseUm=strate->content.bottomMicrometres+100000;
|
||||
|
||||
std::vector<std::array<std::int64_t,3>> controls;
|
||||
controls.reserve(plan->route.size());
|
||||
@@ -1712,9 +1713,46 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
return true;
|
||||
}
|
||||
|
||||
inline std::vector<VoxelForgeCore::ResolvedLayoutPassageConfigV1>
|
||||
BuildResolvedLayoutPassageConfigs(const FStandaloneOwnerWorld& owner)
|
||||
{
|
||||
std::vector<VoxelForgeCore::ResolvedLayoutPassageConfigV1> passageConfigs;
|
||||
if(!owner.Manager)return passageConfigs;
|
||||
for(const FStrateSlot& slot:owner.Manager->GetLayout())
|
||||
{
|
||||
if(!slot.Definition)continue;
|
||||
const auto& authored=slot.Definition->PassageConfig;
|
||||
const auto ownerVoxelsToCoreQ=[&](double value)
|
||||
{return static_cast<std::int64_t>(std::llround(value*owner.OwnerVoxelSizeMicrons
|
||||
*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel/owner.CoreVoxelSizeMicrons));};
|
||||
VoxelForgeCore::ResolvedLayoutPassageConfigV1 config;
|
||||
config.upperStrateIndex=slot.StrateIndex;
|
||||
config.connections=authored.Connections;
|
||||
config.style=static_cast<std::uint32_t>(authored.Style);
|
||||
config.mouthRadiusSubvoxels=std::max(ownerVoxelsToCoreQ(authored.MouthRadius),
|
||||
ownerVoxelsToCoreQ(36.0));
|
||||
config.midRadiusSubvoxels=std::max(ownerVoxelsToCoreQ(authored.MidRadius),
|
||||
ownerVoxelsToCoreQ(24.0));
|
||||
config.reachMinimumSubvoxels=ownerVoxelsToCoreQ(authored.ReachMin);
|
||||
config.reachMaximumSubvoxels=ownerVoxelsToCoreQ(authored.ReachMax);
|
||||
config.distanceMinimumSubvoxels=ownerVoxelsToCoreQ(authored.DistanceMin);
|
||||
config.distanceMaximumSubvoxels=ownerVoxelsToCoreQ(authored.DistanceMax);
|
||||
config.wanderSubvoxels=ownerVoxelsToCoreQ(authored.Wander);
|
||||
config.segments=static_cast<std::uint32_t>(std::clamp(authored.Segments,1,48));
|
||||
config.verticalWobbleSubvoxels=ownerVoxelsToCoreQ(authored.VerticalWobble);
|
||||
config.spiralRadiusSubvoxels=ownerVoxelsToCoreQ(authored.SpiralRadius);
|
||||
config.spiralTurnsQ16=static_cast<std::int64_t>(std::llround(authored.SpiralTurns*65536.0));
|
||||
config.cascadeSteps=static_cast<std::uint32_t>(std::clamp(authored.CascadeSteps,1,16));
|
||||
config.cascadeLedgeSubvoxels=ownerVoxelsToCoreQ(authored.CascadeLedge);
|
||||
passageConfigs.push_back(config);
|
||||
}
|
||||
return passageConfigs;
|
||||
}
|
||||
|
||||
inline bool BuildResolvedLayoutSnapshot(const FStandaloneOwnerWorld& owner,
|
||||
const VoxelForgeCore::WorldAssemblyManifest& assembly,std::string_view recipeVariant,
|
||||
VoxelForgeCore::ResolvedLayoutSnapshotV3& out,std::string& error)
|
||||
VoxelForgeCore::ResolvedLayoutSnapshotV3& out,std::string& error,
|
||||
bool planPassages=true)
|
||||
{
|
||||
using namespace VoxelForgeCore;
|
||||
if(!owner.Manager){error="resolved layout builder requires the initialized director";return false;}
|
||||
@@ -1938,35 +1976,11 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
}
|
||||
out.strates.push_back(std::move(s));
|
||||
}
|
||||
std::vector<ResolvedLayoutPassageConfigV1> passageConfigs;
|
||||
for(const FStrateSlot& slot:owner.Manager->GetLayout())
|
||||
if(planPassages)
|
||||
{
|
||||
if(!slot.Definition)continue;
|
||||
const auto& authored=slot.Definition->PassageConfig;
|
||||
const auto ownerVoxelsToCoreQ=[&](double value)
|
||||
{return static_cast<std::int64_t>(std::llround(value*owner.OwnerVoxelSizeMicrons
|
||||
*WorldAssemblySubvoxelsPerVoxel/owner.CoreVoxelSizeMicrons));};
|
||||
ResolvedLayoutPassageConfigV1 config;config.upperStrateIndex=slot.StrateIndex;
|
||||
config.connections=authored.Connections;
|
||||
config.style=static_cast<std::uint32_t>(authored.Style);
|
||||
config.mouthRadiusSubvoxels=std::max(ownerVoxelsToCoreQ(authored.MouthRadius),
|
||||
ownerVoxelsToCoreQ(36.0));
|
||||
config.midRadiusSubvoxels=std::max(ownerVoxelsToCoreQ(authored.MidRadius),
|
||||
ownerVoxelsToCoreQ(24.0));
|
||||
config.reachMinimumSubvoxels=ownerVoxelsToCoreQ(authored.ReachMin);
|
||||
config.reachMaximumSubvoxels=ownerVoxelsToCoreQ(authored.ReachMax);
|
||||
config.distanceMinimumSubvoxels=ownerVoxelsToCoreQ(authored.DistanceMin);
|
||||
config.distanceMaximumSubvoxels=ownerVoxelsToCoreQ(authored.DistanceMax);
|
||||
config.wanderSubvoxels=ownerVoxelsToCoreQ(authored.Wander);
|
||||
config.segments=static_cast<std::uint32_t>(std::clamp(authored.Segments,1,48));
|
||||
config.verticalWobbleSubvoxels=ownerVoxelsToCoreQ(authored.VerticalWobble);
|
||||
config.spiralRadiusSubvoxels=ownerVoxelsToCoreQ(authored.SpiralRadius);
|
||||
config.spiralTurnsQ16=static_cast<std::int64_t>(std::llround(authored.SpiralTurns*65536.0));
|
||||
config.cascadeSteps=static_cast<std::uint32_t>(std::clamp(authored.CascadeSteps,1,16));
|
||||
config.cascadeLedgeSubvoxels=ownerVoxelsToCoreQ(authored.CascadeLedge);
|
||||
passageConfigs.push_back(config);
|
||||
const auto passageConfigs=BuildResolvedLayoutPassageConfigs(owner);
|
||||
if(!PlanResolvedLayoutPassagesV1(out,owner.Seed,passageConfigs,error))return false;
|
||||
}
|
||||
if(!PlanResolvedLayoutPassagesV1(out,owner.Seed,passageConfigs,error))return false;
|
||||
return out.finalize(error);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user