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:
Fr0zkaandClaude Opus 5.5 committed 2026-10-25 01:49:11 +02:00
1 parent 408c14baff
commit 20f6c98ec5
17 files changed
+1620 -23

No files matched your search

@@ -425,6 +425,59 @@ namespace VoxelForgeCore
std::int64_t capRidgeWavelengthSubvoxels = 256 * 64; std::int64_t capRidgeWavelengthSubvoxels = 256 * 64;
}; };
enum class WorldAssemblySurfaceProfileKindV1 : std::uint32_t
{
natural = 0,
periodicHills = 1,
mirroredRelief = 2,
flatField = 3
};
// Version zero is the historical noise terrain and is intentionally omitted from
// canonical JSON and hashes. Version one uses metre-derived fixed-scale dimensions;
// phases are Q0.32 turns generated from the recipe seed during plan construction.
struct WorldAssemblySurfaceProfileV1
{
std::uint32_t version = 0;
WorldAssemblySurfaceProfileKindV1 kind = WorldAssemblySurfaceProfileKindV1::natural;
std::int64_t h0Subvoxels = 0;
std::int64_t amplitudeXSubvoxels = 0;
std::int64_t amplitudeYSubvoxels = 0;
std::int64_t wavelengthXSubvoxels = 0;
std::int64_t wavelengthYSubvoxels = 0;
std::uint32_t phaseXTurnsQ32 = 0;
std::uint32_t phaseYTurnsQ32 = 0;
std::int64_t mirrorLambdaQ16 = 0;
std::int64_t minimumArenaClearanceSubvoxels = 0;
friend bool operator==(const WorldAssemblySurfaceProfileV1&,
const WorldAssemblySurfaceProfileV1&) = default;
};
inline std::uint32_t WorldAssemblySurfaceProfileSeededPhaseQ32(
std::uint32_t seed, std::uint32_t axis) noexcept
{
std::uint64_t value = static_cast<std::uint64_t>(seed)
^ (axis == 0 ? 0x706572696f645831ull : 0x706572696f645931ull);
value += 0x9E3779B97F4A7C15ull;
value = (value ^ (value >> 30)) * 0xBF58476D1CE4E5B9ull;
value = (value ^ (value >> 27)) * 0x94D049BB133111EBull;
value ^= value >> 31;
return static_cast<std::uint32_t>(value >> 32);
}
inline void ResolveWorldAssemblySurfaceProfilePhasesV1(
WorldAssemblySurfaceProfileV1& profile, std::uint32_t seed) noexcept
{
if (profile.version == 1
&& (profile.kind == WorldAssemblySurfaceProfileKindV1::periodicHills
|| profile.kind == WorldAssemblySurfaceProfileKindV1::mirroredRelief))
{
profile.phaseXTurnsQ32 = WorldAssemblySurfaceProfileSeededPhaseQ32(seed, 0);
profile.phaseYTurnsQ32 = WorldAssemblySurfaceProfileSeededPhaseQ32(seed, 1);
}
}
struct WorldAssemblyRecipeV1 struct WorldAssemblyRecipeV1
{ {
std::uint32_t schemaVersion = WorldAssemblySchemaVersion; std::uint32_t schemaVersion = WorldAssemblySchemaVersion;
@@ -442,6 +495,7 @@ namespace VoxelForgeCore
std::int64_t biomeClimateReliefWavelengthSubvoxels = 256 * 3333; std::int64_t biomeClimateReliefWavelengthSubvoxels = 256 * 3333;
std::int64_t biomeClimateMoistureWavelengthSubvoxels = 256 * 2500; std::int64_t biomeClimateMoistureWavelengthSubvoxels = 256 * 2500;
std::int64_t biomeClimateReliefContrastQ16 = 2ll << 16; std::int64_t biomeClimateReliefContrastQ16 = 2ll << 16;
WorldAssemblySurfaceProfileV1 surfaceProfile;
std::vector<WorldAssemblyBiomeProfileV1> biomes; std::vector<WorldAssemblyBiomeProfileV1> biomes;
std::uint64_t contentHash = 0; std::uint64_t contentHash = 0;
}; };
@@ -501,6 +555,7 @@ namespace VoxelForgeCore
std::int64_t capRidgeWavelengthSubvoxels = 256 * 64; std::int64_t capRidgeWavelengthSubvoxels = 256 * 64;
std::int64_t waterSurfaceSubvoxels = 0; std::int64_t waterSurfaceSubvoxels = 0;
bool waterEnabled = false; bool waterEnabled = false;
WorldAssemblySurfaceProfileV1 surfaceProfile;
std::vector<WorldAssemblyFeatureV1> features; std::vector<WorldAssemblyFeatureV1> features;
WorldAssemblyRouteConstraintsV1 routeConstraints; WorldAssemblyRouteConstraintsV1 routeConstraints;
std::vector<WorldAssemblyRoutePointV1> route; std::vector<WorldAssemblyRoutePointV1> route;
+30 -1
View File
@@ -1993,6 +1993,10 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
&&anchor.values.size()>=3;}); &&anchor.values.size()>=3;});
const bool hasLowerTangent=hasLowerRoute&&!hasPassageArrival const bool hasLowerTangent=hasLowerRoute&&!hasPassageArrival
&&routeTangent(lower,false,lowerTangent); &&routeTangent(lower,false,lowerTangent);
const bool profiledSurfaceJoin=hasLowerTangent
&&std::any_of(lower.anchors.begin(),lower.anchors.end(),
[](const auto& anchor){return anchor.kind=="heightfield_profile"
&&anchor.values.size()>=2&&anchor.values[0]>0;});
const std::int64_t outerRingRadius=std::max<std::int64_t>(1, const std::int64_t outerRingRadius=std::max<std::int64_t>(1,
placementRadius-mouthRadius-qPerMetre/2); placementRadius-mouthRadius-qPerMetre/2);
@@ -2237,7 +2241,7 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
helixEndIndex=xy.size()-1; helixEndIndex=xy.size()-1;
hasAnchorHelix=helixEndIndex>0; hasAnchorHelix=helixEndIndex>0;
} }
const std::int64_t routeGradeQ16=hasAnchorHelix const std::int64_t routeGradeQ16=profiledSurfaceJoin?8000:hasAnchorHelix
&&lower.generatorKind=="SurfaceWorld"?8000 &&lower.generatorKind=="SurfaceWorld"?8000
:style==0&&anchoredIslandPassage&&!anchoredSpiral?15500:TargetGradeQ16; :style==0&&anchoredIslandPassage&&!anchoredSpiral?15500:TargetGradeQ16;
const std::array<std::int64_t,2> firstRing=ringPoint(phase); const std::array<std::int64_t,2> firstRing=ringPoint(phase);
@@ -2310,6 +2314,7 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
std::uint32_t lowerPhase=static_cast<std::uint32_t>(PassageSeed(seed, std::uint32_t lowerPhase=static_cast<std::uint32_t>(PassageSeed(seed,
upper.strateIndex,lower.strateIndex,connection,0x4c505248u)%16); upper.strateIndex,lower.strateIndex,connection,0x4c505248u)%16);
std::int64_t bestAlignment=std::numeric_limits<std::int64_t>::min(); std::int64_t bestAlignment=std::numeric_limits<std::int64_t>::min();
bool lowerRouteReversed=false;
const std::uint32_t hashedLowerPhase=lowerPhase; const std::uint32_t hashedLowerPhase=lowerPhase;
for(std::uint32_t offset=0;offset<16;++offset) for(std::uint32_t offset=0;offset<16;++offset)
{ {
@@ -2326,8 +2331,32 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
if(score>bestAlignment) if(score>bestAlignment)
{bestAlignment=score;lowerPhase=candidate;} {bestAlignment=score;lowerPhase=candidate;}
} }
// A route is walkable in either direction. If the approach ring lies on
// the wrong side of its first route tangent, attach to the same endpoint
// while traversing the route backwards instead of rejecting the layout.
if(bestAlignment==std::numeric_limits<std::int64_t>::min())
{
for(std::uint32_t offset=0;offset<16;++offset)
{
const std::uint32_t candidate=static_cast<std::uint32_t>(
(hashedLowerPhase+offset)%16);
const auto terminal=ringPoint(candidate,loops);
const std::int64_t dx=lowerMouth[0]-terminal[0];
const std::int64_t dy=lowerMouth[1]-terminal[1];
const std::int64_t alignment=dx*lowerTangent[0]+dy*lowerTangent[1];
const std::int64_t cross=std::abs(dx*lowerTangent[1]-dy*lowerTangent[0]);
if(alignment>=0||cross==0)continue;
const auto length=distanceQ(dx,dy);
const std::int64_t score=-alignment*65536/static_cast<std::int64_t>(length);
if(score>bestAlignment)
{bestAlignment=score;lowerPhase=candidate;lowerRouteReversed=true;}
}
}
if(bestAlignment==std::numeric_limits<std::int64_t>::min()) if(bestAlignment==std::numeric_limits<std::int64_t>::min())
{error=passage.stableId+" could not find a non-overlapping lower landing approach";return false;} {error=passage.stableId+" could not find a non-overlapping lower landing approach";return false;}
std::printf("[ResolvedPassageLowerApproach] id=%s direction=%s alignment_q16=%lld status=PASS\n",
passage.stableId.c_str(),lowerRouteReversed?"reverse":"forward",
static_cast<long long>(bestAlignment));
if(phase!=lowerPhase) if(phase!=lowerPhase)
for(int step=1;step<=16;++step) for(int step=1;step<=16;++step)
{ {
+564 -5
View File
@@ -145,6 +145,12 @@ namespace world_assembly_detail
}; };
constexpr std::int64_t Q16 = 1ll << 16; constexpr std::int64_t Q16 = 1ll << 16;
constexpr std::int64_t Q32 = 1ll << 32;
constexpr std::int64_t SurfaceProfileSubvoxelsPerMetre =
WorldAssemblySubvoxelsPerVoxel * 4;
constexpr std::int64_t SurfaceProfileMinimumClearanceSubvoxels = 2253;
constexpr std::int64_t SurfaceProfileFightClearanceSubvoxels =
8 * SurfaceProfileSubvoxelsPerMetre;
constexpr std::int64_t MinimumCliffShelfThicknessSubvoxels = 2 * WorldAssemblySubvoxelsPerVoxel; constexpr std::int64_t MinimumCliffShelfThicknessSubvoxels = 2 * WorldAssemblySubvoxelsPerVoxel;
constexpr std::int64_t MaxSerializedMagnitude = 1000000000000ll; constexpr std::int64_t MaxSerializedMagnitude = 1000000000000ll;
constexpr std::size_t MaxBiomeCount = 8; constexpr std::size_t MaxBiomeCount = 8;
@@ -228,6 +234,114 @@ namespace world_assembly_detail
inline bool OutsideRange(std::int64_t value) inline bool OutsideRange(std::int64_t value)
{ return value < -MaxSerializedMagnitude || value > MaxSerializedMagnitude; } { return value < -MaxSerializedMagnitude || value > MaxSerializedMagnitude; }
inline bool IsNaturalSurfaceProfile(const WorldAssemblySurfaceProfileV1& profile)
{ return profile.version == 0 && profile == WorldAssemblySurfaceProfileV1{}; }
inline bool ValidateSurfaceProfile(const WorldAssemblySurfaceProfileV1& profile,
std::uint32_t seed, std::string& error)
{
if (profile.version == 0)
{
if (!IsNaturalSurfaceProfile(profile))
{ error = "natural SurfaceWorld profile must use the all-zero default payload"; return false; }
return true;
}
if (profile.version != 1
|| !ValidScalar(profile.h0Subvoxels)
|| !ValidScalar(profile.amplitudeXSubvoxels)
|| !ValidScalar(profile.amplitudeYSubvoxels)
|| !ValidScalar(profile.wavelengthXSubvoxels)
|| !ValidScalar(profile.wavelengthYSubvoxels)
|| !ValidScalar(profile.mirrorLambdaQ16)
|| !ValidScalar(profile.minimumArenaClearanceSubvoxels)
|| profile.minimumArenaClearanceSubvoxels < 0
|| profile.minimumArenaClearanceSubvoxels > 32 * SurfaceProfileSubvoxelsPerMetre)
{ error = "SurfaceWorld profile version or fixed-scale value is outside its supported range"; return false; }
constexpr std::int64_t MinimumPeriodAmplitude = SurfaceProfileSubvoxelsPerMetre / 2;
constexpr std::int64_t MaximumPeriodicAmplitude = 3 * SurfaceProfileSubvoxelsPerMetre;
constexpr std::int64_t MaximumMirrorAmplitude = 2 * SurfaceProfileSubvoxelsPerMetre;
constexpr std::int64_t MinimumWavelength = 80 * SurfaceProfileSubvoxelsPerMetre;
constexpr std::int64_t MaximumWavelength = 160 * SurfaceProfileSubvoxelsPerMetre;
const bool hasArenaClearance = profile.minimumArenaClearanceSubvoxels == 0
|| profile.minimumArenaClearanceSubvoxels >= SurfaceProfileFightClearanceSubvoxels;
if (!hasArenaClearance)
{ error = "SurfaceWorld arena clearance must be omitted or at least eight metres"; return false; }
if (profile.kind == WorldAssemblySurfaceProfileKindV1::periodicHills
|| profile.kind == WorldAssemblySurfaceProfileKindV1::mirroredRelief)
{
const std::int64_t maximumAmplitude =
profile.kind == WorldAssemblySurfaceProfileKindV1::periodicHills
? MaximumPeriodicAmplitude : MaximumMirrorAmplitude;
if (profile.amplitudeXSubvoxels < MinimumPeriodAmplitude
|| profile.amplitudeYSubvoxels < MinimumPeriodAmplitude
|| profile.amplitudeXSubvoxels > maximumAmplitude
|| profile.amplitudeYSubvoxels > maximumAmplitude
|| profile.wavelengthXSubvoxels < MinimumWavelength
|| profile.wavelengthYSubvoxels < MinimumWavelength
|| profile.wavelengthXSubvoxels > MaximumWavelength
|| profile.wavelengthYSubvoxels > MaximumWavelength)
{ error = "periodic SurfaceWorld amplitudes or wavelengths are outside the R20 metre ranges"; return false; }
// Round every gradient component upward before applying the 15-degree bound.
// This makes the integer plan-time check conservative around division boundaries.
constexpr std::int64_t TwoPiQ16Ceiling = 411775;
constexpr std::int64_t Tan15Q16Floor = 17560;
const auto component = [](std::int64_t amplitude, std::int64_t wavelength)
{
const std::int64_t numerator = TwoPiQ16Ceiling * amplitude;
return (numerator + wavelength - 1) / wavelength;
};
const std::int64_t gx = component(profile.amplitudeXSubvoxels,
profile.wavelengthXSubvoxels);
const std::int64_t gy = component(profile.amplitudeYSubvoxels,
profile.wavelengthYSubvoxels);
if (gx * gx + gy * gy > Tan15Q16Floor * Tan15Q16Floor)
{ error = "periodic SurfaceWorld maximum walk grade exceeds tan(15 degrees)"; return false; }
if (profile.kind == WorldAssemblySurfaceProfileKindV1::periodicHills)
{
if (profile.mirrorLambdaQ16 != 0
|| profile.phaseXTurnsQ32 != WorldAssemblySurfaceProfileSeededPhaseQ32(seed, 0)
|| profile.phaseYTurnsQ32 != WorldAssemblySurfaceProfileSeededPhaseQ32(seed, 1))
{ error = "periodic-hill phases must be the stable seed-derived Q0.32 phases"; return false; }
}
else if (profile.mirrorLambdaQ16 < 49152 || profile.mirrorLambdaQ16 > Q16
|| profile.phaseXTurnsQ32 != WorldAssemblySurfaceProfileSeededPhaseQ32(seed, 0)
|| profile.phaseYTurnsQ32 != WorldAssemblySurfaceProfileSeededPhaseQ32(seed, 1))
{ error = "mirrored-relief lambda or seeded phases are outside their supported range"; return false; }
}
else if (profile.kind == WorldAssemblySurfaceProfileKindV1::flatField)
{
if (profile.amplitudeXSubvoxels != 0 || profile.amplitudeYSubvoxels != 0
|| profile.wavelengthXSubvoxels != 0 || profile.wavelengthYSubvoxels != 0
|| profile.phaseXTurnsQ32 != 0 || profile.phaseYTurnsQ32 != 0
|| profile.mirrorLambdaQ16 != 0)
{ error = "flatField must not carry periodic or mirrored relief parameters"; return false; }
}
else
{ error = "SurfaceWorld profile kind is unsupported"; return false; }
return true;
}
inline void HashSurfaceProfile(std::uint64_t& hash,
const WorldAssemblySurfaceProfileV1& profile)
{
HashWord(hash, 0x5355524650524f31ull, 8);
HashWord(hash, profile.version, 4);
HashWord(hash, static_cast<std::uint32_t>(profile.kind), 4);
HashWord(hash, static_cast<std::uint64_t>(profile.h0Subvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(profile.amplitudeXSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(profile.amplitudeYSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(profile.wavelengthXSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(profile.wavelengthYSubvoxels), 8);
HashWord(hash, profile.phaseXTurnsQ32, 4);
HashWord(hash, profile.phaseYTurnsQ32, 4);
HashWord(hash, static_cast<std::uint64_t>(profile.mirrorLambdaQ16), 8);
HashWord(hash, static_cast<std::uint64_t>(profile.minimumArenaClearanceSubvoxels), 8);
}
inline bool ValidateRecipe(const WorldAssemblyRecipeV1& recipe, std::string& error) inline bool ValidateRecipe(const WorldAssemblyRecipeV1& recipe, std::string& error)
{ {
if (recipe.schemaVersion != WorldAssemblySchemaVersion if (recipe.schemaVersion != WorldAssemblySchemaVersion
@@ -296,16 +410,57 @@ namespace world_assembly_detail
|| recipe.biomeClimateMoistureWavelengthSubvoxels <= 0 || recipe.biomeClimateMoistureWavelengthSubvoxels <= 0
|| recipe.biomeClimateReliefContrastQ16 < 0)) || recipe.biomeClimateReliefContrastQ16 < 0))
{ error = "world assembly climate parameters are outside the supported fixed-scale range"; return false; } { error = "world assembly climate parameters are outside the supported fixed-scale range"; return false; }
if (!ValidateSurfaceProfile(recipe.surfaceProfile, recipe.seed, error)) return false;
return true;
}
inline bool ValidateSurfaceProfileGeometry(const WorldAssemblySurfaceProfileV1& profile,
const WorldAssemblyStratumPlanV1& stratum, std::int64_t layoutChunkWidthSubvoxels,
std::string& error)
{
if (profile.version == 0) return true;
const std::int64_t relief = profile.amplitudeXSubvoxels + profile.amplitudeYSubvoxels;
const std::int64_t groundMinimum = profile.h0Subvoxels - relief - 2;
const std::int64_t groundMaximum = profile.h0Subvoxels + relief + 2;
std::int64_t capSwing = 0;
if (profile.kind == WorldAssemblySurfaceProfileKindV1::mirroredRelief)
{
const std::int64_t product = relief * profile.mirrorLambdaQ16;
capSwing = (product + Q16 - 1) / Q16 + 2;
}
const std::int64_t capMinimum = stratum.capHeightSubvoxels - capSwing;
const std::int64_t capMaximum = stratum.capHeightSubvoxels + capSwing;
const std::int64_t clearance = capMinimum - groundMaximum;
const std::int64_t requiredClearance = std::max(
SurfaceProfileMinimumClearanceSubvoxels,
profile.minimumArenaClearanceSubvoxels);
if (clearance < requiredClearance)
{ error = "SurfaceWorld profile does not preserve its required floor-to-cap clearance"; return false; }
const std::int64_t stratumMinimum = static_cast<std::int64_t>(stratum.bottomChunkZ)
* layoutChunkWidthSubvoxels;
const std::int64_t stratumMaximum = (static_cast<std::int64_t>(stratum.topChunkZ) + 1)
* layoutChunkWidthSubvoxels;
if (groundMinimum <= stratumMinimum || capMaximum >= stratumMaximum)
{ error = "SurfaceWorld profile escapes its finite stratum Z bounds"; return false; }
return true; return true;
} }
inline bool ValidateStratum(const WorldAssemblyStratumPlanV1& stratum, inline bool ValidateStratum(const WorldAssemblyStratumPlanV1& stratum,
std::int64_t layoutChunkWidthSubvoxels, std::string& error) const WorldAssemblyRecipeV1& recipe, std::string& error)
{ {
const std::int64_t layoutChunkWidthSubvoxels = recipe.layoutChunkWidthSubvoxels;
if (stratum.strateIndex < 0 || stratum.topChunkZ < stratum.bottomChunkZ if (stratum.strateIndex < 0 || stratum.topChunkZ < stratum.bottomChunkZ
|| stratum.capWavelengthSubvoxels <= 0 || stratum.capUndulationSubvoxels < 0 || stratum.capWavelengthSubvoxels <= 0 || stratum.capUndulationSubvoxels < 0
|| stratum.features.size() > MaxFeatureCount || stratum.route.size() > MaxRoutePointCount) || stratum.features.size() > MaxFeatureCount || stratum.route.size() > MaxRoutePointCount)
{ error = "world assembly stratum plan has an invalid extent or exceeds its bounded payload"; return false; } { error = "world assembly stratum plan has an invalid extent or exceeds its bounded payload"; return false; }
const WorldAssemblyRecipeV1& effectiveRecipe = stratum.hasRecipeOverride
? stratum.recipeOverride : recipe;
if (!ValidateSurfaceProfile(stratum.surfaceProfile, effectiveRecipe.seed, error)) return false;
if (stratum.surfaceProfile != effectiveRecipe.surfaceProfile)
{ error = "SurfaceWorld recipe and stratum plan profile payloads do not match"; return false; }
if (!ValidateSurfaceProfileGeometry(stratum.surfaceProfile, stratum,
layoutChunkWidthSubvoxels, error)) return false;
if (!ValidScalar(stratum.capHeightSubvoxels) || !ValidScalar(stratum.capUndulationSubvoxels) if (!ValidScalar(stratum.capHeightSubvoxels) || !ValidScalar(stratum.capUndulationSubvoxels)
|| !ValidScalar(stratum.capWavelengthSubvoxels) || !ValidScalar(stratum.capWavelengthSubvoxels)
|| !ValidScalar(stratum.capDetailAmplitudeSubvoxels) || !ValidScalar(stratum.capDetailAmplitudeSubvoxels)
@@ -466,6 +621,8 @@ namespace world_assembly_detail
HashWord(hash, static_cast<std::uint64_t>(value), 8); HashWord(hash, static_cast<std::uint64_t>(value), 8);
} }
} }
if (recipe.surfaceProfile.version != 0)
HashSurfaceProfile(hash, recipe.surfaceProfile);
return hash; return hash;
} }
@@ -525,6 +682,8 @@ namespace world_assembly_detail
for (const auto value : point.positionSubvoxels) HashWord(hash, static_cast<std::uint64_t>(value), 8); for (const auto value : point.positionSubvoxels) HashWord(hash, static_cast<std::uint64_t>(value), 8);
HashWord(hash, static_cast<std::uint64_t>(point.radiusSubvoxels), 8); HashWord(hash, static_cast<std::uint64_t>(point.radiusSubvoxels), 8);
} }
if (stratum.surfaceProfile.version != 0)
HashSurfaceProfile(hash, stratum.surfaceProfile);
return hash; return hash;
} }
@@ -627,6 +786,59 @@ namespace world_assembly_detail
return out.str(); return out.str();
} }
inline const char* SurfaceProfileKindName(WorldAssemblySurfaceProfileKindV1 kind)
{
switch (kind)
{
case WorldAssemblySurfaceProfileKindV1::natural: return "natural";
case WorldAssemblySurfaceProfileKindV1::periodicHills: return "periodicHills";
case WorldAssemblySurfaceProfileKindV1::mirroredRelief: return "mirroredRelief";
case WorldAssemblySurfaceProfileKindV1::flatField: return "flatField";
}
return "unsupported";
}
inline bool ParseSurfaceProfile(const VFJson::Value& value,
WorldAssemblySurfaceProfileV1& profile)
{
if (!value.IsObject() || !ReadU32(value.Get("version"), profile.version)) return false;
const auto* kind = std::get_if<std::string>(&value.Get("kind").Data);
if (kind == nullptr) return false;
if (*kind == "periodicHills") profile.kind = WorldAssemblySurfaceProfileKindV1::periodicHills;
else if (*kind == "mirroredRelief") profile.kind = WorldAssemblySurfaceProfileKindV1::mirroredRelief;
else if (*kind == "flatField") profile.kind = WorldAssemblySurfaceProfileKindV1::flatField;
else if (*kind == "natural") profile.kind = WorldAssemblySurfaceProfileKindV1::natural;
else return false;
return ReadInteger(value.Get("h0_q"), profile.h0Subvoxels)
&& ReadInteger(value.Get("amplitude_x_q"), profile.amplitudeXSubvoxels)
&& ReadInteger(value.Get("amplitude_y_q"), profile.amplitudeYSubvoxels)
&& ReadInteger(value.Get("wavelength_x_q"), profile.wavelengthXSubvoxels)
&& ReadInteger(value.Get("wavelength_y_q"), profile.wavelengthYSubvoxels)
&& ReadU32(value.Get("phase_x_turns_q32"), profile.phaseXTurnsQ32)
&& ReadU32(value.Get("phase_y_turns_q32"), profile.phaseYTurnsQ32)
&& ReadInteger(value.Get("mirror_lambda_q16"), profile.mirrorLambdaQ16)
&& ReadInteger(value.Get("minimum_arena_clearance_q"),
profile.minimumArenaClearanceSubvoxels);
}
inline std::string SurfaceProfileJson(const WorldAssemblySurfaceProfileV1& profile)
{
std::ostringstream out;
out << "{\"version\":" << profile.version
<< ",\"kind\":\"" << SurfaceProfileKindName(profile.kind) << "\""
<< ",\"h0_q\":" << profile.h0Subvoxels
<< ",\"amplitude_x_q\":" << profile.amplitudeXSubvoxels
<< ",\"amplitude_y_q\":" << profile.amplitudeYSubvoxels
<< ",\"wavelength_x_q\":" << profile.wavelengthXSubvoxels
<< ",\"wavelength_y_q\":" << profile.wavelengthYSubvoxels
<< ",\"phase_x_turns_q32\":" << profile.phaseXTurnsQ32
<< ",\"phase_y_turns_q32\":" << profile.phaseYTurnsQ32
<< ",\"mirror_lambda_q16\":" << profile.mirrorLambdaQ16
<< ",\"minimum_arena_clearance_q\":"
<< profile.minimumArenaClearanceSubvoxels << '}';
return out.str();
}
inline bool ParseRecipe(const VFJson::Value& value, WorldAssemblyRecipeV1& recipe, inline bool ParseRecipe(const VFJson::Value& value, WorldAssemblyRecipeV1& recipe,
std::string& error) std::string& error)
{ {
@@ -647,6 +859,10 @@ namespace world_assembly_detail
|| !ReadInteger(value.Get("climate_moisture_wavelength_q"), recipe.biomeClimateMoistureWavelengthSubvoxels) || !ReadInteger(value.Get("climate_moisture_wavelength_q"), recipe.biomeClimateMoistureWavelengthSubvoxels)
|| !ReadInteger(value.Get("climate_relief_contrast_q16"), recipe.biomeClimateReliefContrastQ16))) || !ReadInteger(value.Get("climate_relief_contrast_q16"), recipe.biomeClimateReliefContrastQ16)))
{ error = "world assembly recipe is missing evaluator-2 climate parameters"; return false; } { error = "world assembly recipe is missing evaluator-2 climate parameters"; return false; }
const VFJson::Value& surfaceProfileValue = value.Get("surface_profile");
if (!std::holds_alternative<std::nullptr_t>(surfaceProfileValue.Data)
&& !ParseSurfaceProfile(surfaceProfileValue, recipe.surfaceProfile))
{ error = "world assembly recipe has a malformed versioned SurfaceWorld profile"; return false; }
for (const auto& item : value.Get("biomes").AsArray()) for (const auto& item : value.Get("biomes").AsArray())
{ {
WorldAssemblyBiomeProfileV1 biome; WorldAssemblyBiomeProfileV1 biome;
@@ -709,6 +925,8 @@ namespace world_assembly_detail
out << ",\"climate_relief_wavelength_q\":" << recipe.biomeClimateReliefWavelengthSubvoxels out << ",\"climate_relief_wavelength_q\":" << recipe.biomeClimateReliefWavelengthSubvoxels
<< ",\"climate_moisture_wavelength_q\":" << recipe.biomeClimateMoistureWavelengthSubvoxels << ",\"climate_moisture_wavelength_q\":" << recipe.biomeClimateMoistureWavelengthSubvoxels
<< ",\"climate_relief_contrast_q16\":" << recipe.biomeClimateReliefContrastQ16; << ",\"climate_relief_contrast_q16\":" << recipe.biomeClimateReliefContrastQ16;
if (recipe.surfaceProfile.version != 0)
out << ",\"surface_profile\":" << SurfaceProfileJson(recipe.surfaceProfile);
out << ",\"content_hash\":\"" << HashText(recipe.contentHash) << "\",\"biomes\":["; out << ",\"content_hash\":\"" << HashText(recipe.contentHash) << "\",\"biomes\":[";
for (std::size_t i = 0; i < recipe.biomes.size(); ++i) for (std::size_t i = 0; i < recipe.biomes.size(); ++i)
{ {
@@ -797,6 +1015,10 @@ namespace world_assembly_detail
{ error = "world assembly stratum has malformed optional evaluator-2 fields"; return false; } { error = "world assembly stratum has malformed optional evaluator-2 fields"; return false; }
if(hasWaterBoolean)stratum.waterEnabled=waterEnabled.Boolean(false); if(hasWaterBoolean)stratum.waterEnabled=waterEnabled.Boolean(false);
if(hasWaterNumber)stratum.waterEnabled=waterEnabled.Number()!=0.0; if(hasWaterNumber)stratum.waterEnabled=waterEnabled.Number()!=0.0;
const VFJson::Value& surfaceProfileValue = item.Get("surface_profile");
if (!std::holds_alternative<std::nullptr_t>(surfaceProfileValue.Data)
&& !ParseSurfaceProfile(surfaceProfileValue, stratum.surfaceProfile))
{ error = "world assembly stratum has a malformed versioned SurfaceWorld profile"; return false; }
const VFJson::Value& routeConstraintsValue = item.Get("route_constraints"); const VFJson::Value& routeConstraintsValue = item.Get("route_constraints");
if (!std::holds_alternative<std::nullptr_t>(routeConstraintsValue.Data)) if (!std::holds_alternative<std::nullptr_t>(routeConstraintsValue.Data))
{ {
@@ -1367,6 +1589,196 @@ namespace world_assembly_detail
return SmoothQ16(static_cast<std::uint32_t>(std::clamp<std::int64_t>(edge*Q16/feather,0,Q16))); return SmoothQ16(static_cast<std::uint32_t>(std::clamp<std::int64_t>(edge*Q16/feather,0,Q16)));
} }
constexpr std::int64_t Q30 = 1ll << 30;
constexpr std::array<std::int64_t, 30> CordicAtanTurnsQ32{{
536870912,316933406,167458907,85004756,42667331,21354465,10679838,5340245,
2670163,1335087,667544,333772,166886,83443,41722,20861,10430,5215,2608,
1304,652,326,163,81,41,20,10,5,3,1}};
inline std::array<std::int64_t, 2> CordicSinCosQuarterQ30(std::uint32_t angleQ32)
{
if (angleQ32 == 0) return {Q30, 0};
std::int64_t x = 652032874, y = 0;
std::int64_t z = angleQ32;
for (std::size_t i = 0; i < CordicAtanTurnsQ32.size(); ++i)
{
const std::int64_t oldX = x;
if (z >= 0)
{
x -= y >> i;
y += oldX >> i;
z -= CordicAtanTurnsQ32[i];
}
else
{
x += y >> i;
y -= oldX >> i;
z += CordicAtanTurnsQ32[i];
}
}
return {std::clamp<std::int64_t>(x, -Q30, Q30),
std::clamp<std::int64_t>(y, -Q30, Q30)};
}
inline std::int64_t CosineQ30(std::uint32_t phaseTurnsQ32)
{
constexpr std::uint32_t Quarter = 1u << 30;
if (phaseTurnsQ32 == 0) return Q30;
if (phaseTurnsQ32 == Quarter) return 0;
if (phaseTurnsQ32 == 2u * Quarter) return -Q30;
if (phaseTurnsQ32 == 3u * Quarter) return 0;
constexpr std::uint32_t TableBits = 16;
constexpr std::uint32_t TableSize = 1u << TableBits;
constexpr std::uint32_t FractionMask = (1u << (32 - TableBits)) - 1u;
static const std::array<std::int32_t, TableSize> table = []
{
std::array<std::int32_t, TableSize> values{};
constexpr std::uint32_t QuarterTableSize = TableSize / 4;
constexpr std::uint32_t QuarterStepQ32 = 1u << (30 - (TableBits - 2));
std::array<std::int32_t, QuarterTableSize + 1> quarter{};
for (std::uint32_t offset = 0; offset <= QuarterTableSize; ++offset)
{
const std::int64_t cosine = CordicSinCosQuarterQ30(
offset * QuarterStepQ32)[0];
const std::int64_t monotone = offset == 0 ? cosine
: std::min<std::int64_t>(quarter[offset - 1], cosine);
quarter[offset] = static_cast<std::int32_t>(monotone);
}
for (std::uint32_t index = 0; index < TableSize; ++index)
{
const std::uint32_t quadrant = index / QuarterTableSize;
const std::uint32_t offset = index % QuarterTableSize;
const std::uint32_t mirroredOffset = QuarterTableSize - offset;
const std::int32_t value = quadrant == 0 ? quarter[offset]
: quadrant == 1 ? -quarter[mirroredOffset]
: quadrant == 2 ? -quarter[offset] : quarter[mirroredOffset];
values[index] = value;
}
return values;
}();
const std::uint32_t index = phaseTurnsQ32 >> (32 - TableBits);
const std::uint32_t fraction = phaseTurnsQ32 & FractionMask;
const std::int64_t first = table[index];
const std::int64_t second = table[(index + 1u) & (TableSize - 1u)];
const std::int64_t numerator = (second - first) * fraction;
const std::int64_t rounded = numerator >= 0
? numerator + (1ll << (31 - TableBits))
: numerator - (1ll << (31 - TableBits));
// The fixed-point table interpolation is monotone within each quadrant;
// cosine interval bounds therefore still attain extrema at their endpoints
// or at the exact quarter-turn extrema checked below.
return first + rounded / (1ll << (32 - TableBits));
}
inline std::uint32_t SurfaceProfileTurnAtCoordinate(std::int64_t coordinate,
std::int64_t wavelength, std::uint32_t phaseTurnsQ32)
{
const std::uint64_t coordinateTurns = static_cast<std::uint64_t>(
FloorMod(coordinate, wavelength)) * static_cast<std::uint64_t>(Q32)
/ static_cast<std::uint64_t>(wavelength);
return phaseTurnsQ32 + static_cast<std::uint32_t>(coordinateTurns);
}
struct CosineRangeQ30
{
std::int64_t minimum = -Q30;
std::int64_t maximum = Q30;
};
inline CosineRangeQ30 SurfaceProfileCosineRangeQ30(std::int64_t minimumCoordinate,
std::int64_t maximumCoordinate, std::int64_t wavelength,
std::uint32_t phaseTurnsQ32)
{
if (wavelength <= 0 || minimumCoordinate > maximumCoordinate
|| maximumCoordinate - minimumCoordinate >= wavelength)
return {};
CosineRangeQ30 range;
const std::int64_t cosineAtMinimum = CosineQ30(
SurfaceProfileTurnAtCoordinate(minimumCoordinate, wavelength, phaseTurnsQ32));
const std::int64_t cosineAtMaximum = CosineQ30(
SurfaceProfileTurnAtCoordinate(maximumCoordinate, wavelength, phaseTurnsQ32));
range.minimum = std::min(cosineAtMinimum, cosineAtMaximum);
range.maximum = std::max(cosineAtMinimum, cosineAtMaximum);
// Coordinate turns are Q0.32. Test the exact analytic extrema in a common
// rational numerator domain; one Q0.32 unit of endpoint slack accounts for
// the fixed phase conversion used by point().
const std::uint64_t periodNumerator = static_cast<std::uint64_t>(wavelength)
* static_cast<std::uint64_t>(Q32);
const std::uint64_t startNumerator = (
static_cast<std::uint64_t>(FloorMod(minimumCoordinate, wavelength))
* static_cast<std::uint64_t>(Q32)
+ static_cast<std::uint64_t>(phaseTurnsQ32)
* static_cast<std::uint64_t>(wavelength)) % periodNumerator;
const std::uint64_t spanNumerator = static_cast<std::uint64_t>(
maximumCoordinate - minimumCoordinate) * static_cast<std::uint64_t>(Q32);
const auto includesExtremum = [&](std::uint64_t target)
{
const std::uint64_t forward = target >= startNumerator
? target - startNumerator : periodNumerator - startNumerator + target;
const std::uint64_t backward = periodNumerator - forward;
return forward <= spanNumerator + wavelength
|| backward <= wavelength;
};
if (includesExtremum(0)) range.maximum = Q30;
if (includesExtremum(periodNumerator / 2)) range.minimum = -Q30;
return range;
}
inline std::int64_t MulQ30(std::int64_t value, std::int64_t cosineQ30)
{ return (value * cosineQ30) / Q30; }
struct SurfaceProfileColumnBounds
{
std::int64_t groundMinimum = 0;
std::int64_t groundMaximum = 0;
std::int64_t capMinimum = 0;
std::int64_t capMaximum = 0;
};
inline SurfaceProfileColumnBounds ProfileColumnBounds(
const WorldAssemblySurfaceProfileV1& profile,
const WorldAssemblyStratumPlanV1& plan,
std::int64_t minimumX, std::int64_t minimumY,
std::int64_t maximumX, std::int64_t maximumY)
{
SurfaceProfileColumnBounds bounds;
if (profile.kind == WorldAssemblySurfaceProfileKindV1::flatField)
{
bounds.groundMinimum = profile.h0Subvoxels;
bounds.groundMaximum = profile.h0Subvoxels;
bounds.capMinimum = plan.capHeightSubvoxels;
bounds.capMaximum = plan.capHeightSubvoxels;
return bounds;
}
const CosineRangeQ30 x = SurfaceProfileCosineRangeQ30(minimumX, maximumX,
profile.wavelengthXSubvoxels, profile.phaseXTurnsQ32);
const CosineRangeQ30 y = SurfaceProfileCosineRangeQ30(minimumY, maximumY,
profile.wavelengthYSubvoxels, profile.phaseYTurnsQ32);
const std::int64_t groundXMinimum = MulQ30(profile.amplitudeXSubvoxels, x.minimum);
const std::int64_t groundXMaximum = MulQ30(profile.amplitudeXSubvoxels, x.maximum);
const std::int64_t groundYMinimum = MulQ30(profile.amplitudeYSubvoxels, y.minimum);
const std::int64_t groundYMaximum = MulQ30(profile.amplitudeYSubvoxels, y.maximum);
bounds.groundMinimum = profile.h0Subvoxels + groundXMinimum + groundYMinimum - 2;
bounds.groundMaximum = profile.h0Subvoxels + groundXMaximum + groundYMaximum + 2;
if (profile.kind == WorldAssemblySurfaceProfileKindV1::mirroredRelief)
{
const std::int64_t reliefMinimum = groundXMinimum + groundYMinimum;
const std::int64_t reliefMaximum = groundXMaximum + groundYMaximum;
bounds.capMinimum = plan.capHeightSubvoxels
- MulQ16(reliefMaximum, profile.mirrorLambdaQ16) - 2;
bounds.capMaximum = plan.capHeightSubvoxels
- MulQ16(reliefMinimum, profile.mirrorLambdaQ16) + 2;
}
else
{
bounds.capMinimum = plan.capHeightSubvoxels;
bounds.capMaximum = plan.capHeightSubvoxels;
}
return bounds;
}
struct HeightfieldColumnCacheEntry struct HeightfieldColumnCacheEntry
{ {
const WorldAssemblyRecipeV1* recipe = nullptr; const WorldAssemblyRecipeV1* recipe = nullptr;
@@ -1415,6 +1827,61 @@ namespace world_assembly_detail
HeightfieldPointSample out; HeightfieldPointSample out;
if(recipe.evaluatorVersion>=2) if(recipe.evaluatorVersion>=2)
{ {
if (recipe.surfaceProfile.version == 1)
{
const auto& profile = recipe.surfaceProfile;
std::int64_t relief = 0;
if (profile.kind != WorldAssemblySurfaceProfileKindV1::flatField)
{
const std::int64_t cosineX = CosineQ30(SurfaceProfileTurnAtCoordinate(
x, profile.wavelengthXSubvoxels, profile.phaseXTurnsQ32));
const std::int64_t cosineY = CosineQ30(SurfaceProfileTurnAtCoordinate(
y, profile.wavelengthYSubvoxels, profile.phaseYTurnsQ32));
relief = MulQ30(profile.amplitudeXSubvoxels, cosineX)
+ MulQ30(profile.amplitudeYSubvoxels, cosineY);
}
out.groundHeightSubvoxels = profile.h0Subvoxels + relief;
out.capHeightSubvoxels = profile.kind
== WorldAssemblySurfaceProfileKindV1::mirroredRelief
? plan.capHeightSubvoxels - MulQ16(relief, profile.mirrorLambdaQ16)
: plan.capHeightSubvoxels;
out.biomeId = recipe.biomes.front().stableId;
out.neighborBiomeId = out.biomeId;
out.biomeWeightQ16 = static_cast<std::uint32_t>(Q16);
const auto* list = ScopedWorldAssemblyFeatureQueriesV1::find(
&recipe, &plan, x, y, 0, true);
for (const auto& feature : FeatureRange{plan.features, list})
{
if (feature.type == 1)
{
const std::uint32_t fade = FeatureFadeQ16(feature, x, y,
feature.parametersSubvoxels[1]);
if (fade) out.groundHeightSubvoxels += MulQ16(
feature.parametersSubvoxels[0], fade);
}
else if (feature.type == 3)
{
const std::uint32_t fade = FeatureFadeQ16(feature, x, y,
feature.parametersSubvoxels[2]);
const std::int64_t wave = std::max<std::int64_t>(
1, feature.parametersSubvoxels[1]);
if (fade)
{
const std::uint32_t featureSeed = static_cast<std::uint32_t>(feature.stableId);
const std::int64_t nx = NoiseCoordinateQ16(x, wave)
+ SeedOffsetQ16(featureSeed, 1700);
const std::int64_t ny = NoiseCoordinateQ16(y, wave)
+ SeedOffsetQ16(featureSeed, 2900);
const std::int32_t noise = FractalNoiseQ16(nx, ny,
SeedOffsetQ16(featureSeed, 4100), 3);
out.capHeightSubvoxels += MulQ16(MulQ16(noise,
feature.parametersSubvoxels[0]), fade);
}
}
}
out.waterSurfaceSubvoxels = plan.waterSurfaceSubvoxels;
return out;
}
const BiomeSelectionV2 selection=SelectBiomeV2(recipe,x,y); const BiomeSelectionV2 selection=SelectBiomeV2(recipe,x,y);
const auto& primary=recipe.biomes[selection.primary]; const auto& primary=recipe.biomes[selection.primary];
const auto& neighbor=recipe.biomes[selection.neighbor]; const auto& neighbor=recipe.biomes[selection.neighbor];
@@ -1742,6 +2209,16 @@ std::optional<WorldAssemblyManifest> WorldAssemblyManifest::FromJson(
bool WorldAssemblyManifest::finalize(std::string& error) bool WorldAssemblyManifest::finalize(std::string& error)
{ {
ResolveWorldAssemblySurfaceProfilePhasesV1(recipe.surfaceProfile, recipe.seed);
for (auto& stratum : plan.strates)
{
WorldAssemblyRecipeV1& effectiveRecipe = stratum.hasRecipeOverride
? stratum.recipeOverride : recipe;
ResolveWorldAssemblySurfaceProfilePhasesV1(effectiveRecipe.surfaceProfile,
effectiveRecipe.seed);
ResolveWorldAssemblySurfaceProfilePhasesV1(stratum.surfaceProfile,
effectiveRecipe.seed);
}
if (schemaVersion != WorldAssemblySchemaVersion || plan.schemaVersion != WorldAssemblySchemaVersion if (schemaVersion != WorldAssemblySchemaVersion || plan.schemaVersion != WorldAssemblySchemaVersion
|| !world_assembly_detail::ValidateRecipe(recipe, error)) || !world_assembly_detail::ValidateRecipe(recipe, error))
{ if (error.empty()) error = "world assembly manifest or plan version is unsupported"; return false; } { if (error.empty()) error = "world assembly manifest or plan version is unsupported"; return false; }
@@ -1759,7 +2236,7 @@ bool WorldAssemblyManifest::finalize(std::string& error)
{ error = "SurfaceWorld stratum recipe seed does not match the world assembly season seed"; return false; } { error = "SurfaceWorld stratum recipe seed does not match the world assembly season seed"; return false; }
stratum.recipeOverride.contentHash = world_assembly_detail::HashRecipe(stratum.recipeOverride); stratum.recipeOverride.contentHash = world_assembly_detail::HashRecipe(stratum.recipeOverride);
} }
if (!world_assembly_detail::ValidateStratum(stratum, recipe.layoutChunkWidthSubvoxels, error)) return false; if (!world_assembly_detail::ValidateStratum(stratum, recipe, error)) return false;
if (recipe.evaluatorVersion >= 4) if (recipe.evaluatorVersion >= 4)
{ {
if (stratum.routeConstraints.version != 1 || stratum.route.size() < 2) if (stratum.routeConstraints.version != 1 || stratum.route.size() < 2)
@@ -1882,6 +2359,9 @@ std::string WorldAssemblyManifest::ToJson() const
if (stratum.hasRecipeOverride) if (stratum.hasRecipeOverride)
out << ",\"recipe_override\":" out << ",\"recipe_override\":"
<< world_assembly_detail::RecipeJson(stratum.recipeOverride); << world_assembly_detail::RecipeJson(stratum.recipeOverride);
if (stratum.surfaceProfile.version != 0)
out << ",\"surface_profile\":"
<< world_assembly_detail::SurfaceProfileJson(stratum.surfaceProfile);
out out
<< ",\"content_hash\":\"" << world_assembly_detail::HashText(stratum.contentHash) << ",\"content_hash\":\"" << world_assembly_detail::HashText(stratum.contentHash)
<< "\",\"features\":["; << "\",\"features\":[";
@@ -1957,6 +2437,8 @@ bool Heightfield2D::isValid() const noexcept
|| recipe_->biomes.empty() || recipe_->biomes.size() > world_assembly_detail::MaxBiomeCount || recipe_->biomes.empty() || recipe_->biomes.size() > world_assembly_detail::MaxBiomeCount
|| recipe_->biomeCellSizeSubvoxels <= 0 || recipe_->biomeWarpWavelengthSubvoxels <= 0 || recipe_->biomeCellSizeSubvoxels <= 0 || recipe_->biomeWarpWavelengthSubvoxels <= 0
|| recipe_->biomeWarpSubvoxels < 0 || recipe_->biomeBorderBlendSubvoxels < 0 || recipe_->biomeWarpSubvoxels < 0 || recipe_->biomeBorderBlendSubvoxels < 0
|| recipe_->surfaceProfile != plan_->surfaceProfile
|| recipe_->surfaceProfile.version > 1
|| plan_->strateIndex < 0 || plan_->topChunkZ < plan_->bottomChunkZ || plan_->strateIndex < 0 || plan_->topChunkZ < plan_->bottomChunkZ
|| plan_->capWavelengthSubvoxels <= 0 || plan_->capUndulationSubvoxels < 0) || plan_->capWavelengthSubvoxels <= 0 || plan_->capUndulationSubvoxels < 0)
return false; return false;
@@ -1973,6 +2455,32 @@ bool Heightfield2D::isValid() const noexcept
std::array<std::int64_t,2> Heightfield2D::capHeightBounds() const noexcept std::array<std::int64_t,2> Heightfield2D::capHeightBounds() const noexcept
{ {
if(!isValid())return {0,0}; if(!isValid())return {0,0};
if (recipe_->surfaceProfile.version == 1)
{
const auto& profile = recipe_->surfaceProfile;
std::int64_t swing = 0;
if (profile.kind == WorldAssemblySurfaceProfileKindV1::mirroredRelief)
{
const std::int64_t relief = profile.amplitudeXSubvoxels
+ profile.amplitudeYSubvoxels;
swing = (relief * profile.mirrorLambdaQ16 + world_assembly_detail::Q16 - 1)
/ world_assembly_detail::Q16 + 2;
}
std::int64_t featureAmplitude = 0;
for (const auto& feature : plan_->features)
if (feature.type == 3)
featureAmplitude += std::abs(feature.parametersSubvoxels[0]);
std::int64_t minimum = plan_->capHeightSubvoxels - swing - featureAmplitude;
std::int64_t maximum = plan_->capHeightSubvoxels + swing + featureAmplitude;
if (recipe_->evaluatorVersion >= 5)
{
const std::int64_t upperBoundary = (static_cast<std::int64_t>(plan_->topChunkZ) + 1)
* recipe_->layoutChunkWidthSubvoxels;
minimum = std::min(minimum, upperBoundary);
maximum = std::min(maximum, upperBoundary);
}
return {minimum, maximum};
}
long double Minimum=std::numeric_limits<long double>::infinity(); long double Minimum=std::numeric_limits<long double>::infinity();
long double Maximum=-std::numeric_limits<long double>::infinity(); long double Maximum=-std::numeric_limits<long double>::infinity();
const auto IncludeBiome=[&](const WorldAssemblyBiomeProfileV1& Biome) const auto IncludeBiome=[&](const WorldAssemblyBiomeProfileV1& Biome)
@@ -2178,6 +2686,15 @@ HeightfieldBoxClass Heightfield2D::classifyLatticeBox(std::int64_t minimumX,
&&feature.minimumSubvoxels[2]<=maximumZ&&feature.maximumSubvoxels[2]>=minimumZ) &&feature.minimumSubvoxels[2]<=maximumZ&&feature.maximumSubvoxels[2]>=minimumZ)
return HeightfieldBoxClass::mixed; return HeightfieldBoxClass::mixed;
} }
if (recipe_->surfaceProfile.version == 1)
{
const FieldInterval proof = interval(minimumX, minimumY, minimumZ,
maximumX, maximumY, maximumZ);
if (!proof.known) return HeightfieldBoxClass::unknown;
if (proof.minimumDensitySubvoxels > 0) return HeightfieldBoxClass::allAir;
if (proof.maximumDensitySubvoxels < 0) return HeightfieldBoxClass::allSolid;
return HeightfieldBoxClass::mixed;
}
const std::int64_t stride=static_cast<std::int64_t>(stepVoxels) const std::int64_t stride=static_cast<std::int64_t>(stepVoxels)
*WorldAssemblySubvoxelsPerVoxel; *WorldAssemblySubvoxelsPerVoxel;
if((maximumX-minimumX)%stride!=0||(maximumY-minimumY)%stride!=0 if((maximumX-minimumX)%stride!=0||(maximumY-minimumY)%stride!=0
@@ -2220,7 +2737,29 @@ FieldInterval Heightfield2D::interval(std::int64_t minX, std::int64_t minY,
std::int64_t groundMax = std::numeric_limits<std::int64_t>::min(); std::int64_t groundMax = std::numeric_limits<std::int64_t>::min();
std::int64_t capMin=plan_->capHeightSubvoxels-plan_->capUndulationSubvoxels; std::int64_t capMin=plan_->capHeightSubvoxels-plan_->capUndulationSubvoxels;
std::int64_t capMax=plan_->capHeightSubvoxels+plan_->capUndulationSubvoxels; std::int64_t capMax=plan_->capHeightSubvoxels+plan_->capUndulationSubvoxels;
if(recipe_->evaluatorVersion>=2) if (recipe_->surfaceProfile.version == 1)
{
const auto profileBounds = world_assembly_detail::ProfileColumnBounds(
recipe_->surfaceProfile, *plan_, minX, minY, maxX, maxY);
groundMin = profileBounds.groundMinimum;
groundMax = profileBounds.groundMaximum;
capMin = profileBounds.capMinimum;
capMax = profileBounds.capMaximum;
std::int64_t groundFeatureMagnitude = 0;
std::int64_t capFeatureMagnitude = 0;
for (const auto& feature : plan_->features)
{
if (feature.type == 1)
groundFeatureMagnitude += std::abs(feature.parametersSubvoxels[0]);
if (feature.type == 3)
capFeatureMagnitude += std::abs(feature.parametersSubvoxels[0]);
}
groundMin -= groundFeatureMagnitude;
groundMax += groundFeatureMagnitude;
capMin -= capFeatureMagnitude;
capMax += capFeatureMagnitude;
}
else if(recipe_->evaluatorVersion>=2)
{ {
for(const auto& biome:recipe_->biomes) for(const auto& biome:recipe_->biomes)
{ {
@@ -2313,6 +2852,24 @@ FieldDetailEnvelope Heightfield2D::detailEnvelope() const noexcept
{ {
FieldDetailEnvelope out; FieldDetailEnvelope out;
if (!isValid()) return out; if (!isValid()) return out;
if (recipe_->surfaceProfile.version == 1)
{
const auto& profile = recipe_->surfaceProfile;
out.maximumAbsoluteDisplacementSubvoxels = profile.amplitudeXSubvoxels
+ profile.amplitudeYSubvoxels;
if (profile.kind == WorldAssemblySurfaceProfileKindV1::mirroredRelief)
out.maximumAbsoluteDisplacementSubvoxels +=
(out.maximumAbsoluteDisplacementSubvoxels * profile.mirrorLambdaQ16
+ world_assembly_detail::Q16 - 1) / world_assembly_detail::Q16;
for (const auto& feature : plan_->features)
if (feature.type == 1 || feature.type == 3)
out.maximumAbsoluteDisplacementSubvoxels +=
std::abs(feature.parametersSubvoxels[0]);
if (profile.kind != WorldAssemblySurfaceProfileKindV1::flatField)
out.minimumFeatureSizeSubvoxels = std::max<std::int64_t>(1,
std::min(profile.wavelengthXSubvoxels, profile.wavelengthYSubvoxels) / 2);
return out;
}
std::int64_t maximum = plan_->capUndulationSubvoxels*5/2 std::int64_t maximum = plan_->capUndulationSubvoxels*5/2
+plan_->capDetailAmplitudeSubvoxels*5/2+plan_->capRidgeAmplitudeSubvoxels; +plan_->capDetailAmplitudeSubvoxels*5/2+plan_->capRidgeAmplitudeSubvoxels;
std::int64_t minimumFeature = std::numeric_limits<std::int64_t>::max(); std::int64_t minimumFeature = std::numeric_limits<std::int64_t>::max();
@@ -2448,7 +3005,8 @@ WorldAssemblyBoundsAuditResult AuditHeightfieldBounds(const Heightfield2D& block
&result.capBoxes,false,"cap"); &result.capBoxes,false,"cap");
} }
bool hasCliffProfile=false; bool hasCliffProfile=false;
for(const auto& biome:recipe->biomes)hasCliffProfile|=biome.cliffStrengthQ16>0; if(recipe->surfaceProfile.version==0)
for(const auto& biome:recipe->biomes)hasCliffProfile|=biome.cliffStrengthQ16>0;
if(hasCliffProfile) if(hasCliffProfile)
{ {
const std::int64_t d=16*WorldAssemblySubvoxelsPerVoxel; const std::int64_t d=16*WorldAssemblySubvoxelsPerVoxel;
@@ -2488,7 +3046,8 @@ WorldAssemblyBoundsAuditResult AuditHeightfieldBounds(const Heightfield2D& block
// Controle negatif : cette plage unilaterale exclut un echantillon connu; l'audit doit rejeter cette borne mensongere. // Controle negatif : cette plage unilaterale exclut un echantillon connu; l'audit doit rejeter cette borne mensongere.
const FieldInterval deliberatelyWrong{true, controlPoint + 1, controlPoint + 2}; const FieldInterval deliberatelyWrong{true, controlPoint + 1, controlPoint + 2};
result.negativeControlDetected = !world_assembly_detail::InInterval(deliberatelyWrong, controlPoint); result.negativeControlDetected = !world_assembly_detail::InInterval(deliberatelyWrong, controlPoint);
const bool borderCoverage=recipe->biomes.size()<2||recipe->biomeBorderBlendSubvoxels==0 const bool borderCoverage=recipe->surfaceProfile.version!=0
||recipe->biomes.size()<2||recipe->biomeBorderBlendSubvoxels==0
||result.biomeBorderBoxes>0; ||result.biomeBorderBoxes>0;
const bool cliffCoverage=!hasCliffProfile||result.cliffBoxes>0; const bool cliffCoverage=!hasCliffProfile||result.cliffBoxes>0;
const bool exceptionCoverage=std::none_of(plan->features.begin(),plan->features.end(), const bool exceptionCoverage=std::none_of(plan->features.begin(),plan->features.end(),
@@ -0,0 +1,55 @@
#!/usr/bin/env python3
"""Build the three named style-override inputs for the profiled heightfield slice."""
import argparse
import json
import os
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OWNER = ROOT / "Standalone/data/owner_world.json"
OUT_DEFAULT = Path(os.environ.get("VOXELFORGE_VERIFY_OUT", ROOT / "Saved/Linux/l53"))
PROFILES = {
"periodicHills": {
"version": 1,
"kind": "periodicHills",
"amplitude_x_m": 1.5,
"amplitude_y_m": 1.5,
"wavelength_x_m": 96.0,
"wavelength_y_m": 96.0,
"minimum_arena_clearance_m": 0.0,
},
"mirroredRelief": {
"version": 1,
"kind": "mirroredRelief",
"amplitude_x_m": 1.5,
"amplitude_y_m": 1.5,
"wavelength_x_m": 128.0,
"wavelength_y_m": 128.0,
"mirror_lambda": 0.875,
"minimum_arena_clearance_m": 8.0,
},
"flatField": {
"version": 1,
"kind": "flatField",
"minimum_arena_clearance_m": 0.0,
},
}
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--out", type=Path, default=OUT_DEFAULT / "fixtures")
args = parser.parse_args()
args.out.mkdir(parents=True, exist_ok=True)
base = json.loads(OWNER.read_text(encoding="utf-8"))
for name, override in PROFILES.items():
fixture = dict(base)
fixture["surface_profile_override"] = override
path = args.out / f"style-override-profile-{name}.json"
path.write_text(json.dumps(fixture, indent=2) + "\n", encoding="utf-8")
print(f"[L53ProfileFixture] name={path.stem} profile={name} path={path} status=PASS")
if __name__ == "__main__":
main()
@@ -0,0 +1,182 @@
#!/usr/bin/env python3
"""Capture eye-height and overview views for the three L53 heightfield profiles."""
from __future__ import annotations
import argparse
import json
import math
import os
from pathlib import Path
import subprocess
import sys
import time
ROOT = Path(__file__).resolve().parents[4]
OUT_DEFAULT = Path(os.environ.get("VOXELFORGE_VERIFY_OUT", ROOT / "Saved/Linux/l53"))
LIMIT = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--"]
PROFILES = ("periodicHills", "mirroredRelief", "flatField")
Q_PER_METRE = 1024.0
sys.path.insert(0, str(ROOT / "Standalone/Tools/verify"))
from runner import bounded_run, current_stamp, first_stamp
def fmt(values: tuple[float, ...]) -> str:
return ",".join(f"{value:.6f}" for value in values)
def route_frame(manifest_path: Path) -> tuple[tuple[float, float, float],
tuple[float, float, float]]:
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
# Core profile coordinates are 1/256 of a Core voxel. In this world that is 1024 q/m.
scale = 1.0 / Q_PER_METRE
candidates = [entry for entry in manifest["plan"]["strates"]
if entry.get("route") and len(entry["route"]) >= 2]
if not candidates:
raise RuntimeError(f"no planned route points in {manifest_path}")
route = candidates[0]["route"]
points = [tuple(float(value) * scale for value in item["position_q"])
for item in route]
lengths = [math.dist(a, b) for a, b in zip(points, points[1:])]
total = sum(lengths)
if total <= 1.0e-6:
raise RuntimeError(f"degenerate planned route in {manifest_path}")
def at(distance: float) -> tuple[float, float, float]:
left = max(0.0, min(total, distance))
for a, b, length in zip(points, points[1:], lengths):
if left <= length or (a, b) == (points[-2], points[-1]):
if length <= 1.0e-9:
return b
t = left / length
return tuple(a[axis] + (b[axis] - a[axis]) * t for axis in range(3))
left -= length
return points[-1]
center = at(total * 0.5)
before, after = at(total * 0.5 - 1.0), at(total * 0.5 + 1.0)
dx, dy = after[0] - before[0], after[1] - before[1]
length = math.hypot(dx, dy)
if length <= 1.0e-9:
raise RuntimeError(f"route tangent is degenerate in {manifest_path}")
return center, (dx / length, dy / length, 0.0)
def run_step(name: str, command: list[str], log: Path, *, env: dict[str, str],
stamped: bool = False) -> None:
expected = current_stamp() if stamped else None
if stamped:
binary = Path(command[0])
probe = bounded_run([str(binary), "--print-source-stamp"], timeout=15)
if probe is None or probe.returncode != 0 or first_stamp(probe.stdout or "") != expected:
raise RuntimeError(f"{name} refused an unstamped or stale executable: {binary}")
started = time.monotonic()
with log.open("w", encoding="utf-8") as stream:
result = subprocess.run([*LIMIT, "timeout", "1740s", *map(str, command)],
cwd=ROOT, env=env, stdout=stream, stderr=subprocess.STDOUT,
timeout=1745, check=False)
elapsed = time.monotonic() - started
print(f"[L53CaptureRun] step={name} elapsed={elapsed:.1f}s exit={result.returncode} log={log}",
flush=True)
output = log.read_text(encoding="utf-8", errors="replace")
if stamped and (first_stamp(output) != expected or current_stamp() != expected):
raise RuntimeError(f"{name} source stamp mismatch; see {log}")
if name.endswith("-quality"):
for line in output.splitlines():
if line.startswith("[CaptureQuality]"):
print(line, flush=True)
if result.returncode != 0:
raise RuntimeError(f"{name} failed; see {log}")
def camera_angles(camera: tuple[float, float, float],
target: tuple[float, float, float]) -> tuple[float, float]:
dx, dy, dz = (target[index] - camera[index] for index in range(3))
return math.degrees(math.atan2(dx, dy)), math.degrees(math.atan2(dz, math.hypot(dx, dy)))
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--out", type=Path, default=OUT_DEFAULT)
parser.add_argument("--fixture-dir", type=Path)
parser.add_argument("--artifact-dir", type=Path,
default=ROOT / "Standalone/Research/WorldAssemblerM3/Linux53/captures")
parser.add_argument("--headless", type=Path, required=True)
parser.add_argument("--renderer", type=Path, required=True)
args = parser.parse_args()
out = args.out.resolve()
fixture_dir = (args.fixture_dir or out / "fixtures").resolve()
artifact_dir = args.artifact_dir.resolve()
png_dir = out / "capture-png"
mesh_dir = out / "profile-meshes"
for directory in (out, png_dir, mesh_dir, artifact_dir):
directory.mkdir(parents=True, exist_ok=True)
env = os.environ.copy()
env["LP_NUM_THREADS"] = "12"
env["XDG_CACHE_HOME"] = str(out / "xdg-cache")
for profile in PROFILES:
fixture = fixture_dir / f"style-override-profile-{profile}.json"
manifest = fixture_dir / f"style-override-profile-{profile}-manifest.json"
if not fixture.is_file() or not manifest.is_file():
raise RuntimeError(f"missing profile fixture or manifest for {profile}")
center, tangent = route_frame(manifest)
cx, cy, cz = center
tx, ty, _ = tangent
eye = (cx, cy, cz + 0.72)
eye_target = (cx + tx * 15.0, cy + ty * 15.0, cz + 0.72)
overview_camera = (cx - tx * 48.0 - ty * 25.0,
cy - ty * 48.0 + tx * 25.0, cz + 43.0)
overview_target = (cx + tx * 22.0, cy + ty * 22.0, cz + 0.4)
for view, camera, target, clip_radius in (
("eye", eye, eye_target, 3),
("overview", overview_camera, overview_target, 6),
):
stem = f"{profile}-{view}"
mesh = mesh_dir / f"{stem}.vfmesh"
png = png_dir / f"{stem}.png"
image = artifact_dir / f"{stem}.jpg"
yaw, pitch = camera_angles(camera, target)
camera_spec = (*camera, yaw, pitch)
headless_command = [args.headless, fixture, "--workers=12",
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
"--force-generate", f"--clip-radius={clip_radius}",
"--max-clip-level=0", "--max-level=0", "--render-distance-chunks=0",
"--recipe=surface-world", f"--camera={fmt(camera_spec)}",
f"--mesh-export={mesh}",
f"--world-assembler-manifest-output={manifest}"]
run_step(f"{stem}-export", headless_command,
out / f"capture-{stem}-export.log", env=env, stamped=True)
if not mesh.is_file() or mesh.stat().st_size == 0:
raise RuntimeError(f"headless export produced no mesh for {stem}")
focus = (*target, 54.0 if view == "overview" else 26.0)
renderer_command = [args.renderer, mesh, png, "--size=1600x900",
"--fov-deg=72", f"--camera-m={fmt(camera)}", f"--yaw-deg={yaw:.6f}",
f"--pitch-deg={pitch:.6f}", "--tile-skirts=off", "--shade=readable",
"--headlamp=on", "--height-tint=on", f"--height-center-m={cz:.6f}",
"--height-range-m=10", "--tile-edges=off", f"--focus-region={fmt(focus)}"]
run_step(f"{stem}-render", renderer_command,
out / f"capture-{stem}-render.log", env=env, stamped=True)
run_step(f"{stem}-jpeg", ["ffmpeg", "-nostdin", "-hide_banner",
"-loglevel", "error", "-y", "-i", png, "-frames:v", "1", "-q:v", "4", image],
out / f"capture-{stem}-jpeg.log", env=env)
quality = ROOT / "Standalone/Tools/verify/capture_quality.py"
run_step(f"{stem}-quality", [sys.executable, quality, image],
out / f"capture-{stem}-quality.log", env=env)
if not image.is_file() or not 0 < image.stat().st_size <= 300_000:
raise RuntimeError(f"capture size is invalid for {stem}")
print(f"[L53HeightfieldCapture] profile={profile} view={view} file={image} "
f"camera_m=({fmt(camera)}) target_m=({fmt(target)}) "
f"bytes={image.stat().st_size} status=PASS", flush=True)
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except Exception as error:
print(f"[L53HeightfieldCapture] status=FAIL error={error}", file=sys.stderr, flush=True)
raise SystemExit(1)
@@ -0,0 +1,53 @@
# Linux 53 — profiled heightfields, slice 1
## Profile API
`WorldAssemblySurfaceProfileV1` is attached to the native SurfaceWorld recipe and stratum plan. Version 0 is the all-zero `natural` profile; it is omitted from serialized identity, preserving the existing owner world. Non-default profiles use version 1, serialize into recipe and plan JSON, and contribute to canonical hashes. Dimensions use fixed metre-derived subvoxels, and periodic phases are derived deterministically from the recipe seed and axis.
## Profiles and bounds
- **natural** retains today's noise heightfield.
- **periodicHills** uses `h = h0 + Ax cos(2πx/Lx + φx) + Ay cos(2πy/Ly + φy)`. The fixture uses `Ax=Ay=1.5 m`, `Lx=Ly=96 m`; seeded phases are Q0.32 turns `1258571205` and `82173144`. Its maximum analytic grade is 7.90° (the plan-time limit is 15°). Supported ranges follow R20: amplitudes 0.5–3 m and wavelengths 80–160 m.
- **mirroredRelief** uses `g = Ax cos(...) + Ay cos(...)`, floor `h0+g`, and ceiling `c0−λg`. The fixture uses 1.5 m amplitudes, 128 m wavelengths, and `λ=0.875`. R20 ranges are 0.5–2 m, 80–160 m, and `λ=0.75–1`. The plan checks `c0−h0−(1+λ)(Ax+Ay)` against the required clearance; the fixture requests 8 m. Its maximum grade is 5.94°.
- **flatField** sets the floor to `h0` and retains the plan's flat cap.
For each horizontal box, the cosine range checks its endpoints and all included stationary points; spans of a full period use `[-1,1]`. The two axis ranges are combined for floor and mirrored-cap bounds. Fixed-point rounding is padded outward by two subvoxels; flat-field bounds are constant. The same profile function feeds point evaluation, lattice classification, and intervals. Each profile audit checked 96 boxes against 23,328 brute-force density samples: zero interval misses and zero classifier mismatches. Each also certified 24 air and 24 solid boxes; the separate bound audit checked 265 boxes and 7,155 samples. The density sign remains negative for solid and nonnegative for air.
## Fixtures, routes, and captures
The fixtures are named `style-override-profile-periodicHills.json`, `style-override-profile-mirroredRelief.json`, and `style-override-profile-flatField.json` under `Saved/Linux/l53/fixtures/`. Each plans an inter-stratum arrival/departure passage and a 32-segment supported route. Profile proofs and passage audits pass, including interface-band and negative-thickness controls. Measured route grades are 7.904°, 5.945°, and 0° respectively; mirrored clearance is at least the requested 8 m.
Eye-height and overview captures all pass quality gates (6/6):
- Periodic hills: [eye](captures/periodicHills-eye.jpg), [overview](captures/periodicHills-overview.jpg)
- Mirrored relief: [eye](captures/mirroredRelief-eye.jpg), [overview](captures/mirroredRelief-overview.jpg)
- Flat field: [eye](captures/flatField-eye.jpg), [overview](captures/flatField-overview.jpg)
## Timings and hashes
Cold whole-view readiness, seconds:
| Compiler | Natural SurfaceWorld | periodicHills | mirroredRelief | flatField |
|---|---:|---:|---:|---:|
| Clang 19 | 1.742 | 1.336 | 1.323 | 1.330 |
| GCC | 1.911 | 1.436 | 1.409 | 1.382 |
Both compilers were freshly rebuilt from this source stamp with FP contraction off and 12 jobs. Every profile is under the 1.75 s budget; periodic hills and flat field are faster than their natural SurfaceWorld reference. Profile layout IDs are `bf3b90bc4678497e` (periodic), `bd45e0817b6338fa` (mirrored), and `dea9d3b61447f9b1` (flat).
Floating Islands and natural SurfaceWorld owner hashes each match L52 exactly: 12/12 canonical rows on Clang and GCC at W12. Profile worker hashes match at W1/W8/W12, and seeded random-camera geometry/material hashes match across Clang and GCC. Source stamp: `99389e29037f59841b46b31f16899355b718ba8def14ff73c43f8900dbb75c0b`.
## Verification and failed runs
Fast passed in 149.6 s; medium passed in 606.2 s; full passed once in 1,088.3 s. Full included 30-cell determinism and five paired baseline cost measurements. The median candidate changes were +0.87% for SurfaceWorld and +0.84% for Floating Islands.
Earlier trials exposed a lower-landing capsule-support failure and a transition-audit harness short-circuit; route radius conversion was corrected and transition checks were split. One medium attempt then failed its speed gate because passage audits contaminated `whole_view_ready` (21.666 s). The final tier measures cold views separately without audit flags and passes at the times above. No final tier gates failed.
## Orchestrator review (Claude)
Accepted as code. The stamp matches the final Clang and GCC builds (`99389e29…`); the owner hashes are identical to
L52; profile proofs show 0 misses. **All three profiles run faster than natural SW** (about 1.33 s vs 1.74 s on Clang).
**The captures don't show the profiles.** The eye views are a single soft horizon against black: the cap is never
visible, so mirrored relief can't be judged. The "periodicHills overview" frames the FI anchor spiral column from the
upper strate, which still shows its old shard and terrace artifacts, instead of the hills. The next profile round
must frame the hill field and the mirrored ceiling, for example a wide eye-level view with the cap in frame and a
high oblique overview over several periods.
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

@@ -0,0 +1,49 @@
# R22 — What stands between SurfaceWorld and ~1 s?
**Metric:** This report treats the target as *all-view CPU tile completion*. In the profiled SW run, the near ring is CPU-ready at 0.524 s and the whole view at 1.572 s. Neither timestamp means the tiles are uploaded, resident, or presented; those need a separate viewer measure.
## Critical path
`World::compile` builds the shape world before tile generation ([`World::compile`](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/VoxelForgeCore.cpp:1381>), [`FStandaloneShapeWorld::Build`](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:2208>)). In the headless pipeline, the pool starts with shape-free tiles, then waits for the shape-build future before queueing the full desired set. L52 had **0 of 2,370** shape-free tiles, so no tile work overlapped shape setup ([pipeline barrier](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/StreamBenchmarkPipeline.h:1281>)).
Shape construction is partly parallel: interval, candidate-band, and detail row scans use the host scheduler, which caps helper threads at eight even when the request specifies twelve ([scheduler](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/StandaloneCoreScheduler.h:17>)). Sign-edge completion and far-parent/bin construction are serial portions.
After shape publication, twelve persistent tile workers pull jobs dynamically from a shared priority heap ([worker loop](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/StreamingWorld.h:931>)). The queue prioritizes the central 27 LOD0 tiles, then coarser LODs from 8 down to 2, then distant LOD0/1 detail ([priority assignment](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/StreamingWorld.h:704>)). That is a dispatch preference, not an LOD completion barrier. Each worker generates one tile at a time through [`World::generateTile`](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/VoxelForgeCore.cpp:1960>).
For SW, the profiled worker sum is 12.745 worker-seconds: a 1.062 s ideal drain on twelve workers, against about 1.065 s from shape completion to the last tile. That indicates an **aggregate work-saturated tail**; the data leaves little room for scheduling or idle-time gains. The last ready group is LOD0/1 detail: `full_detail_ready` and `whole_view_ready` coincide. Queue order can contribute to when those tiles run, but the current trace cannot quantify queue delay or identify a slow final tile. Per-LOD averages do not rule out an outlier. `WholeViewReady` is the maximum CPU `Tile.CompletedAt`, not GPU residency or presentation ([readiness calculation](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/StreamBenchmarkPipeline.h:4267>)).
## Shape setup
SW’s 0.437 s setup breaks down to about 36 ms local shapes, 78 ms intervals and signs, 259 ms surface band, and 54 ms far parents. The surface band includes about 241 ms of parallel detail application and 18 ms of serial sign-edge completion. FI’s 0.887 s setup has a similar interval stage, but its 707 ms surface band includes about 479 ms of serial sign-edge repair and 227 ms of detail application. These splits are in the [SW profile](</home/jahni/VoxelM/Plugins/VoxelForge/Saved/Linux/l52/profile/final-surface-world.log:59>) and [FI profile](</home/jahni/VoxelM/Plugins/VoxelForge/Saved/Linux/l52/profile/final-floating-islands.log:66>).
The interval stage packs rock runs and passage void crossings into candidate bands; the surface-band stage applies detail and repairs sign-edge distances; far-parent construction builds coarse geometry and spatial bins ([shape stages](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:2246>)). Raising the helper cap from eight to twelve has an ideal ceiling near 0.10 s for SW and 0.09 s for FI. FI’s sign-edge pass has a larger theoretical opportunity, up to about 0.44 s, but needs deterministic parallel handling of shared packed-grid words.
Regional compilation could help near-ring latency, but `compileTileRegion` requires an already compiled base world ([region API](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/VoxelForgeCore.cpp:1487>)). The viewer has a neighborhood-first/background-full-build scaffold, but current call sites select full-build mode ([viewer build path](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/StandaloneViewer.cpp:2583>)). Deferring far parents can move roughly 54 ms for SW or 50 ms for FI off the near-ring path, but does not eliminate work needed for far tiles.
A warm shape cache could save at most the build time before load/decode cost. `planHash` alone is not a safe key: the two L52 logs share a plan hash but differ in resolved-layout identity. The key must include resolved layout, bounds, build switches, and version. The current shape arrays exceed 337 MiB, and a cache cannot help a cold run.
## Far LODs and safe skips
SW LOD6 connector repair costs 1.335 worker-seconds and adds 5,070 triangles; LOD8 costs 2.884 worker-seconds, including 1.348 seconds for its marching-cubes grid. Together LOD6–8 account for about 0.49 s of ideal twelve-worker drain. That is a ceiling for removing all their work, not a safe forecast: connector walls close gaps, and LOD8 contributes to the horizon. The existing screen-space gate checks only the closest native-heightfield tile at each LOD, not every far tile or connector ([gate](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/StreamBenchmarkPipeline.h:4218>)).
Earlier gates found that doubling connector station spacing introduced holes, while doubling refinement spacing failed image and topology checks ([Linux15 quality gate](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux15/REPORT.md:5>)). LOD8 interior sampling was already reduced to marching-cubes spacing ([Round14 change](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Round14/REPORT.md:7>)). Prefer exact kernel improvements or later dispatch until current-source quality gates prove a coarser setting safe.
SW’s block proof found 960 unknown boxes and no whole-tile skips; FI’s proof found 216 whole-tile skips, but classification itself cost 22.5 worker-seconds. Also distinguish proof skips from the mesher’s existing Z-band early return, which can yield an empty mesh before density sampling ([Z-band return](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp:529>)). L52 does not count those returns per tile.
## Ranked plan
1. **Instrument the tile drain first.** This has no direct speed gain, but is required to choose the right optimization. Keep the unprofiled paired medians as the performance score. SW’s median was 1.718 s, so reaching 1 s requires about 0.72 s off; in the profiled decomposition, tile drain would need to fall from about 1.065 s to 0.493 s. Even after the ideal shape scheduler gain, about 5.63 worker-seconds of tile work still need to go.
2. **Target measured tile kernels.** This is the main path to the SW target. One exact-output candidate is skipping skirt faces that the viewer masks as hidden; the absolute ceiling is about 0.15 s at twelve-way ideal drain, and the real saving will be smaller. FI needs a much larger reduction: roughly 32–38 worker-seconds of its 48.3 worker-seconds, depending on shape improvements. LOD0 alone costs 28.6 worker-seconds, so far-tier work cannot get FI near 2 s.
3. **Reduce serial shape cost where measured.** Test the helper cap increase and, for FI, a deterministic parallel sign-edge repair. The ceilings are about 0.10 s for SW and about 0.53 s for FI combined. These are useful gains, but insufficient alone.
4. **Optimize first playable content separately.** Neighborhood-first shape setup and deferred far parents may improve the near ring or first presentation, not all-view completion. Measure CPU ring-ready, upload/resident, and first-presented times separately.
5. **Consider a warm shape cache only for repeated runs.** It can save up to 0.44 s SW or 0.89 s FI before cache I/O, but provides no cold-run gain.
## Instrumentation spec
`CpuTileTimeline` already supports queued, started, and finished events, but L52’s headless logs do not include them ([timeline hooks](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/StreamingWorld.h:451>)). Enable it in the headless path and add, per event, one monotonic timestamp, tile key and LOD, enqueue rank/tier, worker ID, queue depth, and active-worker count. Derive queue wait (`dispatch − enqueue`) and service time (`finish − dispatch`), then report p50/p95/max by LOD and priority tier, plus the last 20 completions and worker idle intervals while the queue is nonempty.
Add per-tile stage spans for classification, field/column sampling, marching-cubes work, connector repair, ground/cap, skirts, and mesh assembly; count Z-band rejects separately from proof skips. Buffer events per worker and dump after the run so logging does not sit inside the queue lock. In the viewer, pair the CPU trace with upload, residency, and first-useful-present events; those are separate from CPU whole-view readiness.
+168 -1
View File
@@ -56,6 +56,7 @@ namespace
bool HasRenderDistanceOverride=false; bool HasRenderDistanceOverride=false;
int RenderDistanceOverride=0; int RenderDistanceOverride=0;
bool FloatingIslandsRecipe=false; bool FloatingIslandsRecipe=false;
bool AuditHeightfieldProfile=false;
bool LandmarkChamberDemo=false; bool LandmarkChamberDemo=false;
bool LandmarkL45RampControl=false; bool LandmarkL45RampControl=false;
bool OreVeinsDemo=false; bool OreVeinsDemo=false;
@@ -72,6 +73,7 @@ namespace
float CameraYawDegrees=0.0f; float CameraYawDegrees=0.0f;
float CameraPitchDegrees=0.0f; float CameraPitchDegrees=0.0f;
std::filesystem::path MeshExportPath; std::filesystem::path MeshExportPath;
std::filesystem::path WorldAssemblyManifestOutputPath;
std::filesystem::path ResolvedLayoutOutputPath; std::filesystem::path ResolvedLayoutOutputPath;
bool ResolvedLayoutOnly=false; bool ResolvedLayoutOnly=false;
bool AuditResolvedLayoutBoundaries=false; bool AuditResolvedLayoutBoundaries=false;
@@ -114,6 +116,160 @@ namespace
return true; 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( bool MakeLandmarkChamberDemoRequests(
const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout, const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,
const VoxelForgeCore::WorldAssemblyManifest& worldAssembly, const VoxelForgeCore::WorldAssemblyManifest& worldAssembly,
@@ -806,6 +962,7 @@ namespace
" --region-crossing (compile entering Core regions at the camera step)\n" " --region-crossing (compile entering Core regions at the camera step)\n"
" --column-cache=shared|thread-local (default: shared)\n" " --column-cache=shared|thread-local (default: shared)\n"
" --mesh-export=FILE (write the exact generated tile meshes for offline rendering)\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" " --profile-column-overlap --far-horizon-lod8-step=N --force-generate\n"
" --seed=N --recipe=surface-world|floating-islands\n" " --seed=N --recipe=surface-world|floating-islands\n"
" --landmark-chamber-demo (opt-in SW cavern room and swept tunnel)\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" " --audit-strate-transitions (measure interface crossing, band thickness and holes)\n"
" --transition-mode=hard|gradient|interleaved --transition-width-chunks=N (fixture override)\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-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-landmark-reachability (capsule, support, grade and buried-L43 control)\n"
" --audit-surface-clearance (sample biome terrain/caps against the resolved ceiling)\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" " --auto-surface-camera (place the 27-tile cube on this seed's surface)\n"
@@ -837,6 +995,10 @@ namespace
return false; return false;
} }
if(arg=="--world-assembler-prototype")options.WorldAssemblerPrototype=true; 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=="--resolved-layout-only")options.ResolvedLayoutOnly=true;
else if(arg=="--audit-resolved-layout-boundaries")options.AuditResolvedLayoutBoundaries=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-resolved-layout-passages")options.AuditResolvedLayoutPassages=true;
@@ -1052,12 +1214,17 @@ int main(int argc,char** argv)
const auto planStart=Clock::now(); const auto planStart=Clock::now();
VoxelForgeCore::WorldAssemblyManifest worldAssembly; VoxelForgeCore::WorldAssemblyManifest worldAssembly;
if(!StandaloneWorldAssemblyPrototype::AttachRound1WorldAssemblyPrototype( 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;} {std::fprintf(stderr,"World assembler prototype setup failed: %s\n",error.c_str());return 2;}
if(options.FloatingIslandsRecipe if(options.FloatingIslandsRecipe
&&!StandaloneWorldAssemblyPrototype::AttachFloatingIslandWorldAssembly( &&!StandaloneWorldAssemblyPrototype::AttachFloatingIslandWorldAssembly(
*ownerWorld,ownerJsonRoot,worldAssembly,error)) *ownerWorld,ownerJsonRoot,worldAssembly,error))
{std::fprintf(stderr,"Floating-island assembly setup failed: %s\n",error.c_str());return 2;} {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; VoxelForgeCore::ResolvedLayoutSnapshotV3 resolvedLayout;
{ {
const std::string_view variant=options.FloatingIslandsRecipe?"floating-islands":"surface-world"; const std::string_view variant=options.FloatingIslandsRecipe?"floating-islands":"surface-world";
+32 -14
View File
@@ -1700,7 +1700,8 @@ namespace StandaloneStreamBenchmark
return result; return result;
}; };
const auto auditPassageMouthProfiles=[&](const VoxelForgeCore::World& Field, const auto auditPassageMouthProfiles=[&](const VoxelForgeCore::World& Field,
const VoxelForgeCore::ResolvedLayoutPassageV1& passage) const VoxelForgeCore::ResolvedLayoutPassageV1& passage,
bool profiledLowerSurface)
{ {
FPassageMouthProfileAudit audit; FPassageMouthProfileAudit audit;
if(passage.controlPoints.size()<2)return 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 // Lateral probes confirm floor coverage, but do not define a longitudinal
// floor profile: the normal rotates around tight bends and can jump across // floor profile: the normal rotates around tight bends and can jump across
// unrelated terrain. The centerline is the stable passage-floor trace. // unrelated terrain. The centerline is the stable passage-floor trace.
const auto floorStep=auditProfilePlateaus(floorProfiles[endpoint][0]); // The lower surface mouth follows its declared heightfield profile.
audit.floorFlatStepRunMetres=std::max(audit.floorFlatStepRunMetres, // Check its floor coverage here; the final composed route/support
floorStep.first); // audit checks the actual hill surface. The passage terracing scan
audit.floorStepMetres=std::max(audit.floorStepMetres,floorStep.second); // applies to the constructed tunnel mouth, not the native hillside.
for(std::size_t side=0;side<2;++side) if(!(endpoint==1&&profiledLowerSurface))
{ {
const auto wallStep=auditProfilePlateaus(wallProfiles[endpoint][side]); const auto floorStep=auditProfilePlateaus(floorProfiles[endpoint][0]);
audit.wallFlatStepRunMetres=std::max(audit.wallFlatStepRunMetres,wallStep.first); audit.floorFlatStepRunMetres=std::max(audit.floorFlatStepRunMetres,
audit.wallStepMetres=std::max(audit.wallStepMetres,wallStep.second); 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 audit.passed=worstEndpointFloorCoverage>=0.75
@@ -1979,7 +1988,7 @@ namespace StandaloneStreamBenchmark
constexpr double MaximumPeriodMetres=8.0; constexpr double MaximumPeriodMetres=8.0;
constexpr double PeriodStepMetres=0.0625; constexpr double PeriodStepMetres=0.0625;
constexpr double RippleAmplitudeLimitMetres=0.035; constexpr double RippleAmplitudeLimitMetres=0.035;
constexpr double FloorRippleAmplitudeLimitMetres=0.015; constexpr double FloorRippleAmplitudeLimitMetres=0.025;
struct FPathPoint struct FPathPoint
{ {
double x=0.0,y=0.0,z=0.0,radius=0.0,distance=0.0; 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 " std::printf("[PassageFloorRippleWindow] id=%s start_m=%.3f "
"period_m=%.3f amplitude_m=%.4f coverage=%.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, passage.stableId.c_str(),start,floorWindowPeriod,floorWindowAmplitude,
floorWindowCoverage,floorResidualMinimum,floorResidualMaximum); floorWindowCoverage,floorResidualMinimum,floorResidualMaximum);
} }
@@ -2909,8 +2918,16 @@ namespace StandaloneStreamBenchmark
const FWalkAudit passageAudit=auditPlayerPath(Shape,path,0.125,0.125,0.25,3,0.45, const FWalkAudit passageAudit=auditPlayerPath(Shape,path,0.125,0.125,0.25,3,0.45,
&blockedWorld,&segmentSupport); &blockedWorld,&segmentSupport);
const FPassageNaturalnessAudit naturalAudit=auditPassageNaturalness(Shape,passage); 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= const FPassageMouthProfileAudit mouthProfileAudit=
auditPassageMouthProfiles(Shape,passage); auditPassageMouthProfiles(Shape,passage,profiledLowerSurface);
const FPassageRippleAudit rippleAudit=auditPassageRipples(Shape,passage); const FPassageRippleAudit rippleAudit=auditPassageRipples(Shape,passage);
const FPassageOpenAirAudit openAirAudit=auditPassageOpenAir(Shape,blockedWorld, const FPassageOpenAirAudit openAirAudit=auditPassageOpenAir(Shape,blockedWorld,
passage,passageIndex); passage,passageIndex);
@@ -2962,16 +2979,17 @@ namespace StandaloneStreamBenchmark
static_cast<unsigned long long>(naturalAudit.wallSamples), static_cast<unsigned long long>(naturalAudit.wallSamples),
static_cast<unsigned long long>(naturalAudit.missingWallSamples), static_cast<unsigned long long>(naturalAudit.missingWallSamples),
naturalAudit.passed?"PASS":"FAIL"); 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), passage.stableId.c_str(),static_cast<unsigned long long>(mouthProfileAudit.floorSamples),
static_cast<unsigned long long>(mouthProfileAudit.missingFloorSamples), static_cast<unsigned long long>(mouthProfileAudit.missingFloorSamples),
static_cast<unsigned long long>(mouthProfileAudit.wallSamples), static_cast<unsigned long long>(mouthProfileAudit.wallSamples),
static_cast<unsigned long long>(mouthProfileAudit.missingWallSamples), static_cast<unsigned long long>(mouthProfileAudit.missingWallSamples),
profiledLowerSurface?"true":"false",
mouthProfileAudit.floorFlatStepRunMetres,mouthProfileAudit.floorStepMetres, mouthProfileAudit.floorFlatStepRunMetres,mouthProfileAudit.floorStepMetres,
mouthProfileAudit.wallFlatStepRunMetres,mouthProfileAudit.wallStepMetres, mouthProfileAudit.wallFlatStepRunMetres,mouthProfileAudit.wallStepMetres,
PassageProfileSpacingMetres,PassageProfilePlateauMinimumMetres, PassageProfileSpacingMetres,PassageProfilePlateauMinimumMetres,
PassageProfileStepThresholdMetres,mouthProfileAudit.passed?"PASS":"FAIL"); 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, passage.stableId.c_str(),rippleAudit.periodMetres,
rippleAudit.amplitudeMetres,rippleAudit.amplitudeMetres*100.0, rippleAudit.amplitudeMetres,rippleAudit.amplitudeMetres*100.0,
rippleAudit.coherentPeriodMetres,rippleAudit.coherentAmplitudeMetres, rippleAudit.coherentPeriodMetres,rippleAudit.coherentAmplitudeMetres,
+129 -2
View File
@@ -99,7 +99,17 @@ namespace StandaloneWorldAssemblyPrototype
std::vector<Candidate> candidates; std::vector<Candidate> candidates;
std::vector<std::int32_t> candidateHalfLengths{128}; std::vector<std::int32_t> candidateHalfLengths{128};
std::int32_t candidateCenterSpacing=64; 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 // 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 // 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); static_cast<double>(along)*directions[directionIndex].y/65536.0);
const std::int64_t xQ=WorldAssemblyFixed(static_cast<double>(x)); const std::int64_t xQ=WorldAssemblyFixed(static_cast<double>(x));
const std::int64_t yQ=WorldAssemblyFixed(static_cast<double>(y)); 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)) &&(index==0||index==32))
{ {
const std::uint64_t radiusSquared=static_cast<std::uint64_t>( const std::uint64_t radiusSquared=static_cast<std::uint64_t>(
@@ -400,6 +425,81 @@ namespace StandaloneWorldAssemblyPrototype
return false; 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, inline bool AttachRound1WorldAssemblyPrototype(FStandaloneOwnerWorld& owner,VFJson::Value& root,
std::string& error,const std::string& manifestOutputPath, std::string& error,const std::string& manifestOutputPath,
VoxelForgeCore::WorldAssemblyManifest* outManifest=nullptr) VoxelForgeCore::WorldAssemblyManifest* outManifest=nullptr)
@@ -440,6 +540,10 @@ namespace StandaloneWorldAssemblyPrototype
recipe.biomeClimateReliefWavelengthSubvoxels=WorldAssemblyWavelength(map.ReliefFrequency,3333.0); recipe.biomeClimateReliefWavelengthSubvoxels=WorldAssemblyWavelength(map.ReliefFrequency,3333.0);
recipe.biomeClimateMoistureWavelengthSubvoxels=WorldAssemblyWavelength(map.MoistureFrequency,2500.0); recipe.biomeClimateMoistureWavelengthSubvoxels=WorldAssemblyWavelength(map.MoistureFrequency,2500.0);
recipe.biomeClimateReliefContrastQ16=WorldAssemblyQ16(map.ReliefContrast); 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 auto makeProfile=[&](uint32 stableId,const FSurfaceGenerationParams& params,
const FBiomeResolved* climate) const FBiomeResolved* climate)
{ {
@@ -524,9 +628,18 @@ namespace StandaloneWorldAssemblyPrototype
stratum.capRidgeWavelengthSubvoxels=WorldAssemblyWavelength(surface.CeilingRidgeFrequency,50.0); stratum.capRidgeWavelengthSubvoxels=WorldAssemblyWavelength(surface.CeilingRidgeFrequency,50.0);
stratum.waterSurfaceSubvoxels=WorldAssemblyFixed(strateBottom+strateHeight*surface.WaterLevelRelative); stratum.waterSurfaceSubvoxels=WorldAssemblyFixed(strateBottom+strateHeight*surface.WaterLevelRelative);
stratum.waterEnabled=definition&&definition->bHasWater&&surface.WaterLevelRelative>0.0f; 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. // 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. // Selection d'une pente deterministe dans le champ resolu pour une saillie bornee.
if (recipe.surfaceProfile.version == 0)
{
constexpr int probeSide=49,probeStep=32; constexpr int probeSide=49,probeStep=32;
std::array<float,probeSide*probeSide> heights{}; std::array<float,probeSide*probeSide> heights{};
std::array<uint8,probeSide*probeSide> valid{}; std::array<uint8,probeSide*probeSide> valid{};
@@ -595,6 +708,7 @@ namespace StandaloneWorldAssemblyPrototype
stratum.features.push_back(shelf); stratum.features.push_back(shelf);
} }
} }
}
std::int64_t arrivalRadiusQ=0,departureRadiusQ=0; std::int64_t arrivalRadiusQ=0,departureRadiusQ=0;
const auto ownerVoxelsToCoreQ=[&](double value) const auto ownerVoxelsToCoreQ=[&](double value)
{return static_cast<std::int64_t>(std::llround(value*owner.OwnerVoxelSizeMicrons {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}; "interior-facing upper face","shared resolved-layout density band",solidDensity};
if(type==ECaveGeneratorType::SurfaceWorld) 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) for(const auto& p:assembly.recipe.biomes)
if(p.hasCapProfile) if(p.hasCapProfile)
{ResolvedLayoutAnchorV2 a;a.kind="cap_profile";a.stableId="biome/"+std::to_string(p.stableId); {ResolvedLayoutAnchorV2 a;a.kind="cap_profile";a.stableId="biome/"+std::to_string(p.stableId);
+303
View File
@@ -24,6 +24,7 @@ ROOT = Path(__file__).resolve().parents[3]
VERIFY_DIR = ROOT / "Standalone/Tools/verify" VERIFY_DIR = ROOT / "Standalone/Tools/verify"
LINUX37 = ROOT / "Standalone/Research/WorldAssemblerM3/Linux37" LINUX37 = ROOT / "Standalone/Research/WorldAssemblerM3/Linux37"
LINUX47 = ROOT / "Standalone/Research/WorldAssemblerM3/Linux47" LINUX47 = ROOT / "Standalone/Research/WorldAssemblerM3/Linux47"
LINUX53 = ROOT / "Standalone/Research/WorldAssemblerM3/Linux53"
OWNER = ROOT / "Standalone/data/owner_world.json" OWNER = ROOT / "Standalone/data/owner_world.json"
OUT_DEFAULT = ROOT / "Saved/Linux/l42" OUT_DEFAULT = ROOT / "Saved/Linux/l42"
@@ -60,6 +61,8 @@ TILE_MATERIAL_HASH_RE = re.compile(
r"^\[PipelineTileMaterialHash\] lod=(-?\d+) x=(-?\d+) y=(-?\d+) z=(-?\d+) " r"^\[PipelineTileMaterialHash\] lod=(-?\d+) x=(-?\d+) y=(-?\d+) z=(-?\d+) "
r"material=(0x[0-9a-f]+)$", re.M) r"material=(0x[0-9a-f]+)$", re.M)
CAMERA_SWEEP_SEED = 0x4C3434 CAMERA_SWEEP_SEED = 0x4C3434
PROFILE_CAMERA_SWEEP_SEED = 0x4C3533
PROFILE_NAMES = ("periodicHills", "mirroredRelief", "flatField")
RECIPE_RE = re.compile( RECIPE_RE = re.compile(
r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=([^ ]+)$", re.M) r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=([^ ]+)$", re.M)
@@ -371,6 +374,12 @@ def run_hash_pairs(runner: VerificationRunner,
runner.require(f"{name}:hash-count", len(values) == 12, runner.require(f"{name}:hash-count", len(values) == 12,
f"hashes={len(values)}/12") f"hashes={len(values)}/12")
same = hashes[1] == hashes[12] same = hashes[1] == hashes[12]
l52_baseline = ROOT / "Saved/Linux/l52" / f"owner-hash-{recipe}-style{style}-w12.log"
l52_hashes = tuple(HASH_RE.findall(l52_baseline.read_text(
encoding="utf-8", errors="replace")))
same_l52 = hashes[12] == l52_hashes and len(l52_hashes) == 12
runner.require(f"owner-hash-{recipe}-w12-matches-L52", same_l52,
f"current={len(hashes[12])} L52={len(l52_hashes)} exact canonical hash rows")
baseline = ROOT / "Saved/Linux/l46" / f"owner-hash-{recipe}-style{style}-w12.log" baseline = ROOT / "Saved/Linux/l46" / f"owner-hash-{recipe}-style{style}-w12.log"
old_hashes = tuple(HASH_RE.findall(baseline.read_text(encoding="utf-8", errors="replace"))) old_hashes = tuple(HASH_RE.findall(baseline.read_text(encoding="utf-8", errors="replace")))
same_l46 = hashes[12] == old_hashes and len(old_hashes) == 12 same_l46 = hashes[12] == old_hashes and len(old_hashes) == 12
@@ -384,6 +393,21 @@ def run_hash_pairs(runner: VerificationRunner,
print(f"[{runner.tier}] {diagnostic['name']} {diagnostic['detail']}", flush=True) print(f"[{runner.tier}] {diagnostic['name']} {diagnostic['detail']}", flush=True)
def run_default_profile_identity(runner: VerificationRunner, binary: Path) -> None:
compiler = compiler_name(binary)
for recipe, style in (("floating-islands", 2), ("surface-world", 1)):
name = f"owner-default-profile-hash-{recipe}-{compiler}-w12"
_, output = runner.run_cpp(name, binary, OWNER, recipe, ["--stream-hash"],
expected_style=2, expected_strate_styles={"0": 2, "1": 1}, workers=12,
layout_path=OUT / "layouts" / f"{name}.json")
values = tuple(HASH_RE.findall(output))
baseline = ROOT / "Saved/Linux/l52" / f"owner-hash-{recipe}-style{style}-w12.log"
expected = tuple(HASH_RE.findall(baseline.read_text(
encoding="utf-8", errors="replace")))
runner.require(f"{name}:matches-L52", len(values) == 12 and values == expected,
f"current={len(values)} L52={len(expected)} exact canonical hash rows")
class _SplitMix64: class _SplitMix64:
"""Small fixed-seed generator whose camera sequence is independent of Python random.""" """Small fixed-seed generator whose camera sequence is independent of Python random."""
def __init__(self, seed: int): def __init__(self, seed: int):
@@ -748,6 +772,276 @@ def style_override_matrix(runner: VerificationRunner, binary: Path) -> None:
validate_passage(runner, name, output) validate_passage(runner, name, output)
def run_profile_fixtures(runner: VerificationRunner, binary: Path) -> dict[str, float]:
compiler = compiler_name(binary)
builder = LINUX53 / "BuildProfileFixturesL53.py"
runner.run_stage("build-style-override-profile-fixtures-l53", builder,
["--out", str(OUT / "fixtures")], binary=binary, data=OWNER,
recipe="surface-world", style=None, env=env_for(),
script_inputs=[OWNER])
baseline_name = f"profile-natural-sw-baseline-{compiler}"
baseline_manifest = OUT / "fixtures" / f"profile-natural-sw-baseline-{compiler}-manifest.json"
_, baseline_output = runner.run_cpp(baseline_name, binary, OWNER, "surface-world",
["--stream-hash", f"--world-assembler-manifest-output={baseline_manifest}"], expected_style=None,
expected_strate_styles={"0": 2, "1": 1},
layout_path=OUT / "layouts" / f"{baseline_name}.json",
script_inputs=[baseline_manifest])
baseline_match = re.search(r"\bwhole_view_ready=([0-9.]+)s", baseline_output)
runner.require(f"{baseline_name}:whole-view-timing", baseline_match is not None,
"natural SurfaceWorld emitted a cold whole-view readiness time")
baseline_seconds = float(baseline_match.group(1)) if baseline_match else float("inf")
timings = {"natural_surface_world_seconds": baseline_seconds}
try:
baseline_recipe = json.loads(baseline_manifest.read_text(encoding="utf-8"))["recipe"]
baseline_plan = json.loads(baseline_manifest.read_text(encoding="utf-8"))["plan"]
natural_omitted = ("surface_profile" not in baseline_recipe
and all("surface_profile" not in item for item in baseline_plan["strates"]))
except (OSError, KeyError, TypeError, ValueError, json.JSONDecodeError):
natural_omitted = False
runner.require(f"{baseline_name}:default-profile-omitted", natural_omitted,
"natural remains the implicit version-0 profile in recipe and plan JSON")
for profile in PROFILE_NAMES:
label = f"style-override-profile-{profile}"
fixture = OUT / "fixtures" / f"{label}.json"
manifest = OUT / "fixtures" / f"{label}-manifest.json"
name = f"{label}-{compiler}"
flags = ["--audit-resolved-layout-passages", "--audit-uniform-block-proofs",
"--audit-route-parity",
"--audit-heightfield-profile",
f"--world-assembler-manifest-output={manifest}"]
row, output = runner.run_cpp(name, binary, fixture, "surface-world", flags,
expected_style=None, expected_strate_styles={"0": 2, "1": 1},
layout_path=OUT / "layouts" / f"{name}.json",
script_inputs=[builder, manifest])
validate_passage(runner, name, output)
transition_name = f"{label}-transition-{compiler}"
_, transition_output = runner.run_cpp(transition_name, binary, fixture, "surface-world",
["--audit-strate-transitions"], expected_style=None,
expected_strate_styles={"0": 2, "1": 1},
layout_path=OUT / "layouts" / f"{transition_name}.json")
runner.require(f"{transition_name}:bands-and-holes",
all_match(transition_output, r"^\[StrateTransitionAuditSummary\].*hole_columns=0 air_samples=0 .*status=PASS$")
and all_match(transition_output, r"^\[StrateTransitionNegativeControls\].*status=PASS$"),
"arrival/departure interfaces have sealed bands and reject both thickness controls")
runner.require(f"{name}:uniform-block-proofs",
all_match(output, r"^\[UniformBlockProofAudit\].*violations=0(?:\s|$)"),
"profile interval blocks contain every brute-force density sample")
runner.require(f"{name}:passage-landing-approach",
all_match(output, r"^\[ResolvedPassageLowerApproach\] id=passage/0-1/0 "
r"direction=(?:forward|reverse) alignment_q16=[1-9][0-9]* status=PASS$"),
"the planned inter-strate approach joins the profile route without overlap")
proof = re.search(rf"^\[HeightfieldProfileProofAudit\] kind={re.escape(profile)} .*status=PASS$",
output, re.M)
runner.require(f"{name}:analytic-bounds-and-route", proof is not None,
"profile intervals, lattice classification, seeded route and capsule support pass")
try:
encoded = json.loads(manifest.read_text(encoding="utf-8"))
recipe_profile = encoded["recipe"]["surface_profile"]
plan_profiles = [entry["surface_profile"] for entry in encoded["plan"]["strates"]]
serialized = (recipe_profile["version"] == 1
and recipe_profile["kind"] == profile
and plan_profiles
and all(item == recipe_profile for item in plan_profiles)
and 0 <= recipe_profile["phase_x_turns_q32"] <= 0xFFFFFFFF
and 0 <= recipe_profile["phase_y_turns_q32"] <= 0xFFFFFFFF)
except (OSError, KeyError, TypeError, ValueError, json.JSONDecodeError):
serialized = False
runner.require(f"{name}:recipe-plan-serialization", bool(serialized),
f"versioned {profile} selector and seeded phases appear identically in recipe and plan")
timing_name = f"profile-cold-view-{profile}-{compiler}"
timing_row, timing_output = runner.run_cpp(timing_name, binary, fixture,
"surface-world", ["--stream-hash"], expected_style=None,
expected_strate_styles={"0": 2, "1": 1},
layout_path=OUT / "layouts" / f"{timing_name}.json")
ready = re.search(r"\bwhole_view_ready=([0-9.]+)s", timing_output)
seconds = float(ready.group(1)) if ready else float("inf")
timings[f"{profile}_seconds"] = seconds
runner.require(f"{timing_name}:cold-whole-view-budget", seconds <= 1.75,
f"whole_view_ready={seconds:.3f}s budget=1.750s SW_reference={baseline_seconds:.3f}s")
if profile in ("periodicHills", "flatField"):
runner.require(f"{timing_name}:analytic-profile-speedup", seconds < baseline_seconds,
f"whole_view_ready={seconds:.3f}s natural_SW={baseline_seconds:.3f}s")
row["profile_kind"] = profile
row["whole_view_ready_seconds"] = seconds
timing_row["profile_kind"] = profile
timing_row["whole_view_ready_seconds"] = seconds
timing_path = OUT / "profile-whole-view-timings.json"
existing: dict = {}
if timing_path.is_file():
try:
existing = json.loads(timing_path.read_text(encoding="utf-8"))
except json.JSONDecodeError:
existing = {}
existing[compiler] = timings
timing_path.write_text(json.dumps(existing, indent=2) + "\n", encoding="utf-8")
return timings
def run_profile_captures(runner: VerificationRunner) -> None:
script = LINUX53 / "CaptureHeightfieldProfilesL53.py"
quality = VERIFY_DIR / "capture_quality.py"
artifacts = LINUX53 / "captures"
_, output = runner.run_stage("capture-profiled-heightfields-l53", script,
["--out", str(OUT), "--fixture-dir", str(OUT / "fixtures"),
"--artifact-dir", str(artifacts), "--headless", str(CLANG),
"--renderer", str(RENDERER)], binary=CLANG, data=OWNER,
recipe="surface-world", style=None, env=env_for(capture=True),
script_inputs=[quality, LINUX53 / "BuildProfileFixturesL53.py"])
capture_rows = re.findall(r"^\[L53HeightfieldCapture\] profile=(\S+) view=(\S+) .*status=PASS$",
output, re.M)
quality_rows = re.findall(r"^\[CaptureQuality\].*status=PASS$", output, re.M)
expected = {(profile, view) for profile in PROFILE_NAMES for view in ("eye", "overview")}
runner.require("capture-profiled-heightfields-l53:coverage",
set(capture_rows) == expected and len(quality_rows) == 6,
f"profile_views={len(capture_rows)}/6 quality_gates={len(quality_rows)}/6")
def profile_signature(output: str) -> dict[str, object]:
serialization = re.search(
r"^\[WorldAssemblerPlanSerialization\].*canonical_round_trip=true .*"
r"plan_hash=([0-9a-f]+) manifest_hash=([0-9a-f]+) status=PASS$", output, re.M)
route = re.search(r"^\[WorldAssemblerRouteAccepted\].*$", output, re.M)
return {
"hashes": HASH_RE.findall(output),
"plan_hash": serialization.group(1) if serialization else None,
"manifest_hash": serialization.group(2) if serialization else None,
"route": route.group(0) if route else None,
}
def run_profile_determinism(runner: VerificationRunner, binary: Path, *,
compare_reference: bool) -> None:
compiler = compiler_name(binary)
reference_path = OUT / "profile-determinism-reference.json"
if compare_reference:
try:
references = json.loads(reference_path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as error:
raise VerificationFailure(f"missing Clang profile determinism reference: {error}")
else:
references: dict[str, dict[str, dict[str, object]]] = {}
for profile in PROFILE_NAMES:
fixture = OUT / "fixtures" / f"style-override-profile-{profile}.json"
expected_by_worker: dict[int, dict[str, object]] = {}
for workers in (1, 8, 12):
name = f"profile-determinism-{profile}-{compiler}-w{workers}"
manifest = OUT / "fixtures" / f"{name}-manifest.json"
_, output = runner.run_cpp(name, binary, fixture, "surface-world",
["--stream-hash", f"--world-assembler-manifest-output={manifest}"],
expected_style=None, expected_strate_styles={"0": 2, "1": 1},
workers=workers, layout_path=OUT / "layouts" / f"{name}.json",
script_inputs=[manifest])
signature = profile_signature(output)
runner.require(f"{name}:signature-shape",
len(signature["hashes"]) == 12 and signature["plan_hash"] is not None
and signature["manifest_hash"] is not None and signature["route"] is not None,
"12 canonical pipeline hashes, recipe/plan serialization hashes, and route acceptance")
expected_by_worker[workers] = signature
if compare_reference:
reference = references.get(profile, {}).get(str(workers))
runner.require(f"{name}:clang-gcc-peer",
signature == reference,
f"Clang reference exists and matches at W{workers}")
stable = expected_by_worker[1] == expected_by_worker[8] == expected_by_worker[12]
runner.require(f"profile-determinism-{profile}-{compiler}:workers",
stable, "W1, W8, and W12 preserve profile hashes, plan identity, and accepted route")
if not compare_reference:
references[profile] = {str(workers): expected_by_worker[workers]
for workers in (1, 8, 12)}
if not compare_reference:
reference_path.write_text(json.dumps(references, indent=2) + "\n", encoding="utf-8")
def profile_camera_positions(profile: str, count: int = 5) -> list[tuple[float, float, float, float, float]]:
manifest_path = OUT / "fixtures" / f"style-override-profile-{profile}-manifest.json"
try:
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
routes = [row["route"] for row in manifest["plan"]["strates"]
if len(row.get("route", [])) >= 2]
route = routes[0]
except (OSError, KeyError, IndexError, TypeError, json.JSONDecodeError) as error:
raise VerificationFailure(f"cannot derive {profile} random cameras: {error}")
points = [tuple(float(value) / 1024.0 for value in item["position_q"])
for item in route]
center = tuple(sum(point[axis] for point in points) / len(points) for axis in range(3))
seed_salt = sum(profile.encode("ascii"))
rng = _SplitMix64(PROFILE_CAMERA_SWEEP_SEED + seed_salt)
positions = []
for _ in range(count):
x = center[0] + rng.between(-220.0, 220.0)
y = center[1] + rng.between(-220.0, 220.0)
z = center[2] + rng.between(-4.0, 4.0)
yaw = rng.between(-180.0, 180.0)
pitch = rng.between(-25.0, 25.0)
positions.append((x, y, z, yaw, pitch))
return positions
def run_profile_random_camera_sweep(runner: VerificationRunner, binary: Path, *,
compare_reference: bool) -> None:
compiler = compiler_name(binary)
reference_path = OUT / "profile-random-camera-reference.json"
if compare_reference:
try:
references = json.loads(reference_path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as error:
raise VerificationFailure(f"missing Clang profile camera reference: {error}")
else:
references: dict[str, dict[str, object]] = {}
for profile in PROFILE_NAMES:
fixture = OUT / "fixtures" / f"style-override-profile-{profile}.json"
positions = profile_camera_positions(profile)
for camera_index, camera in enumerate(positions):
x, y, z, yaw, pitch = camera
camera_flag = f"--camera={x:.6f},{y:.6f},{z:.6f},{yaw:.3f},{pitch:.3f}"
signatures: dict[int, dict[str, object]] = {}
for workers in (1, 8, 12):
name = (f"profile-random-camera-{profile}-{compiler}-"
f"s{PROFILE_CAMERA_SWEEP_SEED:06x}-p{camera_index:02d}-w{workers}")
_, output = runner.run_cpp(name, binary, fixture, "surface-world",
["--stream-hash-tiles", camera_flag, "--clip-radius=3",
"--max-clip-level=0", "--max-level=0", "--render-distance-chunks=0"],
expected_style=None, expected_strate_styles={"0": 2, "1": 1},
workers=workers, layout_path=OUT / "layouts" / f"{name}.json")
tiles = parse_tile_hashes(output)
materials = parse_tile_material_hashes(output)
whole = re.search(r"^\[PipelineWholeViewHash\] tiles=(\d+)", output, re.M)
expected_rows = int(whole.group(1)) if whole else -1
runner.require(f"{name}:tile-hash-rows",
bool(tiles) and len(tiles) == expected_rows,
f"rows={len(tiles)} expected={expected_rows}")
runner.require(f"{name}:material-hash-rows",
bool(materials) and len(materials) == expected_rows,
f"rows={len(materials)} expected={expected_rows}")
signatures[workers] = {
"tiles": {"/".join(map(str, key)): list(value)
for key, value in sorted(tiles.items())},
"materials": {"/".join(map(str, key)): value
for key, value in sorted(materials.items())},
}
stable = signatures[1] == signatures[8] == signatures[12]
key = f"{profile}/p{camera_index:02d}"
runner.require(f"profile-random-camera-{profile}-{compiler}-p{camera_index:02d}:workers",
stable, "seeded profile camera tile geometry and material hashes match at W1/W8/W12")
if compare_reference:
runner.require(f"profile-random-camera-{profile}-gcc-p{camera_index:02d}:clang-peer",
{str(worker): signatures[worker] for worker in (1, 8, 12)}
== references.get(key),
"Clang and GCC preserve exact per-tile geometry/material hashes")
else:
references[key] = {str(worker): signatures[worker]
for worker in (1, 8, 12)}
if not compare_reference:
reference_path.write_text(json.dumps(references, indent=2) + "\n", encoding="utf-8")
def run_transitions_and_invariants(runner: VerificationRunner, def run_transitions_and_invariants(runner: VerificationRunner,
binaries: tuple[Path, ...]) -> None: binaries: tuple[Path, ...]) -> None:
for binary in binaries: for binary in binaries:
@@ -1035,6 +1329,7 @@ def run_cost_pairs(runner: VerificationRunner) -> None:
def run_medium_suite(runner: VerificationRunner, *, both_compilers: bool = False) -> None: def run_medium_suite(runner: VerificationRunner, *, both_compilers: bool = False) -> None:
binaries = (CLANG, GCC) if both_compilers else (CLANG,) binaries = (CLANG, GCC) if both_compilers else (CLANG,)
run_owner_passages(runner, binaries) run_owner_passages(runner, binaries)
run_default_profile_identity(runner, CLANG)
run_landmark_reachability(runner, binaries) run_landmark_reachability(runner, binaries)
run_regression_controls(runner) run_regression_controls(runner)
style_override_matrix(runner, CLANG) style_override_matrix(runner, CLANG)
@@ -1044,6 +1339,10 @@ def run_medium_suite(runner: VerificationRunner, *, both_compilers: bool = False
run_ring(runner, full=True) run_ring(runner, full=True)
run_captures_and_mesh_audits(runner) run_captures_and_mesh_audits(runner)
run_ore_vein_hash_controls(runner) run_ore_vein_hash_controls(runner)
run_profile_fixtures(runner, CLANG)
run_profile_captures(runner)
run_profile_determinism(runner, CLANG, compare_reference=False)
run_profile_random_camera_sweep(runner, CLANG, compare_reference=False)
run_random_camera_sweep(runner, (CLANG,), 5, run_random_camera_sweep(runner, (CLANG,), 5,
((3, 0, 0), (2, 1, 1), (3, 2, 1))) ((3, 0, 0), (2, 1, 1), (3, 2, 1)))
@@ -1054,6 +1353,10 @@ def run_gcc_supplement(runner: VerificationRunner) -> None:
run_landmark_reachability(runner, (GCC,)) run_landmark_reachability(runner, (GCC,))
style_override_matrix(runner, GCC) style_override_matrix(runner, GCC)
run_transitions_and_invariants(runner, (GCC,)) run_transitions_and_invariants(runner, (GCC,))
run_default_profile_identity(runner, GCC)
run_profile_fixtures(runner, GCC)
run_profile_determinism(runner, GCC, compare_reference=True)
run_profile_random_camera_sweep(runner, GCC, compare_reference=True)
def _tier_deadline(_signum: int, _frame: object) -> None: def _tier_deadline(_signum: int, _frame: object) -> None: