wip(assembler): round L23 -- inter-strate passages planned from the resolved layout and carved
After L22 the FI -> SW passage no longer connected the strates (legacy owner-voxel coordinates, lower end 23 m above SW), and Core never carved passages through the bands. Now the director plans passages from the resolved layout (PlanResolvedLayoutPassagesV1; deterministic hash of seed/strates/connection; placement disk from PassageConfig, 8-24 m around (0,0)); shared composition applies generator field -> bands -> bounded passage carve and support shelf, opening the bands only along the tunnel. Mouths bind to the strate routes. Capsule audit: 0 blocked / 0 unsupported (325,962 samples FI, 284,319 TunnelNetwork), negative controls detected; all 8 generator kinds exercised. SW away from the passage is bit-identical. Layout V3, evaluator 9. Open: look not yet natural (stepped tube in FI void, terraced SW arrival); SW-as-upper route planning fails; Unreal adapter uncompiled. 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
55c26a05a7
commit
71d09a8c52
14 files changed
+1465
-112
No files matched your search
@@ -67,6 +67,7 @@ namespace
|
||||
std::filesystem::path ResolvedLayoutOutputPath;
|
||||
bool ResolvedLayoutOnly=false;
|
||||
bool AuditResolvedLayoutBoundaries=false;
|
||||
bool AuditResolvedLayoutPassages=false;
|
||||
bool AuditSurfaceClearance=false;
|
||||
};
|
||||
|
||||
@@ -125,6 +126,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-resolved-layout-passages (capsule, support, route-binding and blocked-control audit)\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"
|
||||
@@ -145,6 +147,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-resolved-layout-passages")options.AuditResolvedLayoutPassages=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;
|
||||
@@ -332,14 +335,14 @@ 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::ResolvedLayoutSnapshotV2 resolvedLayout;
|
||||
VoxelForgeCore::ResolvedLayoutSnapshotV3 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);
|
||||
const auto roundTrip=VoxelForgeCore::ResolvedLayoutSnapshotV3::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;}
|
||||
@@ -352,16 +355,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::AuditResolvedLayoutValidatorV2();
|
||||
const auto controls=VoxelForgeCore::AuditResolvedLayoutValidatorV3();
|
||||
std::vector<VoxelForgeCore::ResolvedLayoutIssueV2> compatibilityIssues;
|
||||
std::string compatibilityError;
|
||||
const bool compatibilityAccepted=resolvedLayout.validate(
|
||||
VoxelForgeCore::ResolvedLayoutValidationModeV2::compatibility,
|
||||
VoxelForgeCore::ResolvedLayoutValidationModeV3::compatibility,
|
||||
compatibilityIssues,compatibilityError);
|
||||
std::vector<VoxelForgeCore::ResolvedLayoutIssueV2> strictIssues;
|
||||
std::string strictError;
|
||||
const bool strictAccepted=resolvedLayout.validate(
|
||||
VoxelForgeCore::ResolvedLayoutValidationModeV2::strict,strictIssues,strictError);
|
||||
VoxelForgeCore::ResolvedLayoutValidationModeV3::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",
|
||||
@@ -548,5 +551,6 @@ int main(int argc,char** argv)
|
||||
options.RegionCrossing,options.AuditTileShapeReadiness,
|
||||
options.DiagnosticSlowShapeCompileMs,options.AuditBasePageEquivalence,
|
||||
options.AuditRouteParity,options.AuditResolvedLayoutBoundaries,
|
||||
options.AuditResolvedLayoutPassages,
|
||||
&resolvedLayout);
|
||||
}
|
||||
@@ -255,7 +255,8 @@ namespace StandaloneStreamBenchmark
|
||||
bool bAuditTileShapeReadiness=false,int DiagnosticSlowShapeCompileMs=0,
|
||||
bool bAuditBasePageEquivalence=false,bool bAuditRouteParity=false,
|
||||
bool bAuditResolvedLayoutBoundaries=false,
|
||||
const VoxelForgeCore::ResolvedLayoutSnapshotV2* ResolvedLayout=nullptr)
|
||||
bool bAuditResolvedLayoutPassages=false,
|
||||
const VoxelForgeCore::ResolvedLayoutSnapshotV3* ResolvedLayout=nullptr)
|
||||
{
|
||||
using Clock=std::chrono::steady_clock;
|
||||
FStandaloneOwnerWorld& World=*OwnerWorld;
|
||||
@@ -440,6 +441,349 @@ namespace StandaloneStreamBenchmark
|
||||
Streamer.Stop();
|
||||
std::fprintf(stderr,"[StreamPipeline] full shape build failed: %s\n",BuildError.c_str());return 2;
|
||||
}
|
||||
if(bAuditResolvedLayoutPassages)
|
||||
{
|
||||
if(ResolvedLayout==nullptr||!CoreSnapshot.resolvedLayout())
|
||||
{Streamer.Stop();std::fprintf(stderr,"[ResolvedPassageAudit] missing resolved-layout snapshot\n");return 2;}
|
||||
struct FWalkAudit
|
||||
{
|
||||
std::uint64_t segments=0,centers=0,capsuleSamples=0,blocked=0;
|
||||
std::uint64_t supportSamples=0,unsupported=0,gradeFailures=0;
|
||||
std::array<std::array<double,4>,8> firstBlocked{};
|
||||
std::array<std::array<double,4>,8> firstUnsupported{};
|
||||
std::size_t firstBlockedCount=0,firstUnsupportedCount=0;
|
||||
bool passed=false;
|
||||
};
|
||||
const double coreMetres=static_cast<double>(World.CoreVoxelSizeMicrons)/1000000.0;
|
||||
const auto auditPlayerPath=[&](const VoxelForgeCore::World& Field,
|
||||
const std::vector<std::array<double,3>>& path,
|
||||
double supportStartMetres,double supportStepMetres,
|
||||
double supportMaximumMetres,std::uint32_t minimumSupportProbes,
|
||||
double supportProbeRadiusMetres)
|
||||
{
|
||||
FWalkAudit audit;
|
||||
if(path.size()<2)return audit;
|
||||
constexpr std::array<double,7> heights{{-0.84,-0.60,-0.30,0.0,0.30,0.60,0.84}};
|
||||
constexpr std::array<std::array<double,2>,8> directions{{
|
||||
{{1.0,0.0}},{{0.7071067811865475,0.7071067811865475}},{{0.0,1.0}},
|
||||
{{-0.7071067811865475,0.7071067811865475}},{{-1.0,0.0}},
|
||||
{{-0.7071067811865475,-0.7071067811865475}},{{0.0,-1.0}},
|
||||
{{0.7071067811865475,-0.7071067811865475}}}};
|
||||
const auto solidAt=[&](double xMetres,double yMetres,double zMetres)
|
||||
{
|
||||
++audit.capsuleSamples;
|
||||
const VoxelForgeCore::Vec3d position{xMetres/coreMetres,yMetres/coreMetres,zMetres/coreMetres};
|
||||
const float density=Field.densityAt(position);
|
||||
if(density<0.0f)
|
||||
{
|
||||
if(audit.firstBlockedCount<audit.firstBlocked.size())
|
||||
{
|
||||
audit.firstBlocked[audit.firstBlockedCount++]={xMetres,yMetres,zMetres,density};
|
||||
std::printf("[ResolvedWalkDispatch] kind=blocked xyz_m=(%.3f,%.3f,%.3f) path=%s\n",
|
||||
xMetres,yMetres,zMetres,Field.densityDispatchPathAt(position).c_str());
|
||||
}
|
||||
++audit.blocked;return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
for(std::size_t segment=1;segment<path.size();++segment)
|
||||
{
|
||||
++audit.segments;
|
||||
const auto& a=path[segment-1];const auto& b=path[segment];
|
||||
const double dx=b[0]-a[0],dy=b[1]-a[1],dz=b[2]-a[2];
|
||||
const double horizontal=std::hypot(dx,dy)*coreMetres;
|
||||
const double vertical=std::abs(dz)*coreMetres;
|
||||
if(horizontal<=1.0e-6||vertical>horizontal*0.2679491924311227)
|
||||
++audit.gradeFailures;
|
||||
const double length=std::sqrt(dx*dx+dy*dy+dz*dz)*coreMetres;
|
||||
const std::uint64_t intervals=std::max<std::uint64_t>(1,
|
||||
static_cast<std::uint64_t>(std::ceil(length/0.5)));
|
||||
for(std::uint64_t step=0;step<=intervals;++step)
|
||||
{
|
||||
if(segment>1&&step==0)continue;
|
||||
const double t=static_cast<double>(step)/intervals;
|
||||
const double x=(a[0]+dx*t)*coreMetres;
|
||||
const double y=(a[1]+dy*t)*coreMetres;
|
||||
const double z=(a[2]+dz*t)*coreMetres;
|
||||
++audit.centers;
|
||||
for(const double h:heights)
|
||||
{
|
||||
const double capOffset=std::max(0.0,std::abs(h)-0.54);
|
||||
const double radial=std::abs(h)<=0.54?0.34
|
||||
:std::sqrt(std::max(0.0,0.34*0.34-capOffset*capOffset));
|
||||
solidAt(x,y,z+h);
|
||||
for(const auto& direction:directions)
|
||||
solidAt(x+direction[0]*radial,y+direction[1]*radial,z+h);
|
||||
}
|
||||
const double footZ=z-0.88;
|
||||
const std::array<std::array<double,2>,5> supportOffsets{{
|
||||
{{0.0,0.0}},{{supportProbeRadiusMetres,0.0}},
|
||||
{{-supportProbeRadiusMetres,0.0}},
|
||||
{{0.0,supportProbeRadiusMetres}},
|
||||
{{0.0,-supportProbeRadiusMetres}}}};
|
||||
std::uint32_t supportedProbes=0;
|
||||
double firstUnsupportedX=x,firstUnsupportedY=y,lastUnsupportedDensity=0.0;
|
||||
for(const auto& offset:supportOffsets)
|
||||
{
|
||||
const double supportX=x+offset[0],supportY=y+offset[1];
|
||||
bool supported=false;
|
||||
for(double depth=supportStartMetres;
|
||||
depth<=supportMaximumMetres+1.0e-6;depth+=supportStepMetres)
|
||||
{
|
||||
++audit.supportSamples;
|
||||
lastUnsupportedDensity=Field.densityAt({supportX/coreMetres,
|
||||
supportY/coreMetres,(footZ-depth)/coreMetres});
|
||||
if(lastUnsupportedDensity<0.0)
|
||||
{
|
||||
supported=true;break;
|
||||
}
|
||||
}
|
||||
if(supported)++supportedProbes;
|
||||
else{firstUnsupportedX=supportX;firstUnsupportedY=supportY;}
|
||||
}
|
||||
if(supportedProbes<minimumSupportProbes)
|
||||
{
|
||||
++audit.unsupported;
|
||||
if(audit.firstUnsupportedCount<audit.firstUnsupported.size())
|
||||
{
|
||||
audit.firstUnsupported[audit.firstUnsupportedCount++]={
|
||||
firstUnsupportedX,firstUnsupportedY,footZ,lastUnsupportedDensity};
|
||||
std::printf("[ResolvedWalkDispatch] kind=unsupported xyz_m=(%.3f,%.3f,%.3f) support_probes=%u/5 path=%s\n",
|
||||
firstUnsupportedX,firstUnsupportedY,footZ,supportedProbes,
|
||||
Field.densityDispatchPathAt({firstUnsupportedX/coreMetres,
|
||||
firstUnsupportedY/coreMetres,footZ/coreMetres}).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
audit.passed=audit.centers>0&&audit.blocked==0&&audit.unsupported==0
|
||||
&&audit.gradeFailures==0;
|
||||
return audit;
|
||||
};
|
||||
const auto qToMetres=[&](std::int64_t q)
|
||||
{return static_cast<double>(q)*World.CoreVoxelSizeMicrons
|
||||
/(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel*1000000.0);};
|
||||
const auto findStratum=[&](std::int32_t index)->const VoxelForgeCore::ResolvedLayoutStratumV2*
|
||||
{
|
||||
const auto it=std::find_if(ResolvedLayout->strates.begin(),ResolvedLayout->strates.end(),
|
||||
[index](const auto& s){return s.strateIndex==index;});
|
||||
return it==ResolvedLayout->strates.end()?nullptr:&*it;
|
||||
};
|
||||
const auto mouthInContent=[&](std::int32_t index,const std::array<std::int64_t,3>& mouth)
|
||||
{
|
||||
const auto* stratum=findStratum(index);if(!stratum)return false;
|
||||
const std::int64_t zUm=static_cast<std::int64_t>(std::llround(
|
||||
static_cast<double>(mouth[2])*World.CoreVoxelSizeMicrons
|
||||
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel));
|
||||
return zUm>=stratum->content.bottomMicrometres&&zUm<stratum->content.topMicrometres;
|
||||
};
|
||||
std::uint64_t passageCount=0,passagePasses=0,blockedControls=0;
|
||||
for(const auto& passage:ResolvedLayout->passages)
|
||||
{
|
||||
std::vector<std::array<double,3>> path;path.reserve(passage.controlPoints.size());
|
||||
for(const auto& point:passage.controlPoints)
|
||||
{
|
||||
const double playerZ=static_cast<double>(point.positionSubvoxels[2]
|
||||
-point.radiusSubvoxels)+1.13*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel
|
||||
*1000000.0/World.CoreVoxelSizeMicrons;
|
||||
path.push_back({qToMetres(point.positionSubvoxels[0])/coreMetres,
|
||||
qToMetres(point.positionSubvoxels[1])/coreMetres,playerZ/256.0});
|
||||
}
|
||||
const FWalkAudit passageAudit=auditPlayerPath(Shape,path,0.125,0.125,0.25,3,0.45);
|
||||
const auto radialSquared=[](const std::array<std::int64_t,3>& p)
|
||||
{return p[0]*p[0]+p[1]*p[1];};
|
||||
const bool mouthsInRadius=radialSquared(passage.upperMouthSubvoxels)
|
||||
<=passage.placementRadiusSubvoxels*passage.placementRadiusSubvoxels
|
||||
&&radialSquared(passage.lowerMouthSubvoxels)
|
||||
<=passage.placementRadiusSubvoxels*passage.placementRadiusSubvoxels;
|
||||
const bool mouthsInContent=mouthInContent(passage.upperStrateIndex,passage.upperMouthSubvoxels)
|
||||
&&mouthInContent(passage.lowerStrateIndex,passage.lowerMouthSubvoxels);
|
||||
++passageCount;passagePasses+=passageAudit.passed&&mouthsInRadius&&mouthsInContent;
|
||||
std::printf("[ResolvedPassageAudit] id=%s interface=%d-%d control_points=%zu center_samples=%llu capsule_samples=%llu blocked=%llu support_samples=%llu unsupported=%llu grade_failures=%llu mouth_content=%d/2 mouth_radius=%d/2 walkability=%s\n",
|
||||
passage.stableId.c_str(),passage.upperStrateIndex,passage.lowerStrateIndex,
|
||||
passage.controlPoints.size(),static_cast<unsigned long long>(passageAudit.centers),
|
||||
static_cast<unsigned long long>(passageAudit.capsuleSamples),
|
||||
static_cast<unsigned long long>(passageAudit.blocked),
|
||||
static_cast<unsigned long long>(passageAudit.supportSamples),
|
||||
static_cast<unsigned long long>(passageAudit.unsupported),
|
||||
static_cast<unsigned long long>(passageAudit.gradeFailures),mouthsInContent?2:0,
|
||||
mouthsInRadius?2:0,passageAudit.passed?"PASS":"FAIL");
|
||||
for(std::size_t i=0;i<passageAudit.firstBlockedCount;++i)
|
||||
std::printf("[ResolvedPassageAuditSample] id=%s kind=blocked xyz_m=(%.3f,%.3f,%.3f) density=%.6f\n",
|
||||
passage.stableId.c_str(),passageAudit.firstBlocked[i][0],passageAudit.firstBlocked[i][1],
|
||||
passageAudit.firstBlocked[i][2],passageAudit.firstBlocked[i][3]);
|
||||
for(std::size_t i=0;i<passageAudit.firstUnsupportedCount;++i)
|
||||
std::printf("[ResolvedPassageAuditSample] id=%s kind=unsupported xyz_m=(%.3f,%.3f,%.3f) density=%.6f\n",
|
||||
passage.stableId.c_str(),passageAudit.firstUnsupported[i][0],
|
||||
passageAudit.firstUnsupported[i][1],passageAudit.firstUnsupported[i][2],
|
||||
passageAudit.firstUnsupported[i][3]);
|
||||
}
|
||||
std::uint64_t routePoints=0,routePasses=0,routeWalkPasses=0,routeBindings=0,routeBindingFailures=0;
|
||||
struct FRouteAuditInput
|
||||
{
|
||||
std::int32_t strateIndex=0;
|
||||
std::vector<std::array<double,3>> path;
|
||||
double supportStart=0.125,supportStep=0.125,supportMaximum=0.25,
|
||||
supportProbeRadius=0.45;
|
||||
std::uint32_t minimumSupportProbes=3;
|
||||
};
|
||||
std::vector<FRouteAuditInput> routeAuditInputs;
|
||||
for(const auto& stratum:ResolvedLayout->strates)
|
||||
{
|
||||
std::vector<const VoxelForgeCore::ResolvedLayoutAnchorV2*> anchors;
|
||||
for(const auto& anchor:stratum.anchors)
|
||||
if(anchor.kind=="route_point"&&anchor.values.size()>=3)anchors.push_back(&anchor);
|
||||
if(anchors.size()<2)continue;
|
||||
std::vector<std::array<double,3>> route;route.reserve(anchors.size());
|
||||
for(const auto* anchor:anchors)
|
||||
{
|
||||
route.push_back({qToMetres(anchor->values[0])/coreMetres,
|
||||
qToMetres(anchor->values[1])/coreMetres,
|
||||
qToMetres(anchor->values[2])/coreMetres});
|
||||
++routePoints;
|
||||
}
|
||||
double supportStartMetres=0.125,supportStepMetres=0.125,
|
||||
supportMaximumMetres=0.25,supportProbeRadiusMetres=0.45;
|
||||
std::uint32_t minimumSupportProbes=3;
|
||||
const VoxelForgeCore::WorldAssemblyRouteConstraintsV1* routeConstraints=nullptr;
|
||||
if(Assembly)
|
||||
{
|
||||
const auto worldRoute=std::find_if(Assembly->plan.strates.begin(),
|
||||
Assembly->plan.strates.end(),[&](const auto& candidate)
|
||||
{return candidate.strateIndex==stratum.strateIndex;});
|
||||
if(worldRoute!=Assembly->plan.strates.end()
|
||||
&&worldRoute->routeConstraints.version==1)
|
||||
routeConstraints=&worldRoute->routeConstraints;
|
||||
const auto islandRoute=std::find_if(Assembly->floatingIslands.begin(),
|
||||
Assembly->floatingIslands.end(),[&](const auto& candidate)
|
||||
{return candidate.strateIndex==stratum.strateIndex;});
|
||||
if(islandRoute!=Assembly->floatingIslands.end()
|
||||
&&islandRoute->manifest.recipe.routeConstraints.version==1)
|
||||
routeConstraints=&islandRoute->manifest.recipe.routeConstraints;
|
||||
}
|
||||
if(routeConstraints)
|
||||
{
|
||||
const double qMetres=coreMetres
|
||||
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
|
||||
minimumSupportProbes=std::clamp<std::uint32_t>(
|
||||
routeConstraints->minimumSupportProbes,1,5);
|
||||
supportProbeRadiusMetres=routeConstraints->supportProbeRadiusSubvoxels*qMetres;
|
||||
if(stratum.generatorKind=="FloatingIslands")
|
||||
{
|
||||
supportStartMetres=(routeConstraints->minimumClearanceSubvoxels
|
||||
+VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel/2)*qMetres;
|
||||
supportStepMetres=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel
|
||||
*0.5*qMetres;
|
||||
supportMaximumMetres=(routeConstraints->minimumClearanceSubvoxels
|
||||
+routeConstraints->supportSearchDepthSubvoxels)*qMetres;
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::int64_t spacing=std::min<std::int64_t>(
|
||||
routeConstraints->sweepSpacingSubvoxels,
|
||||
VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel/4);
|
||||
supportStepMetres=std::max<std::int64_t>(1,spacing)*qMetres;
|
||||
supportStartMetres=supportStepMetres;
|
||||
supportMaximumMetres=routeConstraints->supportSearchDepthSubvoxels*qMetres;
|
||||
}
|
||||
}
|
||||
const FWalkAudit routeAudit=auditPlayerPath(Shape,route,supportStartMetres,
|
||||
supportStepMetres,supportMaximumMetres,minimumSupportProbes,
|
||||
supportProbeRadiusMetres);
|
||||
routeAuditInputs.push_back({stratum.strateIndex,route,supportStartMetres,
|
||||
supportStepMetres,supportMaximumMetres,supportProbeRadiusMetres,
|
||||
minimumSupportProbes});
|
||||
bool binding=true;
|
||||
for(const auto& passage:ResolvedLayout->passages)
|
||||
{
|
||||
const auto equalPosition=[](const auto& a,const auto& b)
|
||||
{return a[0]==b[0]&&a[1]==b[1]&&a[2]==b[2];};
|
||||
if(passage.upperStrateIndex==stratum.strateIndex)
|
||||
{
|
||||
const std::array<std::int64_t,3> departure{{anchors.back()->values[0],
|
||||
anchors.back()->values[1],anchors.back()->values[2]}};
|
||||
binding&=equalPosition(passage.upperMouthSubvoxels,
|
||||
departure);
|
||||
}
|
||||
if(passage.lowerStrateIndex==stratum.strateIndex)
|
||||
{
|
||||
const std::array<std::int64_t,3> arrival{{anchors.front()->values[0],
|
||||
anchors.front()->values[1],anchors.front()->values[2]}};
|
||||
binding&=equalPosition(passage.lowerMouthSubvoxels,
|
||||
arrival);
|
||||
}
|
||||
}
|
||||
++routePasses;routeWalkPasses+=routeAudit.passed;
|
||||
routeBindings+=binding;routeBindingFailures+=!binding;
|
||||
std::printf("[ResolvedStrateRouteAudit] strate=%d route_points=%zu center_samples=%llu capsule_samples=%llu blocked=%llu support_samples=%llu unsupported=%llu grade_failures=%llu mouth_binding=%s status=%s\n",
|
||||
stratum.strateIndex,anchors.size(),static_cast<unsigned long long>(routeAudit.centers),
|
||||
static_cast<unsigned long long>(routeAudit.capsuleSamples),
|
||||
static_cast<unsigned long long>(routeAudit.blocked),
|
||||
static_cast<unsigned long long>(routeAudit.supportSamples),
|
||||
static_cast<unsigned long long>(routeAudit.unsupported),
|
||||
static_cast<unsigned long long>(routeAudit.gradeFailures),binding?"PASS":"FAIL",
|
||||
routeAudit.passed&&binding?"PASS":"FAIL");
|
||||
for(std::size_t i=0;i<routeAudit.firstBlockedCount;++i)
|
||||
std::printf("[ResolvedStrateRouteAuditSample] strate=%d kind=blocked xyz_m=(%.3f,%.3f,%.3f) density=%.6f\n",
|
||||
stratum.strateIndex,routeAudit.firstBlocked[i][0],routeAudit.firstBlocked[i][1],
|
||||
routeAudit.firstBlocked[i][2],routeAudit.firstBlocked[i][3]);
|
||||
for(std::size_t i=0;i<routeAudit.firstUnsupportedCount;++i)
|
||||
std::printf("[ResolvedStrateRouteAuditSample] strate=%d kind=unsupported xyz_m=(%.3f,%.3f,%.3f) density=%.6f\n",
|
||||
stratum.strateIndex,routeAudit.firstUnsupported[i][0],
|
||||
routeAudit.firstUnsupported[i][1],routeAudit.firstUnsupported[i][2],
|
||||
routeAudit.firstUnsupported[i][3]);
|
||||
}
|
||||
const auto controlSnapshot=CoreSnapshot.withoutResolvedLayoutPassages();
|
||||
VoxelForgeCore::World blockedWorld(controlSnapshot);
|
||||
std::string blockedCompileError;
|
||||
const bool blockedCompiled=controlSnapshot
|
||||
&&blockedWorld.compile(ShapeRequest,Scheduler,blockedCompileError);
|
||||
if(blockedCompiled)
|
||||
for(const auto& passage:ResolvedLayout->passages)
|
||||
{
|
||||
std::vector<std::array<double,3>> path;path.reserve(passage.controlPoints.size());
|
||||
for(const auto& point:passage.controlPoints)
|
||||
{
|
||||
const double playerZ=static_cast<double>(point.positionSubvoxels[2]
|
||||
-point.radiusSubvoxels)+1.13*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel
|
||||
*1000000.0/World.CoreVoxelSizeMicrons;
|
||||
path.push_back({qToMetres(point.positionSubvoxels[0])/coreMetres,
|
||||
qToMetres(point.positionSubvoxels[1])/coreMetres,playerZ/256.0});
|
||||
}
|
||||
const FWalkAudit blockedAudit=auditPlayerPath(blockedWorld,path,
|
||||
0.125,0.125,0.25,3,0.45);
|
||||
blockedControls+=blockedAudit.blocked>0&&!blockedAudit.passed;
|
||||
std::printf("[ResolvedPassageBlockedControl] id=%s blocked_capsule_samples=%llu detected=%s\n",
|
||||
passage.stableId.c_str(),static_cast<unsigned long long>(blockedAudit.blocked),
|
||||
blockedAudit.blocked>0&&!blockedAudit.passed?"true":"false");
|
||||
}
|
||||
else
|
||||
std::fprintf(stderr,"[ResolvedPassageBlockedControl] compile_failed=%s\n",blockedCompileError.c_str());
|
||||
if(blockedCompiled)
|
||||
for(const auto& input:routeAuditInputs)
|
||||
{
|
||||
const FWalkAudit controlRoute=auditPlayerPath(blockedWorld,input.path,
|
||||
input.supportStart,input.supportStep,input.supportMaximum,
|
||||
input.minimumSupportProbes,input.supportProbeRadius);
|
||||
std::printf("[ResolvedStrateRouteBlockedControl] strate=%d blocked=%llu unsupported=%llu status=%s\n",
|
||||
input.strateIndex,static_cast<unsigned long long>(controlRoute.blocked),
|
||||
static_cast<unsigned long long>(controlRoute.unsupported),
|
||||
controlRoute.passed?"PASS":"FAIL");
|
||||
}
|
||||
const bool auditPassed=passageCount>0&&passagePasses==passageCount
|
||||
&&routePasses>0&&routePasses==routeWalkPasses&&routePasses==routeBindings
|
||||
&&routeBindingFailures==0
|
||||
&&blockedCompiled&&blockedControls==passageCount;
|
||||
std::printf("[ResolvedPassageAuditSummary] interfaces=%llu passed=%llu strate_routes=%llu route_walkable=%llu route_points=%llu route_bindings=%llu route_binding_failures=%llu blocked_controls=%llu/%llu status=%s\n",
|
||||
static_cast<unsigned long long>(passageCount),static_cast<unsigned long long>(passagePasses),
|
||||
static_cast<unsigned long long>(routePasses),static_cast<unsigned long long>(routeWalkPasses),
|
||||
static_cast<unsigned long long>(routePoints),
|
||||
static_cast<unsigned long long>(routeBindings),static_cast<unsigned long long>(routeBindingFailures),
|
||||
static_cast<unsigned long long>(blockedControls),static_cast<unsigned long long>(passageCount),
|
||||
auditPassed?"PASS":"FAIL");
|
||||
if(!auditPassed)
|
||||
{Streamer.Stop();return 10;}
|
||||
}
|
||||
if(bAuditResolvedLayoutBoundaries)
|
||||
{
|
||||
if(ResolvedLayout==nullptr)
|
||||
|
||||
@@ -43,7 +43,9 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
|
||||
inline bool BuildFiniteStrateRoute(VoxelForgeCore::WorldAssemblyRecipeV1& recipe,
|
||||
VoxelForgeCore::WorldAssemblyStratumPlanV1& stratum,std::uint64_t& tested,
|
||||
std::uint64_t& gradeRejected,std::uint64_t& sweepRejected,std::string& error)
|
||||
std::uint64_t& gradeRejected,std::uint64_t& placementRejected,
|
||||
std::uint64_t& sweepRejected,std::int64_t arrivalRadiusSubvoxels,
|
||||
std::int64_t departureRadiusSubvoxels,std::string& error)
|
||||
{
|
||||
using namespace VoxelForgeCore;
|
||||
constexpr std::int64_t PlayerRadiusQ=348; // 0.34 m in Core's 0.25 m voxel scale.
|
||||
@@ -73,7 +75,9 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
constraints.sweepSpacingSubvoxels=StepQ;
|
||||
constraints.supportProbeRadiusSubvoxels=ProbeRadiusQ;
|
||||
constraints.supportSearchDepthSubvoxels=SupportDepthQ;
|
||||
constraints.minimumClearanceSubvoxels=8;
|
||||
// Match the FI route contract's half-voxel body clearance. Core's native
|
||||
// field can differ slightly from the recipe sweep at the lower capsule cap.
|
||||
constraints.minimumClearanceSubvoxels=128;
|
||||
constraints.minimumSupportProbes=3;
|
||||
|
||||
const Heightfield2D finalField(recipe,stratum);
|
||||
@@ -90,7 +94,7 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
candidate.centerX=centerX;candidate.centerY=centerY;candidate.direction=directionIndex;
|
||||
candidate.points.reserve(33);
|
||||
std::int64_t previousGround=0,previousX=0,previousY=0;
|
||||
bool gradePass=true;
|
||||
bool gradePass=true,placementPass=true;
|
||||
for(std::int32_t index=0;index<=32;++index)
|
||||
{
|
||||
const std::int64_t along=(index-16)*waypointSpacing;
|
||||
@@ -98,6 +102,13 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
static_cast<double>(along)*directions[directionIndex].x/65536.0);
|
||||
const std::int64_t y=centerY+std::llround(
|
||||
static_cast<double>(along)*directions[directionIndex].y/65536.0);
|
||||
const std::int64_t xQ=WorldAssemblyFixed(static_cast<double>(x));
|
||||
const std::int64_t yQ=WorldAssemblyFixed(static_cast<double>(y));
|
||||
const auto inRadius=[](std::int64_t xValue,std::int64_t yValue,std::int64_t radius)
|
||||
{return radius<=0||xValue*xValue+yValue*yValue<=radius*radius;};
|
||||
if((index==0&&!inRadius(xQ,yQ,arrivalRadiusSubvoxels))
|
||||
||(index==32&&!inRadius(xQ,yQ,departureRadiusSubvoxels)))
|
||||
{placementPass=false;break;}
|
||||
const auto column=finalField.point(WorldAssemblyFixed(static_cast<double>(x)),
|
||||
WorldAssemblyFixed(static_cast<double>(y)),0);
|
||||
const std::int64_t horizontalQ=static_cast<std::int64_t>(std::llround(
|
||||
@@ -121,7 +132,8 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
previousGround=column.groundHeightSubvoxels;previousX=x;previousY=y;
|
||||
}
|
||||
++tested;
|
||||
if(gradePass)candidates.push_back(std::move(candidate));
|
||||
if(placementPass&&gradePass)candidates.push_back(std::move(candidate));
|
||||
else if(!placementPass)++placementRejected;
|
||||
else ++gradeRejected;
|
||||
}
|
||||
std::stable_sort(candidates.begin(),candidates.end(),[](const Candidate& a,const Candidate& b)
|
||||
@@ -382,14 +394,33 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
stratum.features.push_back(shelf);
|
||||
}
|
||||
}
|
||||
std::uint64_t routeCandidates=0,routeGradeRejected=0,routeSweepRejected=0;
|
||||
std::int64_t arrivalRadiusQ=0,departureRadiusQ=0;
|
||||
const auto ownerVoxelsToCoreQ=[&](double value)
|
||||
{return static_cast<std::int64_t>(std::llround(value*owner.OwnerVoxelSizeMicrons
|
||||
*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel/owner.CoreVoxelSizeMicrons));};
|
||||
for(const FStrateSlot& slot:owner.Manager->GetLayout())
|
||||
{
|
||||
if(!slot.Definition)continue;
|
||||
if(slot.StrateIndex+1==stratum.strateIndex)
|
||||
arrivalRadiusQ=std::max(ownerVoxelsToCoreQ(slot.Definition->PassageConfig.DistanceMin),
|
||||
ownerVoxelsToCoreQ(slot.Definition->PassageConfig.DistanceMax));
|
||||
if(slot.StrateIndex==stratum.strateIndex+1&&definition)
|
||||
departureRadiusQ=std::max(ownerVoxelsToCoreQ(definition->PassageConfig.DistanceMin),
|
||||
ownerVoxelsToCoreQ(definition->PassageConfig.DistanceMax));
|
||||
}
|
||||
std::uint64_t routeCandidates=0,routeGradeRejected=0,routePlacementRejected=0,routeSweepRejected=0;
|
||||
if(!BuildFiniteStrateRoute(recipe,stratum,routeCandidates,routeGradeRejected,
|
||||
routeSweepRejected,error))return false;
|
||||
std::printf("[WorldAssemblerRoutePlan] candidates=%llu grade_rejected=%llu sweep_rejected=%llu accepted_points=%zu capsule_m=0.34x1.76 corridor_radius_vox=4 support_probe_points=5 minimum_support_probes=%u status=PASS\n",
|
||||
routePlacementRejected,routeSweepRejected,arrivalRadiusQ,departureRadiusQ,error))return false;
|
||||
std::printf("[WorldAssemblerRoutePlan] candidates=%llu grade_rejected=%llu placement_rejected=%llu sweep_rejected=%llu accepted_points=%zu capsule_m=0.34x1.76 corridor_radius_vox=4 support_probe_points=5 minimum_support_probes=%u arrival_radius_m=%.3f departure_radius_m=%.3f status=PASS\n",
|
||||
static_cast<unsigned long long>(routeCandidates),
|
||||
static_cast<unsigned long long>(routeGradeRejected),
|
||||
static_cast<unsigned long long>(routePlacementRejected),
|
||||
static_cast<unsigned long long>(routeSweepRejected),stratum.route.size(),
|
||||
stratum.routeConstraints.minimumSupportProbes);
|
||||
stratum.routeConstraints.minimumSupportProbes,
|
||||
static_cast<double>(arrivalRadiusQ)*owner.CoreVoxelSizeMicrons
|
||||
/(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel*1000000.0),
|
||||
static_cast<double>(departureRadiusQ)*owner.CoreVoxelSizeMicrons
|
||||
/(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel*1000000.0));
|
||||
manifest.plan.strates.push_back(std::move(stratum));
|
||||
if(!manifest.finalize(error))return false;
|
||||
if(outManifest)*outManifest=manifest;
|
||||
@@ -537,6 +568,18 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
|
||||
auto planned=VoxelForgeCore::PlanFloatingIslandCluster(recipe,error);
|
||||
if(!planned)return false;
|
||||
const VoxelForgeCore::FloatingIslandFieldV1 routeField(
|
||||
planned->recipe,planned->plan);
|
||||
const auto routeAudit=VoxelForgeCore::AuditFloatingIslandRoute(routeField);
|
||||
std::printf("[FloatingIslandRouteAudit] points=%zu centers=%llu capsule_samples=%llu blocked=%llu support_samples=%llu unsupported=%llu grade_failures=%llu status=%s\n",
|
||||
planned->plan.route.size(),
|
||||
static_cast<unsigned long long>(routeAudit.centerSamples),
|
||||
static_cast<unsigned long long>(routeAudit.capsuleFieldSamples),
|
||||
static_cast<unsigned long long>(routeAudit.blockedCapsuleSamples),
|
||||
static_cast<unsigned long long>(routeAudit.supportProbeSamples),
|
||||
static_cast<unsigned long long>(routeAudit.unsupportedCenterSamples),
|
||||
static_cast<unsigned long long>(routeAudit.gradeFailures),
|
||||
routeAudit.passed?"PASS":"FAIL");
|
||||
const double metresPerSubvoxel=static_cast<double>(owner.CoreVoxelSizeMicrons)
|
||||
/(1000000.0*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
const double bedrockTopMetres=(minimumZ+recipe.boundarySealSubvoxels)*metresPerSubvoxel;
|
||||
@@ -598,7 +641,7 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
|
||||
inline bool BuildResolvedLayoutSnapshot(const FStandaloneOwnerWorld& owner,
|
||||
const VoxelForgeCore::WorldAssemblyManifest& assembly,std::string_view recipeVariant,
|
||||
VoxelForgeCore::ResolvedLayoutSnapshotV2& out,std::string& error)
|
||||
VoxelForgeCore::ResolvedLayoutSnapshotV3& out,std::string& error)
|
||||
{
|
||||
using namespace VoxelForgeCore;
|
||||
if(!owner.Manager){error="resolved layout builder requires the initialized director";return false;}
|
||||
@@ -661,6 +704,15 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
anchor.values={a.centerSubvoxels[0],a.centerSubvoxels[1],a.centerSubvoxels[2],a.radiusSubvoxels,a.topHalfSubvoxels};
|
||||
s.anchors.push_back(std::move(anchor));
|
||||
}
|
||||
for(std::size_t n=0;n<it->manifest.plan.route.size();++n)
|
||||
{
|
||||
const auto& p=it->manifest.plan.route[n];ResolvedLayoutAnchorV2 anchor;
|
||||
anchor.kind="route_point";anchor.stableId="route/"+std::to_string(n);
|
||||
anchor.units="1/256 Core voxel";
|
||||
anchor.values={p.positionSubvoxels[0],p.positionSubvoxels[1],
|
||||
p.positionSubvoxels[2],p.radiusSubvoxels};
|
||||
s.anchors.push_back(std::move(anchor));
|
||||
}
|
||||
}
|
||||
}
|
||||
s.interiorBottomMicrometres=bottom;s.interiorTopMicrometres=top;
|
||||
@@ -678,8 +730,8 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
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)";
|
||||
s.boundDispatchPath="all generator plans use ResolvedLayoutSnapshotV3 layoutChunkWidthSubvoxels";
|
||||
s.queryDispatchPath="all generator dispatch uses floor(CoreZ / ResolvedLayoutSnapshotV3 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};
|
||||
@@ -701,18 +753,28 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
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));}
|
||||
}
|
||||
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;
|
||||
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));
|
||||
}
|
||||
std::vector<ResolvedLayoutPassageConfigV1> 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
|
||||
*WorldAssemblySubvoxelsPerVoxel/owner.CoreVoxelSizeMicrons));};
|
||||
ResolvedLayoutPassageConfigV1 config;config.upperStrateIndex=slot.StrateIndex;
|
||||
config.connections=authored.Connections;
|
||||
config.mouthRadiusSubvoxels=ownerVoxelsToCoreQ(authored.MouthRadius);
|
||||
config.midRadiusSubvoxels=ownerVoxelsToCoreQ(authored.MidRadius);
|
||||
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);
|
||||
passageConfigs.push_back(config);
|
||||
}
|
||||
if(!PlanResolvedLayoutPassagesV1(out,owner.Seed,passageConfigs,error))return false;
|
||||
return out.finalize(error);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user