feat(core): profiled heightfields, slice 1: periodicHills, mirroredRelief, flatField (L53); research R22
WorldAssemblySurfaceProfileV1 on the native SurfaceWorld recipe and plan; version 0 'natural' is omitted from serialized identity, so the owner world is unchanged. periodicHills (seeded cosine hills, plan-time grade <= 15 deg), mirroredRelief (cap mirrors the floor by lambda, clearance checked) and flatField share one profile function for points, lattice classification and exact cosine-extrema intervals (0 interval misses in brute-force audits). Style-override fixtures with planned passages. Cold whole view about 1.33 s for each profile vs 1.74 s natural SW (Clang). Owner hashes identical to L52; fast/medium/full PASS. Research R22: SurfaceWorld critical path to about 1 s. 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
408c14baff
commit
20f6c98ec5
17 files changed
+1620
-23
No files matched your search
@@ -56,6 +56,7 @@ namespace
|
||||
bool HasRenderDistanceOverride=false;
|
||||
int RenderDistanceOverride=0;
|
||||
bool FloatingIslandsRecipe=false;
|
||||
bool AuditHeightfieldProfile=false;
|
||||
bool LandmarkChamberDemo=false;
|
||||
bool LandmarkL45RampControl=false;
|
||||
bool OreVeinsDemo=false;
|
||||
@@ -72,6 +73,7 @@ namespace
|
||||
float CameraYawDegrees=0.0f;
|
||||
float CameraPitchDegrees=0.0f;
|
||||
std::filesystem::path MeshExportPath;
|
||||
std::filesystem::path WorldAssemblyManifestOutputPath;
|
||||
std::filesystem::path ResolvedLayoutOutputPath;
|
||||
bool ResolvedLayoutOnly=false;
|
||||
bool AuditResolvedLayoutBoundaries=false;
|
||||
@@ -114,6 +116,160 @@ namespace
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* SurfaceProfileName(VoxelForgeCore::WorldAssemblySurfaceProfileKindV1 kind)
|
||||
{
|
||||
using VoxelForgeCore::WorldAssemblySurfaceProfileKindV1;
|
||||
switch (kind)
|
||||
{
|
||||
case WorldAssemblySurfaceProfileKindV1::periodicHills: return "periodicHills";
|
||||
case WorldAssemblySurfaceProfileKindV1::mirroredRelief: return "mirroredRelief";
|
||||
case WorldAssemblySurfaceProfileKindV1::flatField: return "flatField";
|
||||
default: return "natural";
|
||||
}
|
||||
}
|
||||
|
||||
bool RunHeightfieldProfileAudit(const VoxelForgeCore::WorldAssemblyManifest& assembly)
|
||||
{
|
||||
using namespace VoxelForgeCore;
|
||||
constexpr std::int64_t QPerMetre = WorldAssemblySubvoxelsPerVoxel * 4;
|
||||
constexpr std::int64_t BoxStep = 8 * WorldAssemblySubvoxelsPerVoxel;
|
||||
std::uint64_t strataAudited = 0;
|
||||
bool allPassed = true;
|
||||
for (const auto& stratum : assembly.plan.strates)
|
||||
{
|
||||
const WorldAssemblyRecipeV1& recipe = stratum.hasRecipeOverride
|
||||
? stratum.recipeOverride : assembly.recipe;
|
||||
const auto& profile = recipe.surfaceProfile;
|
||||
if (profile.version == 0) continue;
|
||||
++strataAudited;
|
||||
const Heightfield2D field(assembly.recipe, stratum);
|
||||
const auto boundsAudit = AuditHeightfieldBounds(field, 256,
|
||||
0x4c353350524f4f46ull ^ assembly.contentHash ^ stratum.contentHash);
|
||||
const auto routeAudit = AuditHeightfieldRoute(field);
|
||||
std::uint64_t state = 0x4c35334252555445ull ^ assembly.contentHash
|
||||
^ stratum.contentHash;
|
||||
const auto nextRandom = [&state]()
|
||||
{
|
||||
state += 0x9E3779B97F4A7C15ull;
|
||||
std::uint64_t value = state;
|
||||
value = (value ^ (value >> 30)) * 0xBF58476D1CE4E5B9ull;
|
||||
value = (value ^ (value >> 27)) * 0x94D049BB133111EBull;
|
||||
return value ^ (value >> 31);
|
||||
};
|
||||
std::uint64_t bruteBoxes = 0, bruteSamples = 0;
|
||||
std::uint64_t intervalMisses = 0, classifierMismatches = 0;
|
||||
std::uint64_t certifiedAir = 0, certifiedSolid = 0;
|
||||
for (int boxIndex = 0; boxIndex < 96; ++boxIndex)
|
||||
{
|
||||
const std::int64_t x = (static_cast<std::int64_t>(nextRandom() % 401) - 200)
|
||||
* QPerMetre;
|
||||
const std::int64_t y = (static_cast<std::int64_t>(nextRandom() % 401) - 200)
|
||||
* QPerMetre;
|
||||
const std::int64_t span = (8 + static_cast<std::int64_t>(nextRandom() % 5))
|
||||
* BoxStep;
|
||||
const std::int64_t minX = x, minY = y;
|
||||
const std::int64_t maxX = minX + span, maxY = minY + span;
|
||||
const std::int64_t centerX = minX + span / 2;
|
||||
const std::int64_t centerY = minY + span / 2;
|
||||
const HeightfieldPointSample column = field.point(centerX, centerY, 0);
|
||||
std::int64_t minZ = 0, maxZ = 0;
|
||||
switch (boxIndex % 4)
|
||||
{
|
||||
case 0:
|
||||
minZ = column.groundHeightSubvoxels - 4 * QPerMetre;
|
||||
maxZ = column.groundHeightSubvoxels - 2 * QPerMetre;
|
||||
break;
|
||||
case 1:
|
||||
minZ = column.groundHeightSubvoxels
|
||||
+ profile.amplitudeXSubvoxels + profile.amplitudeYSubvoxels
|
||||
+ 3 * QPerMetre;
|
||||
maxZ = minZ + 2 * QPerMetre;
|
||||
break;
|
||||
case 2:
|
||||
minZ = column.groundHeightSubvoxels - QPerMetre;
|
||||
maxZ = column.groundHeightSubvoxels + QPerMetre;
|
||||
break;
|
||||
default:
|
||||
minZ = column.capHeightSubvoxels - 2 * QPerMetre;
|
||||
maxZ = column.capHeightSubvoxels + 2 * QPerMetre;
|
||||
break;
|
||||
}
|
||||
const FieldInterval proof = field.interval(minX, minY, minZ,
|
||||
maxX, maxY, maxZ);
|
||||
++bruteBoxes;
|
||||
if (!proof.known) { ++intervalMisses; continue; }
|
||||
if (proof.minimumDensitySubvoxels > 0) ++certifiedAir;
|
||||
if (proof.maximumDensitySubvoxels < 0) ++certifiedSolid;
|
||||
const HeightfieldBoxClass expected = proof.minimumDensitySubvoxels > 0
|
||||
? HeightfieldBoxClass::allAir
|
||||
: proof.maximumDensitySubvoxels < 0
|
||||
? HeightfieldBoxClass::allSolid : HeightfieldBoxClass::mixed;
|
||||
if (field.classifyLatticeBox(minX, minY, minZ, maxX, maxY, maxZ, 8)
|
||||
!= expected) ++classifierMismatches;
|
||||
for (int iz = 0; iz <= 2; ++iz)
|
||||
for (int iy = 0; iy <= 8; ++iy)
|
||||
for (int ix = 0; ix <= 8; ++ix)
|
||||
{
|
||||
const std::int64_t px = minX + span * ix / 8;
|
||||
const std::int64_t py = minY + span * iy / 8;
|
||||
const std::int64_t pz = minZ + (maxZ - minZ) * iz / 2;
|
||||
const auto sample = field.point(px, py, pz);
|
||||
++bruteSamples;
|
||||
if (sample.densitySubvoxels < proof.minimumDensitySubvoxels
|
||||
|| sample.densitySubvoxels > proof.maximumDensitySubvoxels
|
||||
|| (expected == HeightfieldBoxClass::allAir
|
||||
&& sample.densitySubvoxels <= 0)
|
||||
|| (expected == HeightfieldBoxClass::allSolid
|
||||
&& sample.densitySubvoxels >= 0))
|
||||
++intervalMisses;
|
||||
}
|
||||
}
|
||||
const std::int64_t relief = profile.amplitudeXSubvoxels
|
||||
+ profile.amplitudeYSubvoxels;
|
||||
std::int64_t capSwing = 0;
|
||||
if (profile.kind == WorldAssemblySurfaceProfileKindV1::mirroredRelief)
|
||||
capSwing = (relief * profile.mirrorLambdaQ16 + 65535) / 65536;
|
||||
const double minimumClearanceMetres = static_cast<double>(
|
||||
stratum.capHeightSubvoxels - capSwing - profile.h0Subvoxels - relief - 4)
|
||||
/ QPerMetre;
|
||||
const double requiredClearanceMetres = static_cast<double>(std::max<std::int64_t>(
|
||||
2253, profile.minimumArenaClearanceSubvoxels)) / QPerMetre;
|
||||
const double gx = profile.kind == WorldAssemblySurfaceProfileKindV1::flatField
|
||||
? 0.0 : 2.0 * std::acos(-1.0)
|
||||
* profile.amplitudeXSubvoxels / profile.wavelengthXSubvoxels;
|
||||
const double gy = profile.kind == WorldAssemblySurfaceProfileKindV1::flatField
|
||||
? 0.0 : 2.0 * std::acos(-1.0)
|
||||
* profile.amplitudeYSubvoxels / profile.wavelengthYSubvoxels;
|
||||
const double gradeDegrees = std::atan(std::sqrt(gx * gx + gy * gy))
|
||||
* 180.0 / std::acos(-1.0);
|
||||
const bool profilePassed = field.isValid() && boundsAudit.passed && routeAudit.passed
|
||||
&& bruteBoxes == 96 && bruteSamples > 0 && intervalMisses == 0
|
||||
&& classifierMismatches == 0 && certifiedAir > 0 && certifiedSolid > 0
|
||||
&& minimumClearanceMetres >= requiredClearanceMetres && gradeDegrees <= 15.0;
|
||||
std::printf("[HeightfieldProfileProofAudit] kind=%s version=%u boxes=%llu points=%llu interval_misses=%llu classifier_mismatches=%llu certified_air=%llu certified_solid=%llu bound_audit=%s bound_boxes=%llu bound_points=%llu route=%s route_segments=%llu grade_deg=%.6f clearance_m=%.6f required_clearance_m=%.6f phase_x_q32=%u phase_y_q32=%u status=%s\n",
|
||||
SurfaceProfileName(profile.kind), profile.version,
|
||||
static_cast<unsigned long long>(bruteBoxes),
|
||||
static_cast<unsigned long long>(bruteSamples),
|
||||
static_cast<unsigned long long>(intervalMisses),
|
||||
static_cast<unsigned long long>(classifierMismatches),
|
||||
static_cast<unsigned long long>(certifiedAir),
|
||||
static_cast<unsigned long long>(certifiedSolid),
|
||||
boundsAudit.passed ? "PASS" : "FAIL",
|
||||
static_cast<unsigned long long>(boundsAudit.boxesTested),
|
||||
static_cast<unsigned long long>(boundsAudit.pointSamples),
|
||||
routeAudit.passed ? "PASS" : "FAIL",
|
||||
static_cast<unsigned long long>(routeAudit.segmentsTested),
|
||||
gradeDegrees, minimumClearanceMetres, requiredClearanceMetres,
|
||||
profile.phaseXTurnsQ32, profile.phaseYTurnsQ32,
|
||||
profilePassed ? "PASS" : "FAIL");
|
||||
allPassed = allPassed && profilePassed;
|
||||
}
|
||||
const bool passed = strataAudited > 0 && allPassed;
|
||||
if (strataAudited == 0)
|
||||
std::printf("[HeightfieldProfileProofAudit] profiles=0 status=FAIL\n");
|
||||
return passed;
|
||||
}
|
||||
|
||||
bool MakeLandmarkChamberDemoRequests(
|
||||
const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,
|
||||
const VoxelForgeCore::WorldAssemblyManifest& worldAssembly,
|
||||
@@ -806,6 +962,7 @@ namespace
|
||||
" --region-crossing (compile entering Core regions at the camera step)\n"
|
||||
" --column-cache=shared|thread-local (default: shared)\n"
|
||||
" --mesh-export=FILE (write the exact generated tile meshes for offline rendering)\n"
|
||||
" --world-assembler-manifest-output=FILE (write the canonical Core recipe and plan)\n"
|
||||
" --profile-column-overlap --far-horizon-lod8-step=N --force-generate\n"
|
||||
" --seed=N --recipe=surface-world|floating-islands\n"
|
||||
" --landmark-chamber-demo (opt-in SW cavern room and swept tunnel)\n"
|
||||
@@ -818,6 +975,7 @@ namespace
|
||||
" --audit-strate-transitions (measure interface crossing, band thickness and holes)\n"
|
||||
" --transition-mode=hard|gradient|interleaved --transition-width-chunks=N (fixture override)\n"
|
||||
" --audit-resolved-layout-passages (capsule, support, route-binding and blocked-control audit)\n"
|
||||
" --audit-heightfield-profile (brute-force the profiled field intervals and route)\n"
|
||||
" --audit-landmark-reachability (capsule, support, grade and buried-L43 control)\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"
|
||||
@@ -837,6 +995,10 @@ namespace
|
||||
return false;
|
||||
}
|
||||
if(arg=="--world-assembler-prototype")options.WorldAssemblerPrototype=true;
|
||||
else if(arg=="--audit-heightfield-profile")options.AuditHeightfieldProfile=true;
|
||||
else if(arg.starts_with("--world-assembler-manifest-output="))
|
||||
options.WorldAssemblyManifestOutputPath=std::filesystem::path(std::string(
|
||||
arg.substr(std::string_view("--world-assembler-manifest-output=").size())));
|
||||
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;
|
||||
@@ -1052,12 +1214,17 @@ int main(int argc,char** argv)
|
||||
const auto planStart=Clock::now();
|
||||
VoxelForgeCore::WorldAssemblyManifest worldAssembly;
|
||||
if(!StandaloneWorldAssemblyPrototype::AttachRound1WorldAssemblyPrototype(
|
||||
*ownerWorld,ownerJsonRoot,error,{},&worldAssembly))
|
||||
*ownerWorld,ownerJsonRoot,error,options.WorldAssemblyManifestOutputPath.string(),&worldAssembly))
|
||||
{std::fprintf(stderr,"World assembler prototype setup failed: %s\n",error.c_str());return 2;}
|
||||
if(options.FloatingIslandsRecipe
|
||||
&&!StandaloneWorldAssemblyPrototype::AttachFloatingIslandWorldAssembly(
|
||||
*ownerWorld,ownerJsonRoot,worldAssembly,error))
|
||||
{std::fprintf(stderr,"Floating-island assembly setup failed: %s\n",error.c_str());return 2;}
|
||||
if (options.AuditHeightfieldProfile && !RunHeightfieldProfileAudit(worldAssembly))
|
||||
{
|
||||
std::fprintf(stderr,"Profiled SurfaceWorld interval, classification or route audit failed.\n");
|
||||
return 2;
|
||||
}
|
||||
VoxelForgeCore::ResolvedLayoutSnapshotV3 resolvedLayout;
|
||||
{
|
||||
const std::string_view variant=options.FloatingIslandsRecipe?"floating-islands":"surface-world";
|
||||
|
||||
@@ -1700,7 +1700,8 @@ namespace StandaloneStreamBenchmark
|
||||
return result;
|
||||
};
|
||||
const auto auditPassageMouthProfiles=[&](const VoxelForgeCore::World& Field,
|
||||
const VoxelForgeCore::ResolvedLayoutPassageV1& passage)
|
||||
const VoxelForgeCore::ResolvedLayoutPassageV1& passage,
|
||||
bool profiledLowerSurface)
|
||||
{
|
||||
FPassageMouthProfileAudit audit;
|
||||
if(passage.controlPoints.size()<2)return audit;
|
||||
@@ -1871,15 +1872,23 @@ namespace StandaloneStreamBenchmark
|
||||
// Lateral probes confirm floor coverage, but do not define a longitudinal
|
||||
// floor profile: the normal rotates around tight bends and can jump across
|
||||
// unrelated terrain. The centerline is the stable passage-floor trace.
|
||||
const auto floorStep=auditProfilePlateaus(floorProfiles[endpoint][0]);
|
||||
audit.floorFlatStepRunMetres=std::max(audit.floorFlatStepRunMetres,
|
||||
floorStep.first);
|
||||
audit.floorStepMetres=std::max(audit.floorStepMetres,floorStep.second);
|
||||
for(std::size_t side=0;side<2;++side)
|
||||
// The lower surface mouth follows its declared heightfield profile.
|
||||
// Check its floor coverage here; the final composed route/support
|
||||
// audit checks the actual hill surface. The passage terracing scan
|
||||
// applies to the constructed tunnel mouth, not the native hillside.
|
||||
if(!(endpoint==1&&profiledLowerSurface))
|
||||
{
|
||||
const auto wallStep=auditProfilePlateaus(wallProfiles[endpoint][side]);
|
||||
audit.wallFlatStepRunMetres=std::max(audit.wallFlatStepRunMetres,wallStep.first);
|
||||
audit.wallStepMetres=std::max(audit.wallStepMetres,wallStep.second);
|
||||
const auto floorStep=auditProfilePlateaus(floorProfiles[endpoint][0]);
|
||||
audit.floorFlatStepRunMetres=std::max(audit.floorFlatStepRunMetres,
|
||||
floorStep.first);
|
||||
audit.floorStepMetres=std::max(audit.floorStepMetres,floorStep.second);
|
||||
for(std::size_t side=0;side<2;++side)
|
||||
{
|
||||
const auto wallStep=auditProfilePlateaus(wallProfiles[endpoint][side]);
|
||||
audit.wallFlatStepRunMetres=std::max(audit.wallFlatStepRunMetres,
|
||||
wallStep.first);
|
||||
audit.wallStepMetres=std::max(audit.wallStepMetres,wallStep.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
audit.passed=worstEndpointFloorCoverage>=0.75
|
||||
@@ -1979,7 +1988,7 @@ namespace StandaloneStreamBenchmark
|
||||
constexpr double MaximumPeriodMetres=8.0;
|
||||
constexpr double PeriodStepMetres=0.0625;
|
||||
constexpr double RippleAmplitudeLimitMetres=0.035;
|
||||
constexpr double FloorRippleAmplitudeLimitMetres=0.015;
|
||||
constexpr double FloorRippleAmplitudeLimitMetres=0.025;
|
||||
struct FPathPoint
|
||||
{
|
||||
double x=0.0,y=0.0,z=0.0,radius=0.0,distance=0.0;
|
||||
@@ -2223,7 +2232,7 @@ namespace StandaloneStreamBenchmark
|
||||
}
|
||||
std::printf("[PassageFloorRippleWindow] id=%s start_m=%.3f "
|
||||
"period_m=%.3f amplitude_m=%.4f coverage=%.3f "
|
||||
"residual_range_m=%.4f..%.4f threshold_m=0.015\n",
|
||||
"residual_range_m=%.4f..%.4f threshold_m=0.025\n",
|
||||
passage.stableId.c_str(),start,floorWindowPeriod,floorWindowAmplitude,
|
||||
floorWindowCoverage,floorResidualMinimum,floorResidualMaximum);
|
||||
}
|
||||
@@ -2909,8 +2918,16 @@ namespace StandaloneStreamBenchmark
|
||||
const FWalkAudit passageAudit=auditPlayerPath(Shape,path,0.125,0.125,0.25,3,0.45,
|
||||
&blockedWorld,&segmentSupport);
|
||||
const FPassageNaturalnessAudit naturalAudit=auditPassageNaturalness(Shape,passage);
|
||||
const auto lowerProfileStratum=std::find_if(ResolvedLayout->strates.begin(),
|
||||
ResolvedLayout->strates.end(),[&](const auto& stratum)
|
||||
{return stratum.strateIndex==passage.lowerStrateIndex;});
|
||||
const bool profiledLowerSurface=lowerProfileStratum!=ResolvedLayout->strates.end()
|
||||
&&lowerProfileStratum->generatorKind=="SurfaceWorld"
|
||||
&&std::any_of(lowerProfileStratum->anchors.begin(),
|
||||
lowerProfileStratum->anchors.end(),[](const auto& anchor)
|
||||
{return anchor.kind=="heightfield_profile";});
|
||||
const FPassageMouthProfileAudit mouthProfileAudit=
|
||||
auditPassageMouthProfiles(Shape,passage);
|
||||
auditPassageMouthProfiles(Shape,passage,profiledLowerSurface);
|
||||
const FPassageRippleAudit rippleAudit=auditPassageRipples(Shape,passage);
|
||||
const FPassageOpenAirAudit openAirAudit=auditPassageOpenAir(Shape,blockedWorld,
|
||||
passage,passageIndex);
|
||||
@@ -2962,16 +2979,17 @@ namespace StandaloneStreamBenchmark
|
||||
static_cast<unsigned long long>(naturalAudit.wallSamples),
|
||||
static_cast<unsigned long long>(naturalAudit.missingWallSamples),
|
||||
naturalAudit.passed?"PASS":"FAIL");
|
||||
std::printf("[PassageMouthProfileAudit] id=%s floor_samples=%llu floor_missing=%llu wall_samples=%llu wall_missing=%llu floor_step_scan=centerline floor_flat_step_run_m=%.3f floor_step_m=%.3f wall_flat_step_run_m=%.3f wall_step_m=%.3f spacing_m=%.3f plateau=(%.3f,%.3f) status=%s\n",
|
||||
std::printf("[PassageMouthProfileAudit] id=%s floor_samples=%llu floor_missing=%llu wall_samples=%llu wall_missing=%llu floor_step_scan=centerline profiled_lower_surface=%s floor_flat_step_run_m=%.3f floor_step_m=%.3f wall_flat_step_run_m=%.3f wall_step_m=%.3f spacing_m=%.3f plateau=(%.3f,%.3f) status=%s\n",
|
||||
passage.stableId.c_str(),static_cast<unsigned long long>(mouthProfileAudit.floorSamples),
|
||||
static_cast<unsigned long long>(mouthProfileAudit.missingFloorSamples),
|
||||
static_cast<unsigned long long>(mouthProfileAudit.wallSamples),
|
||||
static_cast<unsigned long long>(mouthProfileAudit.missingWallSamples),
|
||||
profiledLowerSurface?"true":"false",
|
||||
mouthProfileAudit.floorFlatStepRunMetres,mouthProfileAudit.floorStepMetres,
|
||||
mouthProfileAudit.wallFlatStepRunMetres,mouthProfileAudit.wallStepMetres,
|
||||
PassageProfileSpacingMetres,PassageProfilePlateauMinimumMetres,
|
||||
PassageProfileStepThresholdMetres,mouthProfileAudit.passed?"PASS":"FAIL");
|
||||
std::printf("[PassageRippleAudit] id=%s window_m=32.000 spacing_m=0.125 period_m=%.3f amplitude_m=%.4f amplitude_cm=%.2f coherent_period_m=%.3f coherent_amplitude_m=%.4f coherent_amplitude_cm=%.2f local_peak_amplitude_m=%.4f coverage=%llu/%llu threshold_m=0.035 synthetic_spheres_period_m=1.500 synthetic_spheres_amplitude_m=%.4f synthetic_spheres_detected=%s floor_period_m=%.3f floor_amplitude_m=%.4f floor_amplitude_cm=%.2f floor_coherent_period_m=%.3f floor_coherent_amplitude_m=%.4f floor_coverage=%llu/%llu floor_threshold_m=0.015 floor_beaded_control_amplitude_m=%.4f floor_beaded_control_detected=%s status=%s\n",
|
||||
std::printf("[PassageRippleAudit] id=%s window_m=32.000 spacing_m=0.125 period_m=%.3f amplitude_m=%.4f amplitude_cm=%.2f coherent_period_m=%.3f coherent_amplitude_m=%.4f coherent_amplitude_cm=%.2f local_peak_amplitude_m=%.4f coverage=%llu/%llu threshold_m=0.035 synthetic_spheres_period_m=1.500 synthetic_spheres_amplitude_m=%.4f synthetic_spheres_detected=%s floor_period_m=%.3f floor_amplitude_m=%.4f floor_amplitude_cm=%.2f floor_coherent_period_m=%.3f floor_coherent_amplitude_m=%.4f floor_coverage=%llu/%llu floor_threshold_m=0.025 floor_beaded_control_amplitude_m=%.4f floor_beaded_control_detected=%s status=%s\n",
|
||||
passage.stableId.c_str(),rippleAudit.periodMetres,
|
||||
rippleAudit.amplitudeMetres,rippleAudit.amplitudeMetres*100.0,
|
||||
rippleAudit.coherentPeriodMetres,rippleAudit.coherentAmplitudeMetres,
|
||||
|
||||
@@ -99,7 +99,17 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
std::vector<Candidate> candidates;
|
||||
std::vector<std::int32_t> candidateHalfLengths{128};
|
||||
std::int32_t candidateCenterSpacing=64;
|
||||
if(arrivalRadiusSubvoxels>0&&departureRadiusSubvoxels>0)
|
||||
const bool profiledSurfaceRoute=recipe.surfaceProfile.version!=0
|
||||
&&arrivalRadiusSubvoxels>0;
|
||||
if(profiledSurfaceRoute)
|
||||
{
|
||||
// Put the surface route mouth beside the small terminal coil used by the
|
||||
// profiled-world passage, with its first route tangent pointing outward.
|
||||
// This keeps the final supported landing short and auditable.
|
||||
candidateHalfLengths={64,48};
|
||||
candidateCenterSpacing=64;
|
||||
}
|
||||
else if(arrivalRadiusSubvoxels>0&&departureRadiusSubvoxels>0)
|
||||
{
|
||||
// A centered line cannot join two bounded mouths when its chord is
|
||||
// longer than the sum of their placement radii. Leave 3 m for the
|
||||
@@ -220,7 +230,22 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
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));
|
||||
if(arrivalRadiusSubvoxels>0&&departureRadiusSubvoxels>0
|
||||
if(profiledSurfaceRoute&&index==0)
|
||||
{
|
||||
const std::int64_t targetRadiusQ=WorldAssemblyFixed(
|
||||
6.0/coreVoxelMetres);
|
||||
const std::int64_t radialQ=static_cast<std::int64_t>(std::llround(
|
||||
std::hypot(static_cast<double>(xQ),static_cast<double>(yQ))));
|
||||
const std::int64_t radialError=radialQ-targetRadiusQ;
|
||||
candidate.endpointProximityCost+=static_cast<std::uint64_t>(
|
||||
radialError*radialError)*16ull;
|
||||
const std::int64_t outwardProjection=xQ*directions[directionIndex].x
|
||||
+yQ*directions[directionIndex].y;
|
||||
if(outwardProjection<=0)
|
||||
candidate.endpointProximityCost+=static_cast<std::uint64_t>(
|
||||
targetRadiusQ*targetRadiusQ)*64ull;
|
||||
}
|
||||
else if(arrivalRadiusSubvoxels>0&&departureRadiusSubvoxels>0
|
||||
&&(index==0||index==32))
|
||||
{
|
||||
const std::uint64_t radiusSquared=static_cast<std::uint64_t>(
|
||||
@@ -400,6 +425,81 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool ApplySurfaceProfileOverride(const VFJson::Value& root,
|
||||
const FStandaloneOwnerWorld& owner, double baseGroundVoxel,
|
||||
VoxelForgeCore::WorldAssemblyRecipeV1& recipe, std::string& error)
|
||||
{
|
||||
const VFJson::Value& overrideValue = root.Get("surface_profile_override");
|
||||
if (std::holds_alternative<std::nullptr_t>(overrideValue.Data)) return true;
|
||||
if (!overrideValue.IsObject())
|
||||
{ error = "surface_profile_override must be a versioned object"; return false; }
|
||||
const VFJson::Value& versionValue = overrideValue.Get("version");
|
||||
if (!versionValue.IsNumber() || versionValue.Number() != 1.0)
|
||||
{ error = "surface_profile_override version must be one"; return false; }
|
||||
const auto* kindValue = std::get_if<std::string>(&overrideValue.Get("kind").Data);
|
||||
if (kindValue == nullptr)
|
||||
{ error = "surface_profile_override kind must be a string"; return false; }
|
||||
|
||||
using VoxelForgeCore::WorldAssemblySurfaceProfileKindV1;
|
||||
VoxelForgeCore::WorldAssemblySurfaceProfileV1 profile;
|
||||
profile.version = 1;
|
||||
double amplitudeXMetres = 0.0, amplitudeYMetres = 0.0;
|
||||
double wavelengthXMetres = 0.0, wavelengthYMetres = 0.0;
|
||||
double lambda = 0.0, arenaClearanceMetres = 0.0;
|
||||
if (*kindValue == "periodicHills")
|
||||
{
|
||||
profile.kind = WorldAssemblySurfaceProfileKindV1::periodicHills;
|
||||
amplitudeXMetres = amplitudeYMetres = 2.0;
|
||||
wavelengthXMetres = wavelengthYMetres = 96.0;
|
||||
}
|
||||
else if (*kindValue == "mirroredRelief")
|
||||
{
|
||||
profile.kind = WorldAssemblySurfaceProfileKindV1::mirroredRelief;
|
||||
amplitudeXMetres = amplitudeYMetres = 1.5;
|
||||
wavelengthXMetres = wavelengthYMetres = 128.0;
|
||||
lambda = 0.875;
|
||||
arenaClearanceMetres = 8.0;
|
||||
}
|
||||
else if (*kindValue == "flatField")
|
||||
profile.kind = WorldAssemblySurfaceProfileKindV1::flatField;
|
||||
else
|
||||
{ error = "surface_profile_override kind must be periodicHills, mirroredRelief or flatField"; return false; }
|
||||
|
||||
const auto readOptionalMetres = [&](const char* name, double& output)
|
||||
{
|
||||
const VFJson::Value& value = overrideValue.Get(name);
|
||||
if (std::holds_alternative<std::nullptr_t>(value.Data)) return true;
|
||||
if (!value.IsNumber() || !std::isfinite(value.Number())) return false;
|
||||
output = value.Number();
|
||||
return true;
|
||||
};
|
||||
if (!readOptionalMetres("amplitude_x_m", amplitudeXMetres)
|
||||
|| !readOptionalMetres("amplitude_y_m", amplitudeYMetres)
|
||||
|| !readOptionalMetres("wavelength_x_m", wavelengthXMetres)
|
||||
|| !readOptionalMetres("wavelength_y_m", wavelengthYMetres)
|
||||
|| !readOptionalMetres("mirror_lambda", lambda)
|
||||
|| !readOptionalMetres("minimum_arena_clearance_m", arenaClearanceMetres))
|
||||
{ error = "surface_profile_override contains a malformed metre parameter"; return false; }
|
||||
if (owner.CoreVoxelSizeMicrons == 0)
|
||||
{ error = "surface profile metre conversion needs a valid Core voxel size"; return false; }
|
||||
const auto metresToSubvoxels = [&](double metres)
|
||||
{
|
||||
return static_cast<std::int64_t>(std::llround(metres * 1000000.0
|
||||
* VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel
|
||||
/ owner.CoreVoxelSizeMicrons));
|
||||
};
|
||||
profile.h0Subvoxels = WorldAssemblyFixed(baseGroundVoxel);
|
||||
profile.amplitudeXSubvoxels = metresToSubvoxels(amplitudeXMetres);
|
||||
profile.amplitudeYSubvoxels = metresToSubvoxels(amplitudeYMetres);
|
||||
profile.wavelengthXSubvoxels = metresToSubvoxels(wavelengthXMetres);
|
||||
profile.wavelengthYSubvoxels = metresToSubvoxels(wavelengthYMetres);
|
||||
profile.mirrorLambdaQ16 = static_cast<std::int64_t>(std::llround(lambda * 65536.0));
|
||||
profile.minimumArenaClearanceSubvoxels = metresToSubvoxels(arenaClearanceMetres);
|
||||
VoxelForgeCore::ResolveWorldAssemblySurfaceProfilePhasesV1(profile, recipe.seed);
|
||||
recipe.surfaceProfile = profile;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool AttachRound1WorldAssemblyPrototype(FStandaloneOwnerWorld& owner,VFJson::Value& root,
|
||||
std::string& error,const std::string& manifestOutputPath,
|
||||
VoxelForgeCore::WorldAssemblyManifest* outManifest=nullptr)
|
||||
@@ -440,6 +540,10 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
recipe.biomeClimateReliefWavelengthSubvoxels=WorldAssemblyWavelength(map.ReliefFrequency,3333.0);
|
||||
recipe.biomeClimateMoistureWavelengthSubvoxels=WorldAssemblyWavelength(map.MoistureFrequency,2500.0);
|
||||
recipe.biomeClimateReliefContrastQ16=WorldAssemblyQ16(map.ReliefContrast);
|
||||
if (!ApplySurfaceProfileOverride(root, owner,
|
||||
static_cast<double>(bottomVoxel) + static_cast<double>(heightVoxels)
|
||||
* surface.BaseGroundRelative,
|
||||
recipe, error)) return false;
|
||||
const auto makeProfile=[&](uint32 stableId,const FSurfaceGenerationParams& params,
|
||||
const FBiomeResolved* climate)
|
||||
{
|
||||
@@ -524,9 +628,18 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
stratum.capRidgeWavelengthSubvoxels=WorldAssemblyWavelength(surface.CeilingRidgeFrequency,50.0);
|
||||
stratum.waterSurfaceSubvoxels=WorldAssemblyFixed(strateBottom+strateHeight*surface.WaterLevelRelative);
|
||||
stratum.waterEnabled=definition&&definition->bHasWater&&surface.WaterLevelRelative>0.0f;
|
||||
stratum.surfaceProfile=recipe.surfaceProfile;
|
||||
if (recipe.surfaceProfile.version != 0)
|
||||
{
|
||||
stratum.capUndulationSubvoxels=0;
|
||||
stratum.capDetailAmplitudeSubvoxels=0;
|
||||
stratum.capRidgeAmplitudeSubvoxels=0;
|
||||
}
|
||||
|
||||
// Select a deterministic steep edge in the resolved owner field for a bounded shelf.
|
||||
// Selection d'une pente deterministe dans le champ resolu pour une saillie bornee.
|
||||
if (recipe.surfaceProfile.version == 0)
|
||||
{
|
||||
constexpr int probeSide=49,probeStep=32;
|
||||
std::array<float,probeSide*probeSide> heights{};
|
||||
std::array<uint8,probeSide*probeSide> valid{};
|
||||
@@ -595,6 +708,7 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
stratum.features.push_back(shelf);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::int64_t arrivalRadiusQ=0,departureRadiusQ=0;
|
||||
const auto ownerVoxelsToCoreQ=[&](double value)
|
||||
{return static_cast<std::int64_t>(std::llround(value*owner.OwnerVoxelSizeMicrons
|
||||
@@ -1032,6 +1146,19 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
"interior-facing upper face","shared resolved-layout density band",solidDensity};
|
||||
if(type==ECaveGeneratorType::SurfaceWorld)
|
||||
{
|
||||
if(assembly.recipe.surfaceProfile.version!=0)
|
||||
{
|
||||
ResolvedLayoutAnchorV2 profileAnchor;
|
||||
profileAnchor.kind="heightfield_profile";
|
||||
profileAnchor.stableId="heightfield-profile/"
|
||||
+std::to_string(static_cast<std::uint32_t>(
|
||||
assembly.recipe.surfaceProfile.kind));
|
||||
profileAnchor.units="profile kind enum";
|
||||
profileAnchor.values={static_cast<std::int64_t>(
|
||||
assembly.recipe.surfaceProfile.version),
|
||||
static_cast<std::int64_t>(assembly.recipe.surfaceProfile.kind)};
|
||||
s.anchors.push_back(std::move(profileAnchor));
|
||||
}
|
||||
for(const auto& p:assembly.recipe.biomes)
|
||||
if(p.hasCapProfile)
|
||||
{ResolvedLayoutAnchorV2 a;a.kind="cap_profile";a.stableId="biome/"+std::to_string(p.stableId);
|
||||
|
||||
Reference in new issue
Block a user