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

+30 -1
View File
@@ -1993,6 +1993,10 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
&&anchor.values.size()>=3;});
const bool hasLowerTangent=hasLowerRoute&&!hasPassageArrival
&&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,
placementRadius-mouthRadius-qPerMetre/2);
@@ -2237,7 +2241,7 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
helixEndIndex=xy.size()-1;
hasAnchorHelix=helixEndIndex>0;
}
const std::int64_t routeGradeQ16=hasAnchorHelix
const std::int64_t routeGradeQ16=profiledSurfaceJoin?8000:hasAnchorHelix
&&lower.generatorKind=="SurfaceWorld"?8000
:style==0&&anchoredIslandPassage&&!anchoredSpiral?15500:TargetGradeQ16;
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,
upper.strateIndex,lower.strateIndex,connection,0x4c505248u)%16);
std::int64_t bestAlignment=std::numeric_limits<std::int64_t>::min();
bool lowerRouteReversed=false;
const std::uint32_t hashedLowerPhase=lowerPhase;
for(std::uint32_t offset=0;offset<16;++offset)
{
@@ -2326,8 +2331,32 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
if(score>bestAlignment)
{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())
{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)
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 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 MaxSerializedMagnitude = 1000000000000ll;
constexpr std::size_t MaxBiomeCount = 8;
@@ -228,6 +234,114 @@ namespace world_assembly_detail
inline bool OutsideRange(std::int64_t value)
{ 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)
{
if (recipe.schemaVersion != WorldAssemblySchemaVersion
@@ -296,16 +410,57 @@ namespace world_assembly_detail
|| recipe.biomeClimateMoistureWavelengthSubvoxels <= 0
|| recipe.biomeClimateReliefContrastQ16 < 0))
{ 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;
}
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
|| stratum.capWavelengthSubvoxels <= 0 || stratum.capUndulationSubvoxels < 0
|| stratum.features.size() > MaxFeatureCount || stratum.route.size() > MaxRoutePointCount)
{ 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)
|| !ValidScalar(stratum.capWavelengthSubvoxels)
|| !ValidScalar(stratum.capDetailAmplitudeSubvoxels)
@@ -466,6 +621,8 @@ namespace world_assembly_detail
HashWord(hash, static_cast<std::uint64_t>(value), 8);
}
}
if (recipe.surfaceProfile.version != 0)
HashSurfaceProfile(hash, recipe.surfaceProfile);
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);
HashWord(hash, static_cast<std::uint64_t>(point.radiusSubvoxels), 8);
}
if (stratum.surfaceProfile.version != 0)
HashSurfaceProfile(hash, stratum.surfaceProfile);
return hash;
}
@@ -627,6 +786,59 @@ namespace world_assembly_detail
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,
std::string& error)
{
@@ -647,6 +859,10 @@ namespace world_assembly_detail
|| !ReadInteger(value.Get("climate_moisture_wavelength_q"), recipe.biomeClimateMoistureWavelengthSubvoxels)
|| !ReadInteger(value.Get("climate_relief_contrast_q16"), recipe.biomeClimateReliefContrastQ16)))
{ 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())
{
WorldAssemblyBiomeProfileV1 biome;
@@ -709,6 +925,8 @@ namespace world_assembly_detail
out << ",\"climate_relief_wavelength_q\":" << recipe.biomeClimateReliefWavelengthSubvoxels
<< ",\"climate_moisture_wavelength_q\":" << recipe.biomeClimateMoistureWavelengthSubvoxels
<< ",\"climate_relief_contrast_q16\":" << recipe.biomeClimateReliefContrastQ16;
if (recipe.surfaceProfile.version != 0)
out << ",\"surface_profile\":" << SurfaceProfileJson(recipe.surfaceProfile);
out << ",\"content_hash\":\"" << HashText(recipe.contentHash) << "\",\"biomes\":[";
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; }
if(hasWaterBoolean)stratum.waterEnabled=waterEnabled.Boolean(false);
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");
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)));
}
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
{
const WorldAssemblyRecipeV1* recipe = nullptr;
@@ -1415,6 +1827,61 @@ namespace world_assembly_detail
HeightfieldPointSample out;
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 auto& primary=recipe.biomes[selection.primary];
const auto& neighbor=recipe.biomes[selection.neighbor];
@@ -1742,6 +2209,16 @@ std::optional<WorldAssemblyManifest> WorldAssemblyManifest::FromJson(
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
|| !world_assembly_detail::ValidateRecipe(recipe, error))
{ 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; }
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 (stratum.routeConstraints.version != 1 || stratum.route.size() < 2)
@@ -1882,6 +2359,9 @@ std::string WorldAssemblyManifest::ToJson() const
if (stratum.hasRecipeOverride)
out << ",\"recipe_override\":"
<< world_assembly_detail::RecipeJson(stratum.recipeOverride);
if (stratum.surfaceProfile.version != 0)
out << ",\"surface_profile\":"
<< world_assembly_detail::SurfaceProfileJson(stratum.surfaceProfile);
out
<< ",\"content_hash\":\"" << world_assembly_detail::HashText(stratum.contentHash)
<< "\",\"features\":[";
@@ -1957,6 +2437,8 @@ bool Heightfield2D::isValid() const noexcept
|| recipe_->biomes.empty() || recipe_->biomes.size() > world_assembly_detail::MaxBiomeCount
|| recipe_->biomeCellSizeSubvoxels <= 0 || recipe_->biomeWarpWavelengthSubvoxels <= 0
|| recipe_->biomeWarpSubvoxels < 0 || recipe_->biomeBorderBlendSubvoxels < 0
|| recipe_->surfaceProfile != plan_->surfaceProfile
|| recipe_->surfaceProfile.version > 1
|| plan_->strateIndex < 0 || plan_->topChunkZ < plan_->bottomChunkZ
|| plan_->capWavelengthSubvoxels <= 0 || plan_->capUndulationSubvoxels < 0)
return false;
@@ -1973,6 +2455,32 @@ bool Heightfield2D::isValid() const noexcept
std::array<std::int64_t,2> Heightfield2D::capHeightBounds() const noexcept
{
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 Maximum=-std::numeric_limits<long double>::infinity();
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)
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)
*WorldAssemblySubvoxelsPerVoxel;
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 capMin=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)
{
@@ -2313,6 +2852,24 @@ FieldDetailEnvelope Heightfield2D::detailEnvelope() const noexcept
{
FieldDetailEnvelope 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
+plan_->capDetailAmplitudeSubvoxels*5/2+plan_->capRidgeAmplitudeSubvoxels;
std::int64_t minimumFeature = std::numeric_limits<std::int64_t>::max();
@@ -2448,7 +3005,8 @@ WorldAssemblyBoundsAuditResult AuditHeightfieldBounds(const Heightfield2D& block
&result.capBoxes,false,"cap");
}
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)
{
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.
const FieldInterval deliberatelyWrong{true, controlPoint + 1, controlPoint + 2};
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;
const bool cliffCoverage=!hasCliffProfile||result.cliffBoxes>0;
const bool exceptionCoverage=std::none_of(plan->features.begin(),plan->features.end(),