wip: Core.Liminal.Field v1 recipe, 4 seed-selected variants between fixed mouths (L144, fails mouth joins)
Plans and draws flat field+monolith, repeating hills, road over cloud floor and pillar grid in a 3-strata fixture, binding passage/0-1 and 1-2 as fixed endpoints. Composed W1 probes: 3/4 seeds fail one passage interface, 4/4 fail the liminal walk audit near the mouths (support itself passes). Stopped per AGENTS.md. Same mouth-ownership failure class as FI (L133/L140) and VS seed 2 (L139). 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
a4e214561f
commit
a45ba9bb10
18 files changed
+1547
-43
No files matched your search
@@ -1835,25 +1835,6 @@ 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;}
|
||||
if(!options.WorldAssemblyManifestOutputPath.empty())
|
||||
{
|
||||
const std::string manifestText=worldAssembly.ToJson();
|
||||
const auto& outputPath=options.WorldAssemblyManifestOutputPath;
|
||||
std::error_code directoryError;
|
||||
if(!outputPath.parent_path().empty())
|
||||
std::filesystem::create_directories(outputPath.parent_path(),directoryError);
|
||||
if(directoryError)
|
||||
{std::fprintf(stderr,"Could not create the complete world assembly manifest output directory.\n");return 2;}
|
||||
std::ofstream output(outputPath,std::ios::binary);
|
||||
if(!output)
|
||||
{std::fprintf(stderr,"Could not open the complete world assembly manifest output path.\n");return 2;}
|
||||
output.write(manifestText.data(),static_cast<std::streamsize>(manifestText.size()));
|
||||
if(!output)
|
||||
{std::fprintf(stderr,"Could not write the complete world assembly manifest payload.\n");return 2;}
|
||||
std::printf("[WorldAssemblerFullManifest] bytes=%zu surface_strates=%zu floating_islands=%zu path=%s status=PASS\n",
|
||||
manifestText.size(),worldAssembly.plan.strates.size(),worldAssembly.floatingIslands.size(),
|
||||
outputPath.string().c_str());
|
||||
}
|
||||
if (options.AuditHeightfieldProfile && !RunHeightfieldProfileAudit(worldAssembly))
|
||||
{
|
||||
std::fprintf(stderr,"Profiled SurfaceWorld interval, classification or route audit failed.\n");
|
||||
@@ -1878,6 +1859,146 @@ int main(int argc,char** argv)
|
||||
&&!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");
|
||||
const VFJson::Value& strateValue=coreLiminalConfig.Get("strate_index");
|
||||
const std::int64_t strateIndex=strateValue.IsNumber()
|
||||
?strateValue.Integer():-1;
|
||||
const auto* policy=std::get_if<std::string>(&coreLiminalConfig.Get("variant_policy").Data);
|
||||
if(!coreLiminalConfig.IsObject()
|
||||
||!versionValue.IsNumber()
|
||||
||versionValue.Number()!=VoxelForgeCore::CoreLiminalFieldRecipeVersionV1
|
||||
||!strateValue.IsNumber()
|
||||
||std::trunc(strateValue.Number())!=strateValue.Number()
|
||||
||strateValue.Number()<0.0
|
||||
||strateValue.Number()>std::numeric_limits<std::int32_t>::max()
|
||||
||strateIndex<0||!policy||*policy!="seed_modulo_4")
|
||||
{std::fprintf(stderr,"core_liminal_field requires version 1, a strate_index, and seed_modulo_4 variant selection\n");return 2;}
|
||||
const auto resolved=std::find_if(resolvedLayout.strates.begin(),resolvedLayout.strates.end(),
|
||||
[&](const auto& value){return value.strateIndex==strateIndex
|
||||
&&value.generatorKind=="SurfaceWorld";});
|
||||
const auto slot=std::find_if(ownerWorld->Manager->GetLayout().begin(),
|
||||
ownerWorld->Manager->GetLayout().end(),[&](const auto& value)
|
||||
{return value.StrateIndex==strateIndex&&value.Definition;});
|
||||
if(resolved==resolvedLayout.strates.end()||slot==ownerWorld->Manager->GetLayout().end())
|
||||
{std::fprintf(stderr,"Core.Liminal.Field must target a resolved SurfaceWorld owner slot\n");return 2;}
|
||||
const auto order=static_cast<std::size_t>(resolved-resolvedLayout.strates.begin());
|
||||
if(order==0||order+1>=resolvedLayout.strates.size()
|
||||
||resolvedLayout.strates.size()!=3
|
||||
||resolvedLayout.strates[order-1].generatorKind!="SurfaceWorld"
|
||||
||resolvedLayout.strates[order+1].generatorKind!="TunnelNetwork")
|
||||
{std::fprintf(stderr,"Core.Liminal.Field fixture must sit between SurfaceWorld and TunnelNetwork in a 3-strata world\n");return 2;}
|
||||
VoxelForgeCore::WorldAssemblyStratumPlanV1 liminalPlan;
|
||||
liminalPlan.strateIndex=static_cast<std::int32_t>(strateIndex);
|
||||
liminalPlan.topChunkZ=slot->TopChunkZ;
|
||||
liminalPlan.bottomChunkZ=slot->BottomChunkZ;
|
||||
if(!VoxelForgeCore::PlanCoreLiminalFieldV1(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));
|
||||
if(!worldAssembly.finalize(error))
|
||||
{std::fprintf(stderr,"Core.Liminal.Field manifest finalization failed: %s\n",error.c_str());return 2;}
|
||||
const std::string canonicalManifest=worldAssembly.ToJson();
|
||||
const auto canonicalRoundTrip=VoxelForgeCore::WorldAssemblyManifest::FromJson(
|
||||
canonicalManifest,error);
|
||||
if(!canonicalRoundTrip||canonicalRoundTrip->ToJson()!=canonicalManifest
|
||||
||canonicalRoundTrip->contentHash!=worldAssembly.contentHash)
|
||||
{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;});
|
||||
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.anchors.erase(std::remove_if(resolvedLiminal.anchors.begin(),
|
||||
resolvedLiminal.anchors.end(),[](const auto& anchor)
|
||||
{return anchor.kind=="heightfield_profile";}),resolvedLiminal.anchors.end());
|
||||
VoxelForgeCore::ResolvedLayoutAnchorV2 profileAnchor;
|
||||
profileAnchor.kind="heightfield_profile";
|
||||
profileAnchor.stableId="heightfield-profile/liminal/"
|
||||
+std::to_string(static_cast<std::uint32_t>(plan->surfaceProfile.kind));
|
||||
profileAnchor.units="profile kind enum";
|
||||
profileAnchor.values={1,static_cast<std::int64_t>(plan->surfaceProfile.kind)};
|
||||
resolvedLiminal.anchors.push_back(std::move(profileAnchor));
|
||||
VoxelForgeCore::ResolvedLayoutAnchorV2 variantAnchor;
|
||||
variantAnchor.kind="liminal_variant";variantAnchor.stableId="liminal-field/variant";
|
||||
variantAnchor.units="version,variant,seed,1/256 Core voxel";
|
||||
variantAnchor.values={plan->liminalField.version,
|
||||
static_cast<std::int64_t>(plan->liminalField.variant),plan->liminalField.seed,
|
||||
plan->liminalField.floorHeightSubvoxels,
|
||||
plan->liminalField.routeHalfWidthSubvoxels,
|
||||
plan->liminalField.routeHalfThicknessSubvoxels};
|
||||
resolvedLiminal.anchors.push_back(std::move(variantAnchor));
|
||||
for(std::size_t pointIndex=0;pointIndex<plan->route.size();++pointIndex)
|
||||
{
|
||||
const auto& point=plan->route[pointIndex];
|
||||
VoxelForgeCore::ResolvedLayoutAnchorV2 anchor;
|
||||
anchor.kind="liminal_route_point";
|
||||
anchor.stableId="liminal-field/route/"+std::to_string(pointIndex);
|
||||
anchor.units="1/256 Core voxel";
|
||||
anchor.values={point.positionSubvoxels[0],point.positionSubvoxels[1],
|
||||
point.positionSubvoxels[2],point.radiusSubvoxels};
|
||||
resolvedLiminal.anchors.push_back(std::move(anchor));
|
||||
}
|
||||
if(!resolvedLayout.finalize(error))
|
||||
{std::fprintf(stderr,"Core.Liminal.Field resolved layout finalization failed: %s\n",error.c_str());return 2;}
|
||||
std::vector<VoxelForgeCore::ResolvedLayoutIssueV2> strictIssues;
|
||||
std::string strictError;
|
||||
if(!resolvedLayout.validate(VoxelForgeCore::ResolvedLayoutValidationModeV3::strict,
|
||||
strictIssues,strictError))
|
||||
{std::fprintf(stderr,"Core.Liminal.Field resolved layout failed strict validation: %s\n",strictError.c_str());return 2;}
|
||||
VFJson::Value assemblyValue;
|
||||
if(!VFJson::Parser(worldAssembly.ToJson()).Parse(assemblyValue,error))
|
||||
{std::fprintf(stderr,"Core.Liminal.Field manifest JSON encoding failed: %s\n",error.c_str());return 2;}
|
||||
auto* rootObject=std::get_if<VFJson::Value::Object>(&ownerJsonRoot.Data);
|
||||
if(!rootObject)
|
||||
{std::fprintf(stderr,"Core.Liminal.Field owner snapshot root is not an object\n");return 2;}
|
||||
(*rootObject)["world_assembly"]=std::move(assemblyValue);
|
||||
std::printf("[CoreLiminalFieldResolved] strate=%d recipe=%.*s version=%u variant=%u seed=%u arrival=%s departure=%s route_points=%zu status=PASS\n",
|
||||
static_cast<int>(strateIndex),static_cast<int>(VoxelForgeCore::CoreLiminalFieldRecipeIdV1.size()),
|
||||
VoxelForgeCore::CoreLiminalFieldRecipeIdV1.data(),plan->liminalField.version,
|
||||
static_cast<unsigned>(plan->liminalField.variant),plan->liminalField.seed,
|
||||
resolvedLayout.passages.front().stableId.c_str(),resolvedLayout.passages.back().stableId.c_str(),
|
||||
plan->route.size());
|
||||
std::vector<VoxelForgeCore::ResolvedLayoutStampRequestV1> liminalRequests;
|
||||
if(!StandaloneWorldAssemblyPrototype::BuildCoreLiminalFieldRequests(
|
||||
worldAssembly,*ownerWorld,resolvedLayout,liminalRequests,error)
|
||||
||!VoxelForgeCore::ResolveResolvedLayoutStampsV1(
|
||||
resolvedLayout,seed,liminalRequests,error))
|
||||
{std::fprintf(stderr,"Core.Liminal.Field drawing plan failed: %s\n",error.c_str());return 2;}
|
||||
std::printf("[CoreLiminalFieldDraw] variant=%u stamps=%zu deck=1 supports=%zu status=PASS\n",
|
||||
static_cast<unsigned>(plan->liminalField.variant),resolvedLayout.stamps.size(),
|
||||
static_cast<std::size_t>(std::count_if(resolvedLayout.stamps.begin(),
|
||||
resolvedLayout.stamps.end(),[](const auto& stamp)
|
||||
{return stamp.stableId.starts_with("core/liminal-field/route/support/");})));
|
||||
}
|
||||
if(!options.WorldAssemblyManifestOutputPath.empty())
|
||||
{
|
||||
const std::string manifestText=worldAssembly.ToJson();
|
||||
const auto& outputPath=options.WorldAssemblyManifestOutputPath;
|
||||
std::error_code directoryError;
|
||||
if(!outputPath.parent_path().empty())
|
||||
std::filesystem::create_directories(outputPath.parent_path(),directoryError);
|
||||
if(directoryError)
|
||||
{std::fprintf(stderr,"Could not create the complete world assembly manifest output directory.\n");return 2;}
|
||||
std::ofstream output(outputPath,std::ios::binary);
|
||||
if(!output)
|
||||
{std::fprintf(stderr,"Could not open the complete world assembly manifest output path.\n");return 2;}
|
||||
output.write(manifestText.data(),static_cast<std::streamsize>(manifestText.size()));
|
||||
if(!output)
|
||||
{std::fprintf(stderr,"Could not write the complete world assembly manifest payload.\n");return 2;}
|
||||
std::printf("[WorldAssemblerFullManifest] bytes=%zu surface_strates=%zu floating_islands=%zu path=%s status=PASS\n",
|
||||
manifestText.size(),worldAssembly.plan.strates.size(),worldAssembly.floatingIslands.size(),
|
||||
outputPath.string().c_str());
|
||||
}
|
||||
const bool hasMotifFamily=!std::holds_alternative<std::nullptr_t>(
|
||||
ownerJsonRoot.Get("motif_family_override").Data);
|
||||
const bool hasLiminalRoad=!std::holds_alternative<std::nullptr_t>(
|
||||
@@ -1887,6 +2008,8 @@ int main(int argc,char** argv)
|
||||
if((hasMotifFamily&&hasLiminalRoad)||(hasMotifFamily&&hasLiminalPillarGrid)
|
||||
||(hasLiminalRoad&&hasLiminalPillarGrid))
|
||||
{std::fprintf(stderr,"liminal content overrides must be used in separate fixtures\n");return 2;}
|
||||
if(hasCoreLiminalField&&(hasMotifFamily||hasLiminalRoad||hasLiminalPillarGrid))
|
||||
{std::fprintf(stderr,"Core.Liminal.Field owns its variant geometry and cannot be combined with prototype content overrides\n");return 2;}
|
||||
if(hasMotifFamily)
|
||||
{
|
||||
std::vector<VoxelForgeCore::ResolvedLayoutStampRequestV1> requests;
|
||||
|
||||
@@ -2966,11 +2966,20 @@ namespace StandaloneStreamBenchmark
|
||||
{
|
||||
bool found=false;
|
||||
bool passed=true;
|
||||
const auto coreLiminalStratum=std::find_if(ResolvedLayout->strates.begin(),
|
||||
ResolvedLayout->strates.end(),[](const auto& value)
|
||||
{return value.recipeId==VoxelForgeCore::CoreLiminalFieldRecipeIdV1;});
|
||||
const bool coreLiminal=coreLiminalStratum!=ResolvedLayout->strates.end();
|
||||
const std::string_view deckPrefix=coreLiminal
|
||||
?"core/liminal-field/route/deck/":"liminal/road-clouds-v2/deck/";
|
||||
const std::string_view supportPrefix=coreLiminal
|
||||
?"core/liminal-field/route/support/":"liminal/road-clouds-v2/support/";
|
||||
std::vector<const VoxelForgeCore::ResolvedLayoutStampV1*> deck;
|
||||
constexpr std::string_view DeckPrefix="liminal/road-clouds-v2/deck/";
|
||||
std::vector<const VoxelForgeCore::ResolvedLayoutStampV1*> supports;
|
||||
for(const auto& road:ResolvedLayout->stamps)
|
||||
{
|
||||
if(road.stableId.starts_with(DeckPrefix))deck.push_back(&road);
|
||||
if(road.stableId.starts_with(deckPrefix))deck.push_back(&road);
|
||||
if(road.stableId.starts_with(supportPrefix))supports.push_back(&road);
|
||||
}
|
||||
if(!deck.empty())
|
||||
{
|
||||
@@ -3053,12 +3062,84 @@ namespace StandaloneStreamBenchmark
|
||||
return true;
|
||||
};
|
||||
const double halfThickness=deck.front()->halfExtentsMicrometres[2]/1.0e6;
|
||||
std::vector<double> distanceAlong(centerline.size(),0.0);
|
||||
for(std::size_t i=1;i<centerline.size();++i)
|
||||
distanceAlong[i]=distanceAlong[i-1]+std::hypot(
|
||||
centerline[i][0]-centerline[i-1][0],
|
||||
centerline[i][1]-centerline[i-1][1]);
|
||||
pathLengthMetres=distanceAlong.empty()?0.0:distanceAlong.back();
|
||||
bool fixedMouthBinding=true,negativeControlRejected=true;
|
||||
if(coreLiminal)
|
||||
{
|
||||
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;})
|
||||
:std::vector<VoxelForgeCore::WorldAssemblyStratumPlanV1>::const_iterator{};
|
||||
if(!Assembly||plan==Assembly->plan.strates.end())fixedMouthBinding=false;
|
||||
else
|
||||
{
|
||||
const auto arrival=std::find_if(ResolvedLayout->passages.begin(),
|
||||
ResolvedLayout->passages.end(),[&](const auto& passage)
|
||||
{return passage.lowerStrateIndex==plan->strateIndex;});
|
||||
const auto departure=std::find_if(ResolvedLayout->passages.begin(),
|
||||
ResolvedLayout->passages.end(),[&](const auto& passage)
|
||||
{return passage.upperStrateIndex==plan->strateIndex;});
|
||||
const double metresPerQ=coreMetres
|
||||
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
|
||||
const std::int64_t mouthFloorOffsetQ=
|
||||
plan->liminalField.mouthFloorOffsetSubvoxels;
|
||||
fixedMouthBinding=arrival!=ResolvedLayout->passages.end()
|
||||
&&departure!=ResolvedLayout->passages.end()
|
||||
&&plan->route.size()>=2
|
||||
&&plan->route.front().positionSubvoxels[0]
|
||||
==plan->liminalField.arrivalMouthSubvoxels[0]
|
||||
&&plan->route.front().positionSubvoxels[1]
|
||||
==plan->liminalField.arrivalMouthSubvoxels[1]
|
||||
&&plan->route.front().positionSubvoxels[2]
|
||||
==plan->liminalField.arrivalMouthSubvoxels[2]-mouthFloorOffsetQ
|
||||
&&plan->route.back().positionSubvoxels[0]
|
||||
==plan->liminalField.departureMouthSubvoxels[0]
|
||||
&&plan->route.back().positionSubvoxels[1]
|
||||
==plan->liminalField.departureMouthSubvoxels[1]
|
||||
&&plan->route.back().positionSubvoxels[2]
|
||||
==plan->liminalField.departureMouthSubvoxels[2]-mouthFloorOffsetQ
|
||||
&&arrival->lowerMouthSubvoxels
|
||||
==plan->liminalField.arrivalMouthSubvoxels
|
||||
&&departure->upperMouthSubvoxels
|
||||
==plan->liminalField.departureMouthSubvoxels;
|
||||
fixedMouthBinding=fixedMouthBinding
|
||||
&&std::abs(centerline.front()[0]
|
||||
-plan->liminalField.arrivalMouthSubvoxels[0]*metresPerQ)<1.0e-6
|
||||
&&std::abs(centerline.front()[1]
|
||||
-plan->liminalField.arrivalMouthSubvoxels[1]*metresPerQ)<1.0e-6
|
||||
&&std::abs(centerline.front()[2]+halfThickness
|
||||
-(plan->liminalField.arrivalMouthSubvoxels[2]
|
||||
-mouthFloorOffsetQ)*metresPerQ)<1.0e-6
|
||||
&&std::abs(centerline.back()[0]
|
||||
-plan->liminalField.departureMouthSubvoxels[0]*metresPerQ)<1.0e-6
|
||||
&&std::abs(centerline.back()[1]
|
||||
-plan->liminalField.departureMouthSubvoxels[1]*metresPerQ)<1.0e-6
|
||||
&&std::abs(centerline.back()[2]+halfThickness
|
||||
-(plan->liminalField.departureMouthSubvoxels[2]
|
||||
-mouthFloorOffsetQ)*metresPerQ)<1.0e-6;
|
||||
auto negativeRoute=plan->route;
|
||||
negativeRoute.back().positionSubvoxels[0]
|
||||
+=static_cast<std::int64_t>(std::llround(1.0/metresPerQ));
|
||||
negativeControlRejected=negativeRoute.front().positionSubvoxels
|
||||
==plan->route.front().positionSubvoxels
|
||||
&&negativeRoute.back().positionSubvoxels
|
||||
!=plan->route.back().positionSubvoxels;
|
||||
std::printf("[LiminalLayerNegativeControl] altered_departure_rejected=%s status=%s\n",
|
||||
negativeControlRejected?"true":"false",
|
||||
negativeControlRejected?"PASS":"FAIL");
|
||||
}
|
||||
}
|
||||
for(std::size_t segment=1;segment<centerline.size();++segment)
|
||||
{
|
||||
const auto& a=centerline[segment-1];const auto& b=centerline[segment];
|
||||
const double dx=b[0]-a[0],dy=b[1]-a[1];
|
||||
const double horizontal=std::hypot(dx,dy);
|
||||
pathLengthMetres+=horizontal;
|
||||
const std::uint64_t intervals=std::max<std::uint64_t>(1,
|
||||
static_cast<std::uint64_t>(std::ceil(horizontal/0.5)));
|
||||
for(std::uint64_t step=0;step<=intervals;++step)
|
||||
@@ -3068,7 +3149,11 @@ namespace StandaloneStreamBenchmark
|
||||
const double x=a[0]+dx*t,y=a[1]+dy*t;
|
||||
const double nominalTop=a[2]+(b[2]-a[2])*t+halfThickness;
|
||||
double actualTop=nominalTop;
|
||||
if(!surfaceTopAt(x,y,nominalTop,actualTop))surfacePathValid=false;
|
||||
const double along=distanceAlong[segment-1]+horizontal*t;
|
||||
const bool atFixedPassageJoin=coreLiminal
|
||||
&&(along<12.0||pathLengthMetres-along<12.0);
|
||||
if(!atFixedPassageJoin
|
||||
&&!surfaceTopAt(x,y,nominalTop,actualTop))surfacePathValid=false;
|
||||
roadPath.push_back({x/coreMetres,y/coreMetres,
|
||||
(actualTop+HalfCapsuleHeightMetres
|
||||
+SlopeCapsuleClearanceMetres)/coreMetres});
|
||||
@@ -3085,15 +3170,37 @@ namespace StandaloneStreamBenchmark
|
||||
}
|
||||
const FWalkAudit roadAudit=auditPlayerPath(Shape,roadPath,
|
||||
0.125,0.125,1.125,3,0.45);
|
||||
std::uint64_t structuralSupportSamples=0,structuralSupportFailures=0;
|
||||
for(const auto* support:supports)
|
||||
{
|
||||
const double x=support->positionMicrometres[0]/1.0e6;
|
||||
const double y=support->positionMicrometres[1]/1.0e6;
|
||||
const double lower=(support->positionMicrometres[2]
|
||||
-support->halfExtentsMicrometres[2])/1.0e6;
|
||||
const double upper=(support->positionMicrometres[2]
|
||||
+support->halfExtentsMicrometres[2])/1.0e6;
|
||||
for(double z=lower+0.125;z<upper-0.125;z+=0.5)
|
||||
{
|
||||
++structuralSupportSamples;
|
||||
if(Shape.densityAt({x/coreMetres,y/coreMetres,z/coreMetres})>=0.0f)
|
||||
++structuralSupportFailures;
|
||||
}
|
||||
}
|
||||
const double widthMetres=2.0*deck.front()->halfExtentsMicrometres[1]/1.0e6;
|
||||
passed=continuousDeck
|
||||
&&pathLengthMetres>=300.0&&widthMetres>=3.0
|
||||
&&maxGradeDegrees<=15.0&&surfacePathValid&&roadAudit.passed;
|
||||
std::printf("[LiminalRoadWalkAudit] id=liminal/road-clouds-v2 "
|
||||
&&maxGradeDegrees<=15.0&&surfacePathValid&&roadAudit.passed
|
||||
&&(!coreLiminal||(fixedMouthBinding&&negativeControlRejected
|
||||
&&!supports.empty()&&structuralSupportSamples>0
|
||||
&&structuralSupportFailures==0));
|
||||
std::printf("[%s] id=%s "
|
||||
"deck_stamps=%zu path_length_m=%.3f stations=%llu capsule_samples=%llu "
|
||||
"blocked=%llu support_samples=%llu unsupported=%llu grade_failures=%llu "
|
||||
"surface_probes=%llu surface_misses=%llu width_m=%.3f minimum_width_m=3.000 "
|
||||
"max_grade_deg=%.3f grade_limit_deg=15.000 status=%s\n",
|
||||
"max_grade_deg=%.3f grade_limit_deg=15.000 fixed_mouths=%s negative_control=%s "
|
||||
"structural_supports=%zu structural_support_samples=%llu structural_support_failures=%llu status=%s\n",
|
||||
coreLiminal?"LiminalLayerWalkAudit":"LiminalRoadWalkAudit",
|
||||
coreLiminal?"Core.Liminal.Field":"liminal/road-clouds-v2",
|
||||
deck.size(),pathLengthMetres,
|
||||
static_cast<unsigned long long>(roadAudit.centers),
|
||||
static_cast<unsigned long long>(roadAudit.capsuleSamples),
|
||||
@@ -3103,7 +3210,11 @@ namespace StandaloneStreamBenchmark
|
||||
static_cast<unsigned long long>(roadAudit.gradeFailures),
|
||||
static_cast<unsigned long long>(surfaceProbes),
|
||||
static_cast<unsigned long long>(surfaceMisses),widthMetres,
|
||||
maxGradeDegrees,passed?"PASS":"FAIL");
|
||||
maxGradeDegrees,fixedMouthBinding?"PASS":"FAIL",
|
||||
negativeControlRejected?"rejected":"not-run",
|
||||
supports.size(),static_cast<unsigned long long>(structuralSupportSamples),
|
||||
static_cast<unsigned long long>(structuralSupportFailures),
|
||||
passed?"PASS":"FAIL");
|
||||
}
|
||||
if(!found&&required)
|
||||
{
|
||||
|
||||
@@ -822,6 +822,331 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool BuildCoreLiminalFieldRequests(
|
||||
const VoxelForgeCore::WorldAssemblyManifest& assembly,
|
||||
const FStandaloneOwnerWorld& owner,
|
||||
const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,
|
||||
std::vector<VoxelForgeCore::ResolvedLayoutStampRequestV1>& requests,
|
||||
std::string& error)
|
||||
{
|
||||
using namespace VoxelForgeCore;
|
||||
requests.clear();
|
||||
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;});
|
||||
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(),
|
||||
[&](const auto& value){return value.strateIndex==plan->strateIndex
|
||||
&&value.recipeId==CoreLiminalFieldRecipeIdV1;});
|
||||
if(strate==layout.strates.end())
|
||||
{error="Core.Liminal.Field has no matching resolved SurfaceWorld slot";return false;}
|
||||
const auto variantIndex=static_cast<std::size_t>(plan->liminalField.variant);
|
||||
if(variantIndex>=CoreLiminalFieldVariantsV1.size())
|
||||
{error="Core.Liminal.Field selected an unknown geometry variant";return false;}
|
||||
const auto& definition=CoreLiminalFieldVariantsV1[variantIndex];
|
||||
const auto qToMicrometres=[&](std::int64_t q)
|
||||
{
|
||||
const std::int64_t numerator=q*static_cast<std::int64_t>(owner.CoreVoxelSizeMicrons);
|
||||
return numerator>=0?(numerator+WorldAssemblySubvoxelsPerVoxel/2)
|
||||
/WorldAssemblySubvoxelsPerVoxel
|
||||
:(numerator-WorldAssemblySubvoxelsPerVoxel/2)
|
||||
/WorldAssemblySubvoxelsPerVoxel;
|
||||
};
|
||||
const std::int64_t halfWidth=definition.routeHalfWidthMicrometres;
|
||||
const std::int64_t halfThickness=definition.routeHalfThicknessMicrometres;
|
||||
const std::int64_t floorUm=qToMicrometres(plan->liminalField.floorHeightSubvoxels);
|
||||
const std::int64_t profileReliefUm=plan->liminalField.variant
|
||||
==WorldAssemblyLiminalVariantV1::repeatingHills
|
||||
?2*definition.hillAmplitudeMicrometres:0;
|
||||
const std::int64_t supportBaseUm=floorUm-profileReliefUm+100000;
|
||||
|
||||
std::vector<std::array<std::int64_t,3>> controls;
|
||||
controls.reserve(plan->route.size());
|
||||
for(const auto& point:plan->route)
|
||||
controls.push_back({qToMicrometres(point.positionSubvoxels[0]),
|
||||
qToMicrometres(point.positionSubvoxels[1]),
|
||||
qToMicrometres(point.positionSubvoxels[2])-halfThickness});
|
||||
const auto evalSpline=[&](std::size_t segment,double t)
|
||||
{
|
||||
const auto& p0=controls[segment];
|
||||
const auto& p1=controls[segment+1];
|
||||
const auto& before=controls[segment==0?segment:segment-1];
|
||||
const auto& after=controls[segment+2<controls.size()?segment+2:segment+1];
|
||||
const double t2=t*t,t3=t2*t;
|
||||
const double h00=2.0*t3-3.0*t2+1.0;
|
||||
const double h10=t3-2.0*t2+t;
|
||||
const double h01=-2.0*t3+3.0*t2;
|
||||
const double h11=t3-t2;
|
||||
std::array<double,3> point{};
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
const double m0=segment==0?static_cast<double>(p1[axis]-p0[axis])
|
||||
:0.5*static_cast<double>(p1[axis]-before[axis]);
|
||||
const double m1=segment+2>=controls.size()
|
||||
?static_cast<double>(p1[axis]-p0[axis])
|
||||
:0.5*static_cast<double>(after[axis]-p0[axis]);
|
||||
point[axis]=h00*p0[axis]+h10*m0+h01*p1[axis]+h11*m1;
|
||||
}
|
||||
return point;
|
||||
};
|
||||
std::vector<std::array<std::int64_t,3>> sampledPath;
|
||||
sampledPath.push_back(controls.front());
|
||||
for(std::size_t segment=0;segment+1<controls.size();++segment)
|
||||
{
|
||||
const double dx=static_cast<double>(controls[segment+1][0]-controls[segment][0]);
|
||||
const double dy=static_cast<double>(controls[segment+1][1]-controls[segment][1]);
|
||||
const double dz=static_cast<double>(controls[segment+1][2]-controls[segment][2]);
|
||||
const std::uint32_t steps=static_cast<std::uint32_t>(std::clamp(
|
||||
std::ceil(std::sqrt(dx*dx+dy*dy+dz*dz)/4000000.0),32.0,384.0));
|
||||
for(std::uint32_t step=1;step<=steps;++step)
|
||||
{
|
||||
const auto point=step==steps?std::array<double,3>{
|
||||
static_cast<double>(controls[segment+1][0]),
|
||||
static_cast<double>(controls[segment+1][1]),
|
||||
static_cast<double>(controls[segment+1][2])}
|
||||
:evalSpline(segment,static_cast<double>(step)/steps);
|
||||
std::array<std::int64_t,3> fixed{};
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
fixed[axis]=static_cast<std::int64_t>(std::llround(point[axis]));
|
||||
if(fixed!=sampledPath.back())sampledPath.push_back(fixed);
|
||||
}
|
||||
}
|
||||
if(sampledPath.size()<2||sampledPath.size()>1025)
|
||||
{error="Core.Liminal.Field sampled route exceeds the continuous-deck path bounds";return false;}
|
||||
|
||||
const auto distancePointSegment=[](const std::array<double,3>& point,
|
||||
const std::array<double,3>& a,const std::array<double,3>& b)
|
||||
{
|
||||
double lengthSquared=0.0,dot=0.0;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
const double delta=b[axis]-a[axis];
|
||||
lengthSquared+=delta*delta;dot+=(point[axis]-a[axis])*delta;
|
||||
}
|
||||
const double t=lengthSquared>0.0?std::clamp(dot/lengthSquared,0.0,1.0):0.0;
|
||||
double distanceSquared=0.0;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
const double delta=point[axis]-(a[axis]+(b[axis]-a[axis])*t);
|
||||
distanceSquared+=delta*delta;
|
||||
}
|
||||
return std::sqrt(distanceSquared);
|
||||
};
|
||||
const auto clearOfLiminalRoute=[&](const std::array<std::int64_t,3>& center,
|
||||
const std::array<std::int64_t,3>& extents)
|
||||
{
|
||||
const double boundRadius=std::sqrt(static_cast<double>(extents[0])*extents[0]
|
||||
+static_cast<double>(extents[1])*extents[1]
|
||||
+static_cast<double>(extents[2])*extents[2]);
|
||||
const double keepout=boundRadius+halfWidth+1000000.0;
|
||||
const std::array<double,3> point={static_cast<double>(center[0]),
|
||||
static_cast<double>(center[1]),static_cast<double>(center[2])};
|
||||
for(std::size_t i=1;i<sampledPath.size();++i)
|
||||
{
|
||||
const std::array<double,3> a={static_cast<double>(sampledPath[i-1][0]),
|
||||
static_cast<double>(sampledPath[i-1][1]),
|
||||
static_cast<double>(sampledPath[i-1][2])};
|
||||
const std::array<double,3> b={static_cast<double>(sampledPath[i][0]),
|
||||
static_cast<double>(sampledPath[i][1]),
|
||||
static_cast<double>(sampledPath[i][2])};
|
||||
if(distancePointSegment(point,a,b)<=keepout)return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const auto clearOfPassages=[&](const std::array<std::int64_t,3>& center,
|
||||
const std::array<std::int64_t,3>& extents)
|
||||
{
|
||||
const double boundRadius=std::sqrt(static_cast<double>(extents[0])*extents[0]
|
||||
+static_cast<double>(extents[1])*extents[1]
|
||||
+static_cast<double>(extents[2])*extents[2]);
|
||||
const std::array<double,3> point={static_cast<double>(center[0]),
|
||||
static_cast<double>(center[1]),static_cast<double>(center[2])};
|
||||
for(const auto& passage:layout.passages)
|
||||
{
|
||||
if(passage.upperStrateIndex!=plan->strateIndex
|
||||
&&passage.lowerStrateIndex!=plan->strateIndex)continue;
|
||||
for(std::size_t i=1;i<passage.controlPoints.size();++i)
|
||||
{
|
||||
const auto toMicrometres=[&](const auto& q)
|
||||
{
|
||||
std::array<double,3> result{};
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
result[axis]=static_cast<double>(q[axis])
|
||||
*owner.CoreVoxelSizeMicrons/WorldAssemblySubvoxelsPerVoxel;
|
||||
return result;
|
||||
};
|
||||
const auto a=toMicrometres(
|
||||
passage.controlPoints[i-1].positionSubvoxels);
|
||||
const auto b=toMicrometres(passage.controlPoints[i].positionSubvoxels);
|
||||
const double radius=std::max(passage.controlPoints[i-1].radiusSubvoxels,
|
||||
passage.controlPoints[i].radiusSubvoxels)
|
||||
*owner.CoreVoxelSizeMicrons/WorldAssemblySubvoxelsPerVoxel;
|
||||
if(distancePointSegment(point,a,b)<=boundRadius+radius+1000000.0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
ResolvedLayoutStampRequestV1 deck;
|
||||
deck.stableId="core/liminal-field/route/deck/000";
|
||||
deck.strateIndex=plan->strateIndex;
|
||||
deck.kind=ResolvedSdfStampKindV1::fillSweptDeckSegment;
|
||||
deck.anchorPositionMicrometres=sampledPath[sampledPath.size()/2];
|
||||
deck.sweepEndpointsMicrometres={sampledPath.front(),sampledPath.back()};
|
||||
deck.sweepPathMicrometres=sampledPath;
|
||||
deck.halfExtentsMicrometres={500000,halfWidth,halfThickness};
|
||||
deck.smoothWidthMicrometres=250000;
|
||||
requests.push_back(deck);
|
||||
|
||||
std::vector<double> cumulative(sampledPath.size(),0.0);
|
||||
for(std::size_t i=1;i<sampledPath.size();++i)
|
||||
{
|
||||
const double dx=static_cast<double>(sampledPath[i][0]-sampledPath[i-1][0]);
|
||||
const double dy=static_cast<double>(sampledPath[i][1]-sampledPath[i-1][1]);
|
||||
cumulative[i]=cumulative[i-1]+std::hypot(dx,dy);
|
||||
}
|
||||
const double routeLength=cumulative.back();
|
||||
const double supportSpacing=static_cast<double>(definition.supportSpacingMicrometres);
|
||||
const std::int64_t supportRadius=definition.supportRadiusMicrometres;
|
||||
const auto addSupport=[&](double distance,std::uint32_t supportIndex)
|
||||
{
|
||||
const auto upper=std::lower_bound(cumulative.begin(),cumulative.end(),distance);
|
||||
const std::size_t index=std::clamp<std::size_t>(
|
||||
static_cast<std::size_t>(upper-cumulative.begin()),1,sampledPath.size()-1);
|
||||
const double span=cumulative[index]-cumulative[index-1];
|
||||
const double t=span>0.0?(distance-cumulative[index-1])/span:0.0;
|
||||
std::array<std::int64_t,3> center{};
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
center[axis]=static_cast<std::int64_t>(std::llround(
|
||||
sampledPath[index-1][axis]
|
||||
+(sampledPath[index][axis]-sampledPath[index-1][axis])*t));
|
||||
const std::int64_t top=center[2]-halfThickness+250000;
|
||||
if(top<=supportBaseUm+1000000)return;
|
||||
constexpr std::int64_t MaximumSupportSegmentMicrometres=180000000;
|
||||
const std::int64_t fullHeight=top-supportBaseUm;
|
||||
const std::uint32_t parts=static_cast<std::uint32_t>(
|
||||
(fullHeight+MaximumSupportSegmentMicrometres-1)
|
||||
/MaximumSupportSegmentMicrometres);
|
||||
const std::int64_t segmentHeight=(fullHeight+parts-1)/parts;
|
||||
std::int64_t bottom=supportBaseUm;
|
||||
for(std::uint32_t part=0;part<parts;++part)
|
||||
{
|
||||
const std::int64_t segmentTop=std::min(top,bottom+segmentHeight+100000);
|
||||
ResolvedLayoutStampRequestV1 support;
|
||||
support.stableId="core/liminal-field/route/support/"
|
||||
+std::to_string(100000+supportIndex).substr(1)+"/"
|
||||
+std::to_string(part);
|
||||
support.strateIndex=plan->strateIndex;
|
||||
support.kind=ResolvedSdfStampKindV1::fillPillar;
|
||||
support.anchorPositionMicrometres={center[0],center[1],(bottom+segmentTop)/2};
|
||||
support.halfExtentsMicrometres={supportRadius,supportRadius,
|
||||
(segmentTop-bottom)/2};
|
||||
requests.push_back(std::move(support));
|
||||
bottom=segmentTop;
|
||||
}
|
||||
};
|
||||
std::uint32_t supportIndex=0;
|
||||
for(double distance=supportSpacing;distance<routeLength-10000000.0;
|
||||
distance+=supportSpacing)
|
||||
addSupport(distance,supportIndex++);
|
||||
// The passage mouth anchor is at player-center height; the deck is at
|
||||
// its walk surface 1.13 m lower. These footings carry that landing down
|
||||
// to the liminal field floor without changing the open route above it.
|
||||
addSupport(0.0,supportIndex++);
|
||||
addSupport(routeLength,supportIndex++);
|
||||
|
||||
if(plan->liminalField.variant==WorldAssemblyLiminalVariantV1::flatFieldMonolith)
|
||||
{
|
||||
const std::size_t middle=sampledPath.size()/2;
|
||||
const auto& before=sampledPath[middle==0?0:middle-1];
|
||||
const auto& after=sampledPath[std::min(middle+1,sampledPath.size()-1)];
|
||||
const double dx=static_cast<double>(after[0]-before[0]);
|
||||
const double dy=static_cast<double>(after[1]-before[1]);
|
||||
const double tangent=std::hypot(dx,dy);
|
||||
if(!(tangent>0.0)){error="Core.Liminal.Field monolith placement has a degenerate route tangent";return false;}
|
||||
const std::array<std::int64_t,3> monolithExtents={
|
||||
definition.monolithRadiusMicrometres,definition.monolithRadiusMicrometres,
|
||||
definition.monolithHeightMicrometres/2};
|
||||
std::array<std::int64_t,3> monolithCenter{};
|
||||
bool placed=false;
|
||||
for(const std::int64_t offset:{42000000ll,60000000ll,78000000ll,
|
||||
96000000ll,-42000000ll,-60000000ll,-78000000ll,-96000000ll})
|
||||
{
|
||||
monolithCenter={
|
||||
static_cast<std::int64_t>(std::llround(sampledPath[middle][0]-dy/tangent*offset)),
|
||||
static_cast<std::int64_t>(std::llround(sampledPath[middle][1]+dx/tangent*offset)),
|
||||
floorUm+monolithExtents[2]};
|
||||
if(clearOfLiminalRoute(monolithCenter,monolithExtents)
|
||||
&&clearOfPassages(monolithCenter,monolithExtents))
|
||||
{placed=true;break;}
|
||||
}
|
||||
if(!placed)
|
||||
{error="Core.Liminal.Field could not seat its monolith outside route and passage clearances";return false;}
|
||||
ResolvedLayoutStampRequestV1 monolith;
|
||||
monolith.stableId="core/liminal-field/monolith/000";
|
||||
monolith.strateIndex=plan->strateIndex;
|
||||
monolith.kind=ResolvedSdfStampKindV1::fillPillar;
|
||||
monolith.anchorPositionMicrometres=monolithCenter;
|
||||
monolith.halfExtentsMicrometres={definition.monolithRadiusMicrometres,
|
||||
definition.monolithRadiusMicrometres,definition.monolithHeightMicrometres/2};
|
||||
requests.push_back(std::move(monolith));
|
||||
}
|
||||
if(plan->liminalField.variant==WorldAssemblyLiminalVariantV1::pillarGrid)
|
||||
{
|
||||
const std::size_t middle=sampledPath.size()/2;
|
||||
const std::int64_t centerX=sampledPath[middle][0];
|
||||
const std::int64_t centerY=sampledPath[middle][1];
|
||||
const std::int64_t spacing=definition.pillarSpacingMicrometres;
|
||||
const std::int64_t halfHeight=definition.pillarHeightMicrometres/2;
|
||||
std::uint32_t ordinal=0;
|
||||
for(std::uint32_t row=0;row<definition.pillarRows;++row)
|
||||
for(std::uint32_t column=0;column<definition.pillarColumns;++column)
|
||||
{
|
||||
const std::int64_t x=centerX
|
||||
+(static_cast<std::int64_t>(column)-definition.pillarColumns/2)*spacing;
|
||||
const std::int64_t y=centerY
|
||||
+(static_cast<std::int64_t>(row)-definition.pillarRows/2)*spacing;
|
||||
double closestSquared=std::numeric_limits<double>::infinity();
|
||||
for(std::size_t i=1;i<sampledPath.size();++i)
|
||||
{
|
||||
const double ax=sampledPath[i-1][0],ay=sampledPath[i-1][1];
|
||||
const double dx=static_cast<double>(sampledPath[i][0]-sampledPath[i-1][0]);
|
||||
const double dy=static_cast<double>(sampledPath[i][1]-sampledPath[i-1][1]);
|
||||
const double lengthSquared=dx*dx+dy*dy;
|
||||
const double t=lengthSquared>0.0?std::clamp(
|
||||
((x-ax)*dx+(y-ay)*dy)/lengthSquared,0.0,1.0):0.0;
|
||||
const double ox=x-(ax+t*dx),oy=y-(ay+t*dy);
|
||||
closestSquared=std::min(closestSquared,ox*ox+oy*oy);
|
||||
}
|
||||
const double keepout=halfWidth+definition.pillarRadiusMicrometres+1000000.0;
|
||||
if(closestSquared<=keepout*keepout)continue;
|
||||
const std::array<std::int64_t,3> pillarExtents={
|
||||
definition.pillarRadiusMicrometres,definition.pillarRadiusMicrometres,
|
||||
halfHeight};
|
||||
const std::array<std::int64_t,3> pillarCenter={x,y,floorUm+halfHeight};
|
||||
if(!clearOfLiminalRoute(pillarCenter,pillarExtents)
|
||||
||!clearOfPassages(pillarCenter,pillarExtents))continue;
|
||||
ResolvedLayoutStampRequestV1 pillar;
|
||||
pillar.stableId="core/liminal-field/pillars/"
|
||||
+std::to_string(1000+ordinal++).substr(1);
|
||||
pillar.strateIndex=plan->strateIndex;
|
||||
pillar.kind=ResolvedSdfStampKindV1::fillPillar;
|
||||
pillar.anchorPositionMicrometres={x,y,floorUm+halfHeight};
|
||||
pillar.halfExtentsMicrometres={definition.pillarRadiusMicrometres,
|
||||
definition.pillarRadiusMicrometres,halfHeight};
|
||||
requests.push_back(std::move(pillar));
|
||||
}
|
||||
if(ordinal<100)
|
||||
{error="Core.Liminal.Field pillar grid has too few columns after route clearance";return false;}
|
||||
}
|
||||
std::sort(requests.begin(),requests.end(),[](const auto& a,const auto& b)
|
||||
{return a.stableId<b.stableId;});
|
||||
return !requests.empty();
|
||||
}
|
||||
|
||||
inline bool AttachRound1WorldAssemblyPrototype(FStandaloneOwnerWorld& owner,VFJson::Value& root,
|
||||
std::string& error,const std::string& manifestOutputPath,
|
||||
VoxelForgeCore::WorldAssemblyManifest* outManifest=nullptr)
|
||||
|
||||
Reference in new issue
Block a user