Files
VoxelForge/Standalone/Core/src/WorldAssemblerInternal.h
T
Fr0zkaandClaude Opus 5.5 87b0e5d8cd wip(assembler): round S13 -- tile cost counted per LOD (diagnostics only, no output change)
Exclusive per-stage tile timers and probes. Findings: LOD0 is MC-exclusive
(10.4 of 14 ms p95, 243 ns/sample); LOD4's 3x cost is column-cache misses
(8,224 vs ~1,600-1,900 at LOD3/5), cap refine secondary; per-tile feature lists
cost ~0.001 ms; cross-worker column repeats common (277k at LOD0). Global octave
drop and stride-2 interpolation probes fail the 2 cm gate. Worker cap 1 kept
(cap 0 worse 40 m/s tick tail). Report: Standalone/Research/WorldAssemblerM3/Round17.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-19 15:40:25 +02:00

2534 lines
136 KiB
C++

#pragma once
#include "Json.h"
#include "VoxelForgeCore/VoxelForgeCore.h"
#include <algorithm>
#include <bit>
#include <charconv>
#include <chrono>
#include <cmath>
#include <iomanip>
#include <limits>
#include <sstream>
#include <stdexcept>
namespace VoxelForgeCore
{
namespace
{
thread_local const ScopedWorldAssemblyFeatureQueriesV1* ActiveAssemblyQueries = nullptr;
}
ScopedWorldAssemblyFeatureQueriesV1::ScopedWorldAssemblyFeatureQueriesV1(
std::span<const WorldAssemblyFeatureQueryV1> queries, bool forceCandidatesForValidation) noexcept
: queries_(queries), forceCandidates_(forceCandidatesForValidation), previous_(ActiveAssemblyQueries)
{
// Resolve cost hints once per domain, never rediscover them in the sample loop.
// Resoudre les indications de cout une fois par domaine, hors de la boucle d'echantillons.
ActiveAssemblyQueries = nullptr;
for (const auto& query : queries_)
if (forceCandidates_ || query.features.candidateEvaluationUseful)
{ ActiveAssemblyQueries = this; break; }
}
ScopedWorldAssemblyFeatureQueriesV1::~ScopedWorldAssemblyFeatureQueriesV1()
{ ActiveAssemblyQueries = previous_; }
const WorldAssemblyFeatureTileListV1* ScopedWorldAssemblyFeatureQueriesV1::find(
const void* recipe, const void* plan, std::int64_t x, std::int64_t y,
std::int64_t z, bool columnOnly) noexcept
{
const std::array<std::int64_t, 3> point{x,y,z};
if (ActiveAssemblyQueries)
for (const auto& query : ActiveAssemblyQueries->queries_)
{
if (query.recipe != recipe || query.plan != plan
|| (!ActiveAssemblyQueries->forceCandidates_ && !query.features.candidateEvaluationUseful)) continue;
bool inside = true;
for (std::size_t axis=0; axis<(columnOnly ? 2u : 3u); ++axis)
inside = inside && point[axis]>=query.features.minimumSubvoxels[axis]
&& point[axis]<=query.features.maximumSubvoxels[axis];
if (inside) return &query.features;
}
return nullptr;
}
std::vector<std::uint32_t> BuildWorldAssemblyFeatureTileListV1(
std::span<const WorldAssemblyFeatureBoundsV1> features,
const std::array<std::int64_t, 3>& tileMinimumSubvoxels,
const std::array<std::int64_t, 3>& tileMaximumSubvoxels)
{
if (features.size() > std::numeric_limits<std::uint32_t>::max())
throw std::length_error("world-assembly feature list exceeds 32-bit indexing");
const auto allFeatures = [&]()
{
std::vector<std::uint32_t> result;
result.reserve(features.size());
for (std::size_t index = 0; index < features.size(); ++index)
result.push_back(static_cast<std::uint32_t>(index));
return result;
};
for (std::size_t axis = 0; axis < 3; ++axis)
if (tileMinimumSubvoxels[axis] > tileMaximumSubvoxels[axis]) return allFeatures();
for (const auto& feature : features)
for (std::size_t axis = 0; axis < 3; ++axis)
if (feature.minimumSubvoxels[axis] > feature.maximumSubvoxels[axis]) return allFeatures();
std::vector<std::uint32_t> result;
result.reserve(features.size());
for (std::size_t index = 0; index < features.size(); ++index)
{
const auto& feature = features[index];
bool intersects = true;
for (std::size_t axis = 0; axis < 3; ++axis)
intersects = intersects
&& feature.minimumSubvoxels[axis] <= tileMaximumSubvoxels[axis]
&& feature.maximumSubvoxels[axis] >= tileMinimumSubvoxels[axis];
if (intersects) result.push_back(static_cast<std::uint32_t>(index));
}
return result;
}
WorldAssemblyFeatureColumnRangeV1 BuildWorldAssemblyFeatureColumnRangeV1(
std::span<const WorldAssemblyFeatureBoundsV1> bounds, std::int64_t x, std::int64_t y)
{
WorldAssemblyFeatureColumnRangeV1 result;
result.known=true;
result.empty=true;
for(const auto& bound:bounds)
{
for(std::size_t axis=0;axis<3;++axis)
if(bound.minimumSubvoxels[axis]>bound.maximumSubvoxels[axis]) return {};
if(x<bound.minimumSubvoxels[0]||x>bound.maximumSubvoxels[0]
||y<bound.minimumSubvoxels[1]||y>bound.maximumSubvoxels[1])continue;
if(result.empty)
{
result.minimumZSubvoxels=bound.minimumSubvoxels[2];
result.maximumZSubvoxels=bound.maximumSubvoxels[2];
result.empty=false;
}
else
{
result.minimumZSubvoxels=std::min(result.minimumZSubvoxels,bound.minimumSubvoxels[2]);
result.maximumZSubvoxels=std::max(result.maximumZSubvoxels,bound.maximumSubvoxels[2]);
}
}
return result;
}
namespace world_assembly_detail
{
// Preserve recipe order without allocating or looking up metadata in the point loop.
// Garder l'ordre de recette sans allocation ni recherche de metadonnees par point.
struct FeatureRange
{
const std::vector<WorldAssemblyFeatureV1>& features;
const WorldAssemblyFeatureTileListV1* list;
struct Iterator
{
const FeatureRange* range;
std::size_t index;
const WorldAssemblyFeatureV1& operator*() const
{ return range->features[range->list ? range->list->featureIndices[index] : index]; }
Iterator& operator++() { ++index; return *this; }
bool operator!=(const Iterator& other) const { return index!=other.index; }
};
Iterator begin() const { return {this,0}; }
Iterator end() const { return {this,list ? list->featureIndices.size() : features.size()}; }
};
constexpr std::int64_t Q16 = 1ll << 16;
constexpr std::int64_t MinimumCliffShelfThicknessSubvoxels = 2 * WorldAssemblySubvoxelsPerVoxel;
constexpr std::int64_t MaxSerializedMagnitude = 1000000000000ll;
constexpr std::size_t MaxBiomeCount = 8;
constexpr std::size_t MaxStrateCount = 64;
constexpr std::size_t MaxFeatureCount = 4096;
constexpr std::size_t MaxRoutePointCount = 4096;
inline void HashWord(std::uint64_t& hash, std::uint64_t word, int bytes)
{
for (int i = 0; i < bytes; ++i)
{
hash ^= static_cast<std::uint8_t>(word >> (i * 8));
hash *= 1099511628211ull;
}
}
inline std::uint64_t StartHash() { return 14695981039346656037ull; }
inline bool ReadInteger(const VFJson::Value& value, std::int64_t& out)
{
if (!value.IsNumber()) return false;
const double number = value.Number();
constexpr double ExactJsonIntegerLimit = 9007199254740991.0;
if (!std::isfinite(number) || std::trunc(number) != number
|| std::abs(number) > ExactJsonIntegerLimit) return false;
out = static_cast<std::int64_t>(number);
return true;
}
inline bool ReadU32(const VFJson::Value& value, std::uint32_t& out)
{
std::int64_t parsed = 0;
if (!ReadInteger(value, parsed) || parsed < 0
|| parsed > std::numeric_limits<std::uint32_t>::max()) return false;
out = static_cast<std::uint32_t>(parsed);
return true;
}
inline bool ReadI32(const VFJson::Value& value, std::int32_t& out)
{
std::int64_t parsed = 0;
if (!ReadInteger(value, parsed) || parsed < std::numeric_limits<std::int32_t>::min()
|| parsed > std::numeric_limits<std::int32_t>::max()) return false;
out = static_cast<std::int32_t>(parsed);
return true;
}
inline bool ReadArray(const VFJson::Value& value, std::int64_t* out, std::size_t count)
{
if (!value.IsArray() || value.AsArray().size() != count) return false;
for (std::size_t i = 0; i < count; ++i)
if (!ReadInteger(value.AsArray()[i], out[i])) return false;
return true;
}
inline bool ReadHash(const VFJson::Value& value, std::uint64_t& out)
{
if (!value.IsString()) return false;
std::string_view text = value.String();
if (text.starts_with("0x") || text.starts_with("0X")) text.remove_prefix(2);
if (text.empty() || text.size() > 16) return false;
const auto result = std::from_chars(text.data(), text.data() + text.size(), out, 16);
return result.ec == std::errc{} && result.ptr == text.data() + text.size();
}
inline std::string HashText(std::uint64_t hash)
{
constexpr char Hex[] = "0123456789abcdef";
std::string out(16, '0');
for (int i = 15; i >= 0; --i)
{
out[static_cast<std::size_t>(i)] = Hex[hash & 0xfu];
hash >>= 4;
}
return out;
}
inline bool ValidScalar(std::int64_t value)
{ return value >= -MaxSerializedMagnitude && value <= MaxSerializedMagnitude; }
inline bool OutsideRange(std::int64_t value)
{ return value < -MaxSerializedMagnitude || value > MaxSerializedMagnitude; }
inline bool ValidateRecipe(const WorldAssemblyRecipeV1& recipe, std::string& error)
{
if (recipe.schemaVersion != WorldAssemblySchemaVersion
|| recipe.evaluatorVersion == 0
|| recipe.evaluatorVersion > WorldAssemblyEvaluatorVersion
|| recipe.unitsPerVoxel != WorldAssemblySubvoxelsPerVoxel)
{ error = "world assembly recipe version, evaluator or fixed scale is unsupported"; return false; }
if (recipe.biomes.empty() || recipe.biomes.size() > MaxBiomeCount)
{ error = "world assembly recipe must contain one to eight biome profiles"; return false; }
if (recipe.biomeCellSizeSubvoxels <= 0 || recipe.biomeCellSizeSubvoxels > 100000000ll
|| recipe.biomeWarpSubvoxels < 0 || recipe.biomeWarpSubvoxels > recipe.biomeCellSizeSubvoxels / 2
|| recipe.biomeWarpWavelengthSubvoxels <= 0
|| recipe.biomeWarpWavelengthSubvoxels > 100000000ll
|| recipe.biomeBorderBlendSubvoxels < 0
|| recipe.biomeBorderBlendSubvoxels > recipe.biomeCellSizeSubvoxels)
{ error = "world assembly biome-map parameters are outside the supported fixed-scale range"; return false; }
std::vector<std::uint32_t> ids;
ids.reserve(recipe.biomes.size());
for (const auto& biome : recipe.biomes)
{
if (biome.stableId == 0 || std::find(ids.begin(), ids.end(), biome.stableId) != ids.end())
{ error = "world assembly biome IDs must be nonzero and unique"; return false; }
ids.push_back(biome.stableId);
const std::array<std::int64_t, 31> values = {biome.baseHeightSubvoxels,
biome.continentAmplitudeSubvoxels, biome.continentWavelengthSubvoxels,
biome.mountainAmplitudeSubvoxels, biome.mountainWavelengthSubvoxels,
biome.detailAmplitudeSubvoxels, biome.detailWavelengthSubvoxels,
biome.reliefMinimumQ16, biome.reliefMaximumQ16, biome.moistureMinimumQ16,
biome.moistureMaximumQ16, biome.reliefStrengthQ16, biome.reliefContrastQ16,
biome.reliefWavelengthSubvoxels, biome.heightWarpStrengthSubvoxels,
biome.heightWarpWavelengthSubvoxels, biome.beachWidthSubvoxels,
biome.cliffStrengthQ16, biome.cliffSlopeThresholdQ16, biome.cliffSharpnessQ16,
biome.cliffSampleDistanceSubvoxels, biome.cliffMaximumDisplacementSubvoxels,
biome.capHeightSubvoxels, biome.capWarpStrengthSubvoxels,
biome.capWarpWavelengthSubvoxels, biome.capUndulationSubvoxels,
biome.capWavelengthSubvoxels, biome.capDetailAmplitudeSubvoxels,
biome.capDetailWavelengthSubvoxels, biome.capRidgeAmplitudeSubvoxels,
biome.capRidgeWavelengthSubvoxels};
if (!std::all_of(values.begin(), values.end(), ValidScalar)
|| biome.continentAmplitudeSubvoxels < 0 || biome.mountainAmplitudeSubvoxels < 0
|| biome.detailAmplitudeSubvoxels < 0 || biome.continentWavelengthSubvoxels <= 0
|| biome.mountainWavelengthSubvoxels <= 0 || biome.detailWavelengthSubvoxels <= 0
|| biome.reliefMinimumQ16 < 0 || biome.reliefMaximumQ16 > Q16
|| biome.reliefMinimumQ16 > biome.reliefMaximumQ16
|| biome.moistureMinimumQ16 < 0 || biome.moistureMaximumQ16 > Q16
|| biome.moistureMinimumQ16 > biome.moistureMaximumQ16
|| biome.reliefStrengthQ16 < 0 || biome.reliefStrengthQ16 > Q16
|| biome.reliefContrastQ16 < 0 || biome.reliefWavelengthSubvoxels <= 0
|| biome.heightWarpStrengthSubvoxels < 0 || biome.heightWarpWavelengthSubvoxels <= 0
|| biome.beachWidthSubvoxels < 0 || biome.cliffStrengthQ16 < 0
|| biome.cliffSlopeThresholdQ16 <= 0 || biome.cliffSharpnessQ16 < 0
|| biome.cliffSampleDistanceSubvoxels <= 0
|| biome.cliffMaximumDisplacementSubvoxels < 0)
{ error = "world assembly biome profile has an invalid fixed-scale value"; return false; }
if (biome.capWarpStrengthSubvoxels < 0 || biome.capWarpWavelengthSubvoxels <= 0
|| biome.capUndulationSubvoxels < 0 || biome.capWavelengthSubvoxels <= 0
|| biome.capDetailAmplitudeSubvoxels < 0 || biome.capDetailWavelengthSubvoxels <= 0
|| biome.capRidgeAmplitudeSubvoxels < 0 || biome.capRidgeWavelengthSubvoxels <= 0)
{ error = "world assembly biome cap profile has an invalid fixed-scale value"; return false; }
}
if (recipe.evaluatorVersion >= 2
&& (recipe.biomeClimateReliefWavelengthSubvoxels <= 0
|| recipe.biomeClimateMoistureWavelengthSubvoxels <= 0
|| recipe.biomeClimateReliefContrastQ16 < 0))
{ error = "world assembly climate parameters are outside the supported fixed-scale range"; return false; }
return true;
}
inline bool ValidateStratum(const WorldAssemblyStratumPlanV1& stratum, std::string& error)
{
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; }
if (!ValidScalar(stratum.capHeightSubvoxels) || !ValidScalar(stratum.capUndulationSubvoxels)
|| !ValidScalar(stratum.capWavelengthSubvoxels)
|| !ValidScalar(stratum.capDetailAmplitudeSubvoxels)
|| !ValidScalar(stratum.capDetailWavelengthSubvoxels)
|| !ValidScalar(stratum.capRidgeAmplitudeSubvoxels)
|| !ValidScalar(stratum.capRidgeWavelengthSubvoxels)
|| !ValidScalar(stratum.waterSurfaceSubvoxels)
|| stratum.capDetailAmplitudeSubvoxels < 0 || stratum.capDetailWavelengthSubvoxels <= 0
|| stratum.capRidgeAmplitudeSubvoxels < 0 || stratum.capRidgeWavelengthSubvoxels <= 0)
{ error = "world assembly cap contains a value outside the fixed-scale range"; return false; }
std::vector<std::uint64_t> ids;
ids.reserve(stratum.features.size());
for (const auto& feature : stratum.features)
{
if (feature.stableId == 0 || feature.type < 1 || feature.type > 4
|| std::find(ids.begin(), ids.end(), feature.stableId) != ids.end())
{ error = "world assembly feature IDs and types must be stable, unique and supported"; return false; }
ids.push_back(feature.stableId);
for (std::size_t axis = 0; axis < 3; ++axis)
if (!ValidScalar(feature.minimumSubvoxels[axis])
|| !ValidScalar(feature.maximumSubvoxels[axis])
|| feature.minimumSubvoxels[axis] > feature.maximumSubvoxels[axis])
{ error = "world assembly feature has an invalid influence bound"; return false; }
if (!std::all_of(feature.parametersSubvoxels.begin(), feature.parametersSubvoxels.end(), ValidScalar))
{ error = "world assembly feature parameter is outside the fixed-scale range"; return false; }
if (feature.type == 2 || feature.type == 4)
{
const std::int64_t stratumMinZ = static_cast<std::int64_t>(stratum.bottomChunkZ)
* 32 * WorldAssemblySubvoxelsPerVoxel;
const std::int64_t stratumMaxZ = (static_cast<std::int64_t>(stratum.topChunkZ) + 1)
* 32 * WorldAssemblySubvoxelsPerVoxel;
constexpr std::int64_t maximumExceptionExtent = 1024
* WorldAssemblySubvoxelsPerVoxel;
for (std::size_t axis = 0; axis < 3; ++axis)
if (feature.maximumSubvoxels[axis] <= feature.minimumSubvoxels[axis]
|| feature.maximumSubvoxels[axis] - feature.minimumSubvoxels[axis]
> maximumExceptionExtent)
{ error = "world assembly overhang exception is not a bounded positive-volume box"; return false; }
if (feature.minimumSubvoxels[2] < stratumMinZ
|| feature.maximumSubvoxels[2] > stratumMaxZ)
{ error = "world assembly overhang exception escapes its finite stratum Z bounds"; return false; }
if (feature.type == 4
&& (std::abs(feature.parametersSubvoxels[0])
+ std::abs(feature.parametersSubvoxels[1]) != 256
|| feature.parametersSubvoxels[2] <= 0
|| feature.parametersSubvoxels[3] <= 0))
{ error = "world assembly cliff shelf requires a unit horizontal axis and positive height/reach"; return false; }
}
}
for (const auto& point : stratum.route)
{
if (point.stableFeatureId == 0 || point.radiusSubvoxels <= 0
|| !ValidScalar(point.radiusSubvoxels)
|| !std::all_of(point.positionSubvoxels.begin(), point.positionSubvoxels.end(), ValidScalar))
{ error = "world assembly route point has an invalid feature ID, position or radius"; return false; }
}
std::vector<std::uint64_t> routeIds;
routeIds.reserve(stratum.route.size());
for (const auto& point : stratum.route)
{
if (std::find(routeIds.begin(), routeIds.end(), point.stableFeatureId) != routeIds.end())
{ error = "world assembly route point IDs must be unique"; return false; }
routeIds.push_back(point.stableFeatureId);
}
for (std::size_t i = 1; i < stratum.route.size(); ++i)
{
std::uint64_t squared = 0;
for (std::size_t axis = 0; axis < 3; ++axis)
{
const std::int64_t delta = stratum.route[i].positionSubvoxels[axis]
- stratum.route[i - 1].positionSubvoxels[axis];
if (std::abs(delta) > 4096 * WorldAssemblySubvoxelsPerVoxel)
{ error = "world assembly route segment exceeds its bounded sweep length"; return false; }
squared += static_cast<std::uint64_t>(delta * delta);
}
if (squared == 0)
{ error = "world assembly route contains a zero-length segment"; return false; }
}
const auto& constraints = stratum.routeConstraints;
if (constraints.version == 0)
{
if (constraints.capsuleRadiusSubvoxels != 0 || constraints.capsuleHeightSubvoxels != 0
|| constraints.maximumGradeQ16 != 0 || constraints.sweepSpacingSubvoxels != 0
|| constraints.supportProbeRadiusSubvoxels != 0
|| constraints.supportSearchDepthSubvoxels != 0
|| constraints.minimumClearanceSubvoxels != 0 || constraints.minimumSupportProbes != 0)
{ error = "world assembly route constraints need a supported version"; return false; }
}
else if (constraints.version != 1
|| !ValidScalar(constraints.capsuleRadiusSubvoxels)
|| !ValidScalar(constraints.capsuleHeightSubvoxels)
|| !ValidScalar(constraints.maximumGradeQ16)
|| !ValidScalar(constraints.sweepSpacingSubvoxels)
|| !ValidScalar(constraints.supportProbeRadiusSubvoxels)
|| !ValidScalar(constraints.supportSearchDepthSubvoxels)
|| !ValidScalar(constraints.minimumClearanceSubvoxels)
|| constraints.capsuleRadiusSubvoxels <= 0
|| constraints.capsuleHeightSubvoxels < 2 * constraints.capsuleRadiusSubvoxels
|| constraints.maximumGradeQ16 <= 0 || constraints.maximumGradeQ16 > Q16
|| constraints.sweepSpacingSubvoxels <= 0
|| constraints.sweepSpacingSubvoxels > WorldAssemblySubvoxelsPerVoxel / 2
|| constraints.supportProbeRadiusSubvoxels <= 0
|| constraints.supportSearchDepthSubvoxels <= 0
|| constraints.minimumClearanceSubvoxels < 0
|| constraints.minimumSupportProbes == 0 || constraints.minimumSupportProbes > 5)
{ error = "world assembly route support constraints are outside the supported range"; return false; }
if (constraints.version == 1)
for (const auto& point : stratum.route)
if (point.radiusSubvoxels < constraints.capsuleRadiusSubvoxels)
{ error = "world assembly route corridor is narrower than its player capsule"; return false; }
return true;
}
inline std::uint64_t HashRecipe(const WorldAssemblyRecipeV1& recipe)
{
std::uint64_t hash = StartHash();
HashWord(hash, recipe.schemaVersion, 4); HashWord(hash, recipe.evaluatorVersion, 4);
HashWord(hash, recipe.unitsPerVoxel, 4); HashWord(hash, recipe.seed, 4);
HashWord(hash, static_cast<std::uint64_t>(recipe.biomeCellSizeSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.biomeWarpSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.biomeWarpWavelengthSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.biomeBorderBlendSubvoxels), 8);
if (recipe.evaluatorVersion >= 2)
{
HashWord(hash, 0x535552465632ull, 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.biomeClimateReliefWavelengthSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.biomeClimateMoistureWavelengthSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.biomeClimateReliefContrastQ16), 8);
}
HashWord(hash, recipe.biomes.size(), 4);
for (const auto& biome : recipe.biomes)
{
HashWord(hash, biome.stableId, 4);
HashWord(hash, static_cast<std::uint64_t>(biome.baseHeightSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(biome.continentAmplitudeSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(biome.continentWavelengthSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(biome.mountainAmplitudeSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(biome.mountainWavelengthSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(biome.detailAmplitudeSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(biome.detailWavelengthSubvoxels), 8);
if (recipe.evaluatorVersion >= 2)
{
for (const auto value : {biome.reliefMinimumQ16, biome.reliefMaximumQ16,
biome.moistureMinimumQ16, biome.moistureMaximumQ16, biome.reliefStrengthQ16,
biome.reliefContrastQ16, biome.reliefWavelengthSubvoxels,
biome.heightWarpStrengthSubvoxels, biome.heightWarpWavelengthSubvoxels,
biome.beachWidthSubvoxels, biome.cliffStrengthQ16,
biome.cliffSlopeThresholdQ16, biome.cliffSharpnessQ16,
biome.cliffSampleDistanceSubvoxels, biome.cliffMaximumDisplacementSubvoxels})
HashWord(hash, static_cast<std::uint64_t>(value), 8);
HashWord(hash, biome.hasCapProfile ? 1u : 0u, 1);
for (const auto value : {biome.capHeightSubvoxels,
biome.capWarpStrengthSubvoxels, biome.capWarpWavelengthSubvoxels,
biome.capUndulationSubvoxels, biome.capWavelengthSubvoxels,
biome.capDetailAmplitudeSubvoxels, biome.capDetailWavelengthSubvoxels,
biome.capRidgeAmplitudeSubvoxels, biome.capRidgeWavelengthSubvoxels})
HashWord(hash, static_cast<std::uint64_t>(value), 8);
}
}
return hash;
}
inline std::uint64_t HashStratum(const WorldAssemblyStratumPlanV1& stratum)
{
std::uint64_t hash = StartHash();
HashWord(hash, static_cast<std::uint32_t>(stratum.strateIndex), 4);
HashWord(hash, static_cast<std::uint32_t>(stratum.topChunkZ), 4);
HashWord(hash, static_cast<std::uint32_t>(stratum.bottomChunkZ), 4);
if (stratum.hasRecipeOverride)
{
HashWord(hash, 0x5354524154455250ull, 8);
HashWord(hash, HashRecipe(stratum.recipeOverride), 8);
}
HashWord(hash, static_cast<std::uint64_t>(stratum.capHeightSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(stratum.capUndulationSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(stratum.capWavelengthSubvoxels), 8);
const bool hasExtendedCap = stratum.capDetailAmplitudeSubvoxels != 0
|| stratum.capRidgeAmplitudeSubvoxels != 0 || stratum.waterEnabled
|| stratum.waterSurfaceSubvoxels != 0;
if (hasExtendedCap)
{
HashWord(hash, 0x4341505632ull, 8);
HashWord(hash, static_cast<std::uint64_t>(stratum.capDetailAmplitudeSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(stratum.capDetailWavelengthSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(stratum.capRidgeAmplitudeSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(stratum.capRidgeWavelengthSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(stratum.waterSurfaceSubvoxels), 8);
HashWord(hash, stratum.waterEnabled ? 1u : 0u, 1);
}
HashWord(hash, stratum.features.size(), 4);
for (const auto& feature : stratum.features)
{
HashWord(hash, feature.stableId, 8); HashWord(hash, feature.type, 4);
for (const auto value : feature.minimumSubvoxels) HashWord(hash, static_cast<std::uint64_t>(value), 8);
for (const auto value : feature.maximumSubvoxels) HashWord(hash, static_cast<std::uint64_t>(value), 8);
for (const auto value : feature.parametersSubvoxels) HashWord(hash, static_cast<std::uint64_t>(value), 8);
}
if (stratum.routeConstraints.version != 0)
{
const auto& constraints = stratum.routeConstraints;
HashWord(hash, 0x524f5554455631ull, 8);
HashWord(hash, constraints.version, 4);
HashWord(hash, static_cast<std::uint64_t>(constraints.capsuleRadiusSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(constraints.capsuleHeightSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(constraints.maximumGradeQ16), 8);
HashWord(hash, static_cast<std::uint64_t>(constraints.sweepSpacingSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(constraints.supportProbeRadiusSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(constraints.supportSearchDepthSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(constraints.minimumClearanceSubvoxels), 8);
HashWord(hash, constraints.minimumSupportProbes, 4);
}
HashWord(hash, stratum.route.size(), 4);
for (const auto& point : stratum.route)
{
HashWord(hash, point.stableFeatureId, 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);
}
return hash;
}
inline std::uint64_t HashPlan(const WorldAssemblyPlanV1& plan)
{
std::uint64_t hash = StartHash();
HashWord(hash, plan.schemaVersion, 4); HashWord(hash, plan.strates.size(), 4);
for (const auto& stratum : plan.strates) HashWord(hash, stratum.contentHash, 8);
return hash;
}
inline std::uint64_t HashManifest(const WorldAssemblyManifest& manifest)
{
std::uint64_t hash = StartHash();
HashWord(hash, manifest.schemaVersion, 4);
HashWord(hash, manifest.recipe.contentHash, 8);
HashWord(hash, manifest.plan.contentHash, 8);
// Preserve the historical SurfaceWorld manifest digest when no island plan is attached.
if (!manifest.floatingIslands.empty())
{
HashWord(hash, 0x464c4f415449534cull, 8);
HashWord(hash, manifest.floatingIslands.size(), 4);
for (const auto& islands : manifest.floatingIslands)
{
HashWord(hash, static_cast<std::uint32_t>(islands.strateIndex), 4);
HashWord(hash, static_cast<std::uint32_t>(islands.topChunkZ), 4);
HashWord(hash, static_cast<std::uint32_t>(islands.bottomChunkZ), 4);
HashWord(hash, islands.manifest.contentHash, 8);
}
}
return hash;
}
inline void AppendJsonString(std::ostringstream& out, std::string_view value)
{
out << '"';
for (const unsigned char ch : value)
{
switch (ch)
{
case '"': out << "\\\""; break;
case '\\': out << "\\\\"; break;
case '\b': out << "\\b"; break;
case '\f': out << "\\f"; break;
case '\n': out << "\\n"; break;
case '\r': out << "\\r"; break;
case '\t': out << "\\t"; break;
default:
if (ch < 0x20) out << "\\u00" << std::hex << std::setw(2)
<< std::setfill('0') << static_cast<unsigned int>(ch) << std::dec;
else out << static_cast<char>(ch);
break;
}
}
out << '"';
}
inline void AppendJsonValue(std::ostringstream& out, const VFJson::Value& value)
{
if (std::holds_alternative<std::nullptr_t>(value.Data)) out << "null";
else if (const auto* boolean = std::get_if<bool>(&value.Data)) out << (*boolean ? "true" : "false");
else if (const auto* number = std::get_if<double>(&value.Data))
{
char buffer[64];
const auto result = std::to_chars(buffer, buffer + sizeof(buffer), *number,
std::chars_format::general, std::numeric_limits<double>::max_digits10);
if (result.ec == std::errc{}) out.write(buffer, result.ptr - buffer);
else out << '0';
}
else if (const auto* string = std::get_if<std::string>(&value.Data)) AppendJsonString(out, *string);
else if (const auto* array = std::get_if<VFJson::Value::Array>(&value.Data))
{
out << '[';
for (std::size_t i = 0; i < array->size(); ++i)
{ if (i) out << ','; AppendJsonValue(out, (*array)[i]); }
out << ']';
}
else
{
const auto& object = std::get<VFJson::Value::Object>(value.Data);
std::vector<std::string_view> keys;
keys.reserve(object.size());
for (const auto& entry : object) keys.push_back(entry.first);
std::sort(keys.begin(), keys.end());
out << '{';
for (std::size_t i = 0; i < keys.size(); ++i)
{
if (i) out << ',';
AppendJsonString(out, keys[i]); out << ':';
AppendJsonValue(out, object.at(std::string(keys[i])));
}
out << '}';
}
}
inline std::string JsonText(const VFJson::Value& value)
{
std::ostringstream out;
AppendJsonValue(out, value);
return out.str();
}
inline bool ParseRecipe(const VFJson::Value& value, WorldAssemblyRecipeV1& recipe,
std::string& error)
{
if (!value.IsObject() || !ReadU32(value.Get("schema_version"), recipe.schemaVersion)
|| !ReadU32(value.Get("evaluator_version"), recipe.evaluatorVersion)
|| !ReadU32(value.Get("units_per_voxel"), recipe.unitsPerVoxel)
|| !ReadU32(value.Get("seed"), recipe.seed)
|| !ReadInteger(value.Get("biome_cell_size_q"), recipe.biomeCellSizeSubvoxels)
|| !ReadInteger(value.Get("biome_warp_q"), recipe.biomeWarpSubvoxels)
|| !ReadInteger(value.Get("biome_warp_wavelength_q"), recipe.biomeWarpWavelengthSubvoxels)
|| !ReadInteger(value.Get("biome_border_blend_q"), recipe.biomeBorderBlendSubvoxels)
|| !ReadHash(value.Get("content_hash"), recipe.contentHash)
|| !value.Get("biomes").IsArray())
{ error = "world assembly recipe is missing a required versioned fixed-scale field"; return false; }
if (recipe.evaluatorVersion >= 2
&& (!ReadInteger(value.Get("climate_relief_wavelength_q"), recipe.biomeClimateReliefWavelengthSubvoxels)
|| !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; }
for (const auto& item : value.Get("biomes").AsArray())
{
WorldAssemblyBiomeProfileV1 biome;
if (!item.IsObject() || !ReadU32(item.Get("id"), biome.stableId)
|| !ReadInteger(item.Get("base_height_q"), biome.baseHeightSubvoxels)
|| !ReadInteger(item.Get("continent_amplitude_q"), biome.continentAmplitudeSubvoxels)
|| !ReadInteger(item.Get("continent_wavelength_q"), biome.continentWavelengthSubvoxels)
|| !ReadInteger(item.Get("mountain_amplitude_q"), biome.mountainAmplitudeSubvoxels)
|| !ReadInteger(item.Get("mountain_wavelength_q"), biome.mountainWavelengthSubvoxels)
|| !ReadInteger(item.Get("detail_amplitude_q"), biome.detailAmplitudeSubvoxels)
|| !ReadInteger(item.Get("detail_wavelength_q"), biome.detailWavelengthSubvoxels))
{ error = "world assembly biome profile is missing a required fixed-scale field"; return false; }
if (recipe.evaluatorVersion >= 2
&& (!ReadInteger(item.Get("relief_min_q16"), biome.reliefMinimumQ16)
|| !ReadInteger(item.Get("relief_max_q16"), biome.reliefMaximumQ16)
|| !ReadInteger(item.Get("moisture_min_q16"), biome.moistureMinimumQ16)
|| !ReadInteger(item.Get("moisture_max_q16"), biome.moistureMaximumQ16)
|| !ReadInteger(item.Get("relief_strength_q16"), biome.reliefStrengthQ16)
|| !ReadInteger(item.Get("relief_contrast_q16"), biome.reliefContrastQ16)
|| !ReadInteger(item.Get("relief_wavelength_q"), biome.reliefWavelengthSubvoxels)
|| !ReadInteger(item.Get("height_warp_strength_q"), biome.heightWarpStrengthSubvoxels)
|| !ReadInteger(item.Get("height_warp_wavelength_q"), biome.heightWarpWavelengthSubvoxels)
|| !ReadInteger(item.Get("beach_width_q"), biome.beachWidthSubvoxels)
|| !ReadInteger(item.Get("cliff_strength_q16"), biome.cliffStrengthQ16)
|| !ReadInteger(item.Get("cliff_slope_threshold_q16"), biome.cliffSlopeThresholdQ16)
|| !ReadInteger(item.Get("cliff_sharpness_q16"), biome.cliffSharpnessQ16)
|| !ReadInteger(item.Get("cliff_sample_distance_q"), biome.cliffSampleDistanceSubvoxels)
|| !ReadInteger(item.Get("cliff_max_displacement_q"), biome.cliffMaximumDisplacementSubvoxels)
|| !std::holds_alternative<bool>(item.Get("cap_enabled").Data)
|| !ReadInteger(item.Get("cap_height_q"), biome.capHeightSubvoxels)
|| !ReadInteger(item.Get("cap_warp_strength_q"), biome.capWarpStrengthSubvoxels)
|| !ReadInteger(item.Get("cap_warp_wavelength_q"), biome.capWarpWavelengthSubvoxels)
|| !ReadInteger(item.Get("cap_undulation_q"), biome.capUndulationSubvoxels)
|| !ReadInteger(item.Get("cap_wavelength_q"), biome.capWavelengthSubvoxels)
|| !ReadInteger(item.Get("cap_detail_amplitude_q"), biome.capDetailAmplitudeSubvoxels)
|| !ReadInteger(item.Get("cap_detail_wavelength_q"), biome.capDetailWavelengthSubvoxels)
|| !ReadInteger(item.Get("cap_ridge_amplitude_q"), biome.capRidgeAmplitudeSubvoxels)
|| !ReadInteger(item.Get("cap_ridge_wavelength_q"), biome.capRidgeWavelengthSubvoxels)))
{ error = "world assembly biome profile is missing evaluator-2 surface parameters"; return false; }
if (recipe.evaluatorVersion >= 2)
biome.hasCapProfile = std::get<bool>(item.Get("cap_enabled").Data);
recipe.biomes.push_back(biome);
}
return true;
}
inline std::string RecipeJson(const WorldAssemblyRecipeV1& recipe)
{
std::ostringstream out;
out << "{\"schema_version\":" << recipe.schemaVersion
<< ",\"evaluator_version\":" << recipe.evaluatorVersion
<< ",\"units_per_voxel\":" << recipe.unitsPerVoxel
<< ",\"seed\":" << recipe.seed
<< ",\"biome_cell_size_q\":" << recipe.biomeCellSizeSubvoxels
<< ",\"biome_warp_q\":" << recipe.biomeWarpSubvoxels
<< ",\"biome_warp_wavelength_q\":" << recipe.biomeWarpWavelengthSubvoxels
<< ",\"biome_border_blend_q\":" << recipe.biomeBorderBlendSubvoxels;
if (recipe.evaluatorVersion >= 2)
out << ",\"climate_relief_wavelength_q\":" << recipe.biomeClimateReliefWavelengthSubvoxels
<< ",\"climate_moisture_wavelength_q\":" << recipe.biomeClimateMoistureWavelengthSubvoxels
<< ",\"climate_relief_contrast_q16\":" << recipe.biomeClimateReliefContrastQ16;
out << ",\"content_hash\":\"" << HashText(recipe.contentHash) << "\",\"biomes\":[";
for (std::size_t i = 0; i < recipe.biomes.size(); ++i)
{
const auto& biome = recipe.biomes[i];
if (i) out << ',';
out << "{\"id\":" << biome.stableId << ",\"base_height_q\":" << biome.baseHeightSubvoxels
<< ",\"continent_amplitude_q\":" << biome.continentAmplitudeSubvoxels
<< ",\"continent_wavelength_q\":" << biome.continentWavelengthSubvoxels
<< ",\"mountain_amplitude_q\":" << biome.mountainAmplitudeSubvoxels
<< ",\"mountain_wavelength_q\":" << biome.mountainWavelengthSubvoxels
<< ",\"detail_amplitude_q\":" << biome.detailAmplitudeSubvoxels
<< ",\"detail_wavelength_q\":" << biome.detailWavelengthSubvoxels;
if (recipe.evaluatorVersion >= 2)
out << ",\"relief_min_q16\":" << biome.reliefMinimumQ16
<< ",\"relief_max_q16\":" << biome.reliefMaximumQ16
<< ",\"moisture_min_q16\":" << biome.moistureMinimumQ16
<< ",\"moisture_max_q16\":" << biome.moistureMaximumQ16
<< ",\"relief_strength_q16\":" << biome.reliefStrengthQ16
<< ",\"relief_contrast_q16\":" << biome.reliefContrastQ16
<< ",\"relief_wavelength_q\":" << biome.reliefWavelengthSubvoxels
<< ",\"height_warp_strength_q\":" << biome.heightWarpStrengthSubvoxels
<< ",\"height_warp_wavelength_q\":" << biome.heightWarpWavelengthSubvoxels
<< ",\"beach_width_q\":" << biome.beachWidthSubvoxels
<< ",\"cliff_strength_q16\":" << biome.cliffStrengthQ16
<< ",\"cliff_slope_threshold_q16\":" << biome.cliffSlopeThresholdQ16
<< ",\"cliff_sharpness_q16\":" << biome.cliffSharpnessQ16
<< ",\"cliff_sample_distance_q\":" << biome.cliffSampleDistanceSubvoxels
<< ",\"cliff_max_displacement_q\":" << biome.cliffMaximumDisplacementSubvoxels
<< ",\"cap_enabled\":" << (biome.hasCapProfile ? "true" : "false")
<< ",\"cap_height_q\":" << biome.capHeightSubvoxels
<< ",\"cap_warp_strength_q\":" << biome.capWarpStrengthSubvoxels
<< ",\"cap_warp_wavelength_q\":" << biome.capWarpWavelengthSubvoxels
<< ",\"cap_undulation_q\":" << biome.capUndulationSubvoxels
<< ",\"cap_wavelength_q\":" << biome.capWavelengthSubvoxels
<< ",\"cap_detail_amplitude_q\":" << biome.capDetailAmplitudeSubvoxels
<< ",\"cap_detail_wavelength_q\":" << biome.capDetailWavelengthSubvoxels
<< ",\"cap_ridge_amplitude_q\":" << biome.capRidgeAmplitudeSubvoxels
<< ",\"cap_ridge_wavelength_q\":" << biome.capRidgeWavelengthSubvoxels;
out << '}';
}
out << "]}";
return out.str();
}
inline bool ParsePlan(const VFJson::Value& value, WorldAssemblyPlanV1& plan,
std::string& error)
{
if (!value.IsObject() || !ReadU32(value.Get("schema_version"), plan.schemaVersion)
|| !ReadHash(value.Get("content_hash"), plan.contentHash)
|| !value.Get("strates").IsArray())
{ error = "world assembly plan is missing a required versioned field"; return false; }
for (const auto& item : value.Get("strates").AsArray())
{
WorldAssemblyStratumPlanV1 stratum;
if (!item.IsObject() || !ReadI32(item.Get("strate_index"), stratum.strateIndex)
|| !ReadI32(item.Get("top_chunk_z"), stratum.topChunkZ)
|| !ReadI32(item.Get("bottom_chunk_z"), stratum.bottomChunkZ)
|| !ReadInteger(item.Get("cap_height_q"), stratum.capHeightSubvoxels)
|| !ReadInteger(item.Get("cap_undulation_q"), stratum.capUndulationSubvoxels)
|| !ReadInteger(item.Get("cap_wavelength_q"), stratum.capWavelengthSubvoxels)
|| !ReadHash(item.Get("content_hash"), stratum.contentHash)
|| !item.Get("features").IsArray() || !item.Get("route").IsArray())
{ error = "world assembly stratum is missing a required fixed-scale field"; return false; }
const VFJson::Value& recipeOverrideValue = item.Get("recipe_override");
if (!std::holds_alternative<std::nullptr_t>(recipeOverrideValue.Data))
{
if (!ParseRecipe(recipeOverrideValue, stratum.recipeOverride, error)) return false;
stratum.hasRecipeOverride = true;
}
const auto readOptional = [&item](const char* name, std::int64_t& output)
{
const VFJson::Value& field = item.Get(name);
if (std::holds_alternative<std::nullptr_t>(field.Data)) return true;
return field.IsNumber() && ReadInteger(field, output);
};
const VFJson::Value& waterEnabled = item.Get("water_enabled");
const bool hasWaterBoolean=std::holds_alternative<bool>(waterEnabled.Data);
const bool hasWaterNumber=waterEnabled.IsNumber();
const bool hasWaterNull=std::holds_alternative<std::nullptr_t>(waterEnabled.Data);
if (!readOptional("cap_detail_amplitude_q", stratum.capDetailAmplitudeSubvoxels)
|| !readOptional("cap_detail_wavelength_q", stratum.capDetailWavelengthSubvoxels)
|| !readOptional("cap_ridge_amplitude_q", stratum.capRidgeAmplitudeSubvoxels)
|| !readOptional("cap_ridge_wavelength_q", stratum.capRidgeWavelengthSubvoxels)
|| !readOptional("water_surface_q", stratum.waterSurfaceSubvoxels)
|| (!hasWaterBoolean&&!hasWaterNumber&&!hasWaterNull))
{ 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& routeConstraintsValue = item.Get("route_constraints");
if (!std::holds_alternative<std::nullptr_t>(routeConstraintsValue.Data))
{
auto& constraints = stratum.routeConstraints;
std::uint32_t minimumSupportProbes = 0;
if (!routeConstraintsValue.IsObject()
|| !ReadU32(routeConstraintsValue.Get("version"), constraints.version)
|| !ReadInteger(routeConstraintsValue.Get("capsule_radius_q"), constraints.capsuleRadiusSubvoxels)
|| !ReadInteger(routeConstraintsValue.Get("capsule_height_q"), constraints.capsuleHeightSubvoxels)
|| !ReadInteger(routeConstraintsValue.Get("maximum_grade_q16"), constraints.maximumGradeQ16)
|| !ReadInteger(routeConstraintsValue.Get("sweep_spacing_q"), constraints.sweepSpacingSubvoxels)
|| !ReadInteger(routeConstraintsValue.Get("support_probe_radius_q"), constraints.supportProbeRadiusSubvoxels)
|| !ReadInteger(routeConstraintsValue.Get("support_search_depth_q"), constraints.supportSearchDepthSubvoxels)
|| !ReadInteger(routeConstraintsValue.Get("minimum_clearance_q"), constraints.minimumClearanceSubvoxels)
|| !ReadU32(routeConstraintsValue.Get("minimum_support_probes"), minimumSupportProbes))
{ error = "world assembly route constraints are missing a fixed-scale field"; return false; }
constraints.minimumSupportProbes = minimumSupportProbes;
}
for (const auto& featureValue : item.Get("features").AsArray())
{
WorldAssemblyFeatureV1 feature;
std::uint32_t type = 0;
if (!featureValue.IsObject() || !ReadHash(featureValue.Get("stable_id"), feature.stableId)
|| !ReadU32(featureValue.Get("type"), type)
|| !ReadArray(featureValue.Get("min_q"), feature.minimumSubvoxels.data(), 3)
|| !ReadArray(featureValue.Get("max_q"), feature.maximumSubvoxels.data(), 3)
|| !ReadArray(featureValue.Get("parameters_q"), feature.parametersSubvoxels.data(), 4))
{ error = "world assembly feature is missing a fixed-scale field"; return false; }
feature.type = type;
stratum.features.push_back(feature);
}
for (const auto& pointValue : item.Get("route").AsArray())
{
WorldAssemblyRoutePointV1 point;
if (!pointValue.IsObject() || !ReadHash(pointValue.Get("feature_id"), point.stableFeatureId)
|| !ReadArray(pointValue.Get("position_q"), point.positionSubvoxels.data(), 3)
|| !ReadInteger(pointValue.Get("radius_q"), point.radiusSubvoxels))
{ error = "world assembly route point is missing a fixed-scale field"; return false; }
stratum.route.push_back(point);
}
plan.strates.push_back(std::move(stratum));
}
return true;
}
inline std::optional<WorldAssemblyManifest> ParseManifestValue(const VFJson::Value& value,
std::string& error)
{
WorldAssemblyManifest manifest;
if (!value.IsObject() || !ReadU32(value.Get("schema_version"), manifest.schemaVersion)
|| !ReadHash(value.Get("content_hash"), manifest.contentHash)
|| !ParseRecipe(value.Get("recipe"), manifest.recipe, error)
|| !ParsePlan(value.Get("plan"), manifest.plan, error))
{
if (error.empty()) error = "world assembly manifest is missing a required field";
return std::nullopt;
}
const VFJson::Value& islandPlans = value.Get("floating_islands");
if (!std::holds_alternative<std::nullptr_t>(islandPlans.Data))
{
if (!islandPlans.IsArray() || islandPlans.AsArray().size() > MaxStrateCount)
{ error = "world assembly floating-island plans must be an array of at most sixty-four entries"; return std::nullopt; }
for (const VFJson::Value& islandValue : islandPlans.AsArray())
{
WorldAssemblyFloatingIslandPlanV1 entry;
if (!islandValue.IsObject()
|| !ReadI32(islandValue.Get("strate_index"), entry.strateIndex)
|| !ReadI32(islandValue.Get("top_chunk_z"), entry.topChunkZ)
|| !ReadI32(islandValue.Get("bottom_chunk_z"), entry.bottomChunkZ))
{ error = "world assembly floating-island slot is missing its resolved strate bounds"; return std::nullopt; }
const std::string nestedJson = JsonText(islandValue.Get("manifest"));
auto parsedIsland = FloatingIslandManifestV1::FromJson(nestedJson, error);
if (!parsedIsland) return std::nullopt;
entry.manifest = std::move(*parsedIsland);
manifest.floatingIslands.push_back(std::move(entry));
}
}
if (!manifest.validate(error)) return std::nullopt;
return manifest;
}
inline std::int64_t FloorDiv(std::int64_t value, std::int64_t divisor)
{
const std::int64_t quotient = value / divisor, remainder = value % divisor;
return quotient - ((remainder != 0 && remainder < 0) ? 1 : 0);
}
inline std::int64_t FloorMod(std::int64_t value, std::int64_t divisor)
{ return value - FloorDiv(value, divisor) * divisor; }
inline std::uint64_t Mix64(std::uint64_t value)
{
value += 0x9e3779b97f4a7c15ull;
value = (value ^ (value >> 30)) * 0xbf58476d1ce4e5b9ull;
value = (value ^ (value >> 27)) * 0x94d049bb133111ebull;
return value ^ (value >> 31);
}
inline std::uint64_t CoordinateHash(std::uint32_t seed, std::int64_t x,
std::int64_t y, std::uint64_t salt)
{
std::uint64_t hash = Mix64(static_cast<std::uint64_t>(seed) ^ salt);
hash ^= Mix64(static_cast<std::uint64_t>(x) + 0x517cc1b727220a95ull);
hash ^= Mix64(static_cast<std::uint64_t>(y) + 0x6eed0e9da4d94a4full);
return Mix64(hash);
}
inline std::int32_t LatticeValue(std::uint32_t seed, std::int64_t x,
std::int64_t y, std::uint64_t salt)
{
const std::uint32_t bits = static_cast<std::uint32_t>(CoordinateHash(seed, x, y, salt) >> 48) & 0x1ffffu;
return static_cast<std::int32_t>(bits) - 65536;
}
inline std::int32_t LerpQ16(std::int32_t a, std::int32_t b, std::uint32_t t)
{ return static_cast<std::int32_t>(a + (static_cast<std::int64_t>(b - a) * t) / Q16); }
inline std::uint32_t SmoothQ16(std::uint32_t t)
{
const std::int64_t t2 = (static_cast<std::int64_t>(t) * t) / Q16;
return static_cast<std::uint32_t>((t2 * (3 * Q16 - 2 * static_cast<std::int64_t>(t))) / Q16);
}
inline std::int32_t Noise2D(std::int64_t x, std::int64_t y, std::int64_t wavelength,
std::uint32_t seed, std::uint64_t salt)
{
const std::int64_t cellX = FloorDiv(x, wavelength), cellY = FloorDiv(y, wavelength);
const std::uint32_t fractionX = static_cast<std::uint32_t>((FloorMod(x, wavelength) * Q16) / wavelength);
const std::uint32_t fractionY = static_cast<std::uint32_t>((FloorMod(y, wavelength) * Q16) / wavelength);
const std::uint32_t smoothX = SmoothQ16(fractionX), smoothY = SmoothQ16(fractionY);
const std::int32_t low = LerpQ16(LatticeValue(seed, cellX, cellY, salt),
LatticeValue(seed, cellX + 1, cellY, salt), smoothX);
const std::int32_t high = LerpQ16(LatticeValue(seed, cellX, cellY + 1, salt),
LatticeValue(seed, cellX + 1, cellY + 1, salt), smoothX);
return LerpQ16(low, high, smoothY);
}
inline std::uint32_t RatioQ16(std::uint64_t numerator, std::uint64_t denominator)
{
if (denominator == 0 || numerator >= denominator) return 65536;
std::uint32_t result = 0;
std::uint64_t remainder = numerator;
for (int bit = 15; bit >= 0; --bit)
{
remainder *= 2;
if (remainder >= denominator)
{ remainder -= denominator; result |= (1u << bit); }
}
return result;
}
inline std::int64_t MulQ16(std::int64_t value, std::int64_t q16)
{ return (value * q16) / Q16; }
inline std::int64_t ProfileHeight(const WorldAssemblyBiomeProfileV1& biome,
std::int64_t x, std::int64_t y, std::uint32_t seed)
{
const std::int64_t continent = MulQ16(biome.continentAmplitudeSubvoxels,
Noise2D(x, y, biome.continentWavelengthSubvoxels, seed, 0x434f4e54494e454eull));
const std::int64_t mountain = MulQ16(biome.mountainAmplitudeSubvoxels,
Noise2D(x, y, biome.mountainWavelengthSubvoxels, seed, 0x4d4f554e5441494eull));
const std::int64_t detail = MulQ16(biome.detailAmplitudeSubvoxels,
Noise2D(x, y, biome.detailWavelengthSubvoxels, seed, 0x44455441494c3031ull));
return biome.baseHeightSubvoxels + continent + mountain + detail;
}
inline std::uint32_t Mix32(std::uint32_t value)
{
value ^= value >> 16; value *= 0x45d9f3bu;
value ^= value >> 16; value *= 0x45d9f3bu;
value ^= value >> 16;
return value;
}
inline std::int64_t SeedOffsetQ16(std::uint32_t seed, std::uint32_t siteHundredths)
{
const std::uint32_t site = siteHundredths * 2654435761u;
return static_cast<std::int64_t>(Mix32(seed ^ site) & 0x3fffu) * Q16;
}
inline std::uint32_t HashCorner(std::int64_t x, std::int64_t y, std::int64_t z)
{
std::uint32_t hash = static_cast<std::uint32_t>(x) * 0x9E3779B1u
^ static_cast<std::uint32_t>(y) * 0x85EBCA77u
^ static_cast<std::uint32_t>(z) * 0xC2B2AE3Du;
hash ^= hash >> 15; hash *= 0x2C1B3C6Du;
hash ^= hash >> 12; hash *= 0x297A2D39u;
hash ^= hash >> 15;
return hash;
}
inline std::int32_t FadeQ16(std::int32_t value)
{
const std::int64_t t2 = (static_cast<std::int64_t>(value) * value) / Q16;
const std::int64_t t3 = (t2 * value) / Q16;
const std::int64_t t4 = (t3 * value) / Q16;
const std::int64_t t5 = (t4 * value) / Q16;
return static_cast<std::int32_t>(6 * t5 - 15 * t4 + 10 * t3);
}
inline std::int32_t GradientDotQ16(std::uint32_t hash, std::int32_t x,
std::int32_t y, std::int32_t z)
{
const std::uint32_t h = hash & 15u;
const std::int32_t u = (h & 8u) == 0u ? x : y;
const std::int32_t v = (h & 12u) == 0u ? y : ((h & 13u) == 12u ? x : z);
return ((h & 1u) == 0u ? u : -u) + ((h & 2u) == 0u ? v : -v);
}
inline std::int32_t Perlin3DQ16(std::int64_t xQ16, std::int64_t yQ16,
std::int64_t zQ16)
{
const std::int64_t cellX = FloorDiv(xQ16, Q16);
const std::int64_t cellY = FloorDiv(yQ16, Q16);
const std::int64_t cellZ = FloorDiv(zQ16, Q16);
const std::int32_t fx = static_cast<std::int32_t>(FloorMod(xQ16, Q16));
const std::int32_t fy = static_cast<std::int32_t>(FloorMod(yQ16, Q16));
const std::int32_t fz = static_cast<std::int32_t>(FloorMod(zQ16, Q16));
const std::int32_t sx = FadeQ16(fx), sy = FadeQ16(fy), sz = FadeQ16(fz);
const std::int32_t fx1 = fx - static_cast<std::int32_t>(Q16);
const std::int32_t fy1 = fy - static_cast<std::int32_t>(Q16);
const std::int32_t fz1 = fz - static_cast<std::int32_t>(Q16);
const auto n = [&](std::int64_t dx, std::int64_t dy, std::int64_t dz,
std::int32_t px, std::int32_t py, std::int32_t pz)
{ return GradientDotQ16(HashCorner(cellX + dx, cellY + dy, cellZ + dz), px, py, pz); };
const std::int32_t n000=n(0,0,0,fx,fy,fz), n100=n(1,0,0,fx1,fy,fz);
const std::int32_t n010=n(0,1,0,fx,fy1,fz), n110=n(1,1,0,fx1,fy1,fz);
const std::int32_t n001=n(0,0,1,fx,fy,fz1), n101=n(1,0,1,fx1,fy,fz1);
const std::int32_t n011=n(0,1,1,fx,fy1,fz1), n111=n(1,1,1,fx1,fy1,fz1);
const std::int32_t x00=LerpQ16(n000,n100,static_cast<std::uint32_t>(sx));
const std::int32_t x10=LerpQ16(n010,n110,static_cast<std::uint32_t>(sx));
const std::int32_t x01=LerpQ16(n001,n101,static_cast<std::uint32_t>(sx));
const std::int32_t x11=LerpQ16(n011,n111,static_cast<std::uint32_t>(sx));
const std::int32_t y0=LerpQ16(x00,x10,static_cast<std::uint32_t>(sy));
const std::int32_t y1=LerpQ16(x01,x11,static_cast<std::uint32_t>(sy));
return LerpQ16(y0,y1,static_cast<std::uint32_t>(sz));
}
inline std::int64_t NoiseCoordinateQ16(std::int64_t positionSubvoxels,
std::int64_t wavelengthSubvoxels)
{ return FloorDiv(positionSubvoxels * Q16, wavelengthSubvoxels); }
// Audit-only override; normal tile generation resets this to zero before evaluating a recipe.
// Surcharge reservee aux mesures; la generation remet zero avant d'evaluer une recette.
inline thread_local std::uint32_t HeightfieldColumnOctaveDrop = 0;
inline std::int32_t FractalNoiseQ16(std::int64_t xQ16, std::int64_t yQ16,
std::int64_t zQ16, std::uint32_t octaves, bool ridged = false)
{
std::int64_t total = 0, weightSum = 0, weight = Q16, ridgeWeight = Q16;
std::uint32_t effectiveOctaves=octaves;
if(HeightfieldColumnOctaveDrop!=0&&octaves!=0)
effectiveOctaves=std::max(1u,octaves-std::min(octaves-1,HeightfieldColumnOctaveDrop));
for (std::uint32_t octave = 0; octave < effectiveOctaves; ++octave)
{
const std::int64_t scale = 1ll << octave;
const std::int64_t noise = Perlin3DQ16(xQ16 * scale,yQ16 * scale,zQ16 * scale);
std::int64_t value = noise;
if (ridged)
{
const std::int64_t scaled = noise * 5 / 4;
const std::int64_t folded = Q16 - std::abs(scaled);
value = MulQ16(MulQ16(folded,folded),ridgeWeight);
ridgeWeight = std::clamp<std::int64_t>(value * 2,0,Q16);
}
total += MulQ16(value,weight);
weightSum += weight;
weight /= 2;
}
if (weightSum == 0) return 0;
const std::int64_t average = (total * Q16) / weightSum;
return static_cast<std::int32_t>(ridged ? average * 2 - Q16 : average);
}
inline std::int32_t ReliefQ16(std::uint32_t seed, std::int64_t x,
std::int64_t y, std::int64_t wavelength, std::int64_t contrastQ16)
{
const std::int32_t noise = FractalNoiseQ16(
NoiseCoordinateQ16(x,wavelength)+SeedOffsetQ16(seed,730),
NoiseCoordinateQ16(y,wavelength)+SeedOffsetQ16(seed,210),
SeedOffsetQ16(seed,50),2);
const std::int64_t raw = Q16/2 + noise/2;
const std::int64_t contrasted = Q16/2 + MulQ16(raw-Q16/2,contrastQ16);
return static_cast<std::int32_t>(SmoothQ16(static_cast<std::uint32_t>(
std::clamp<std::int64_t>(contrasted,0,Q16))));
}
inline std::int32_t MoistureQ16(std::uint32_t seed, std::int64_t x,
std::int64_t y, std::int64_t wavelength)
{
const std::int32_t noise = FractalNoiseQ16(
NoiseCoordinateQ16(x,wavelength)+SeedOffsetQ16(seed,470),
NoiseCoordinateQ16(y,wavelength)+SeedOffsetQ16(seed,890),
SeedOffsetQ16(seed,130),2);
return static_cast<std::int32_t>(std::clamp<std::int64_t>(Q16/2+noise/2,0,Q16));
}
inline std::int64_t WarpDisplacement(std::int32_t noiseQ16,
std::int64_t amplitudeSubvoxels)
{ return (static_cast<std::int64_t>(noiseQ16)*amplitudeSubvoxels*5)/(4*Q16); }
inline std::int64_t StructuralHeightV2(const WorldAssemblyRecipeV1& recipe,
const WorldAssemblyBiomeProfileV1& biome, std::int64_t x, std::int64_t y,
std::int32_t* outRelief = nullptr)
{
const std::uint32_t seed=recipe.seed;
std::int64_t qx=x,qy=y;
if(biome.heightWarpStrengthSubvoxels>0)
{
const std::int64_t xw=NoiseCoordinateQ16(x,biome.heightWarpWavelengthSubvoxels);
const std::int64_t yw=NoiseCoordinateQ16(y,biome.heightWarpWavelengthSubvoxels);
const std::int32_t wx=Perlin3DQ16(xw+SeedOffsetQ16(seed,31),yw+4*Q16+13*Q16/64,SeedOffsetQ16(seed,170));
const std::int32_t wy=Perlin3DQ16(xw+8*Q16+3*Q16/5,yw+SeedOffsetQ16(seed,53),SeedOffsetQ16(seed,290));
qx+=WarpDisplacement(wx,biome.heightWarpStrengthSubvoxels);
qy+=WarpDisplacement(wy,biome.heightWarpStrengthSubvoxels);
}
const std::int32_t relief=ReliefQ16(seed,x,y,biome.reliefWavelengthSubvoxels,
biome.reliefContrastQ16);
const std::int32_t mountainous=static_cast<std::int32_t>(Q16
+MulQ16(relief-Q16,biome.reliefStrengthQ16));
if(outRelief)*outRelief=mountainous;
const std::int64_t contX=NoiseCoordinateQ16(qx,biome.continentWavelengthSubvoxels)+SeedOffsetQ16(seed,310);
const std::int64_t contY=NoiseCoordinateQ16(qy,biome.continentWavelengthSubvoxels)+SeedOffsetQ16(seed,570);
const std::int64_t contZ=SeedOffsetQ16(seed,70);
const std::int32_t continent=FractalNoiseQ16(contX,contY,contZ,4);
const std::int64_t continentScale=Q16*45/100+MulQ16(mountainous,Q16*55/100);
std::int64_t height=biome.baseHeightSubvoxels
+MulQ16(MulQ16(continent,biome.continentAmplitudeSubvoxels),continentScale)/2;
if(biome.mountainAmplitudeSubvoxels>0)
{
const std::int64_t mountainX=NoiseCoordinateQ16(qx,biome.mountainWavelengthSubvoxels)+SeedOffsetQ16(seed,990);
const std::int64_t mountainY=NoiseCoordinateQ16(qy,biome.mountainWavelengthSubvoxels)+SeedOffsetQ16(seed,770);
const std::int64_t mountainZ=SeedOffsetQ16(seed,90);
const std::int32_t ridge=FractalNoiseQ16(mountainX,mountainY,mountainZ,4,true);
const std::int64_t ridgeHeight=std::clamp<std::int64_t>((ridge+Q16)/2,0,Q16);
height+=MulQ16(MulQ16(ridgeHeight,biome.mountainAmplitudeSubvoxels),mountainous);
}
const std::int64_t detailX=NoiseCoordinateQ16(x,biome.detailWavelengthSubvoxels)+11*Q16;
const std::int64_t detailY=NoiseCoordinateQ16(y,biome.detailWavelengthSubvoxels)+22*Q16;
const std::int64_t detailZ=SeedOffsetQ16(seed,130);
height+=MulQ16(FractalNoiseQ16(detailX,detailY,detailZ,3),biome.detailAmplitudeSubvoxels);
return height;
}
inline std::int64_t SurfaceHeightV2(const WorldAssemblyRecipeV1& recipe,
const WorldAssemblyStratumPlanV1& plan, const WorldAssemblyBiomeProfileV1& biome,
std::int64_t x,std::int64_t y)
{
std::int64_t terrain=StructuralHeightV2(recipe,biome,x,y);
if(biome.cliffStrengthQ16>0 && biome.cliffMaximumDisplacementSubvoxels>0)
{
const std::int64_t d=biome.cliffSampleDistanceSubvoxels;
const std::int64_t xp=StructuralHeightV2(recipe,biome,x+d,y);
const std::int64_t xm=StructuralHeightV2(recipe,biome,x-d,y);
const std::int64_t yp=StructuralHeightV2(recipe,biome,x,y+d);
const std::int64_t ym=StructuralHeightV2(recipe,biome,x,y-d);
const std::int64_t gx=((xp-xm)*Q16)/(2*d),gy=((yp-ym)*Q16)/(2*d);
const std::uint64_t square=static_cast<std::uint64_t>(gx*gx+gy*gy);
std::uint64_t remainder=square,root=0,bit=1ull<<62;
while(bit>remainder)bit>>=2;
while(bit)
{
if(remainder>=root+bit)
{remainder-=root+bit;root=(root>>1)+bit;}
else root>>=1;
bit>>=2;
}
const std::int64_t slope=static_cast<std::int64_t>(root);
const std::int64_t threshold=std::max<std::int64_t>(1,biome.cliffSlopeThresholdQ16);
const std::int64_t gate=std::clamp<std::int64_t>(
(slope-threshold)*Q16/threshold,0,Q16);
if(gate>0)
{
const std::int64_t reference=(xp+xm+yp+ym)/4;
const std::int64_t gain=MulQ16(MulQ16(biome.cliffStrengthQ16,gate),
biome.cliffSharpnessQ16);
const std::int64_t displacement=std::clamp<std::int64_t>(
MulQ16(terrain-reference,gain),-biome.cliffMaximumDisplacementSubvoxels,
biome.cliffMaximumDisplacementSubvoxels);
terrain+=displacement;
}
}
if(biome.beachWidthSubvoxels>0 && plan.waterEnabled)
{
const std::int64_t distance=std::abs(terrain-plan.waterSurfaceSubvoxels);
if(distance<biome.beachWidthSubvoxels)
{
const std::uint32_t t=static_cast<std::uint32_t>(std::clamp<std::int64_t>(
distance*Q16/biome.beachWidthSubvoxels,0,Q16));
const std::uint32_t smooth=SmoothQ16(t);
terrain=plan.waterSurfaceSubvoxels+MulQ16(terrain-plan.waterSurfaceSubvoxels,smooth);
}
}
return terrain;
}
inline std::uint64_t IntegerSqrt(std::uint64_t value)
{
std::uint64_t remainder=value,root=0,bit=1ull<<62;
while(bit>remainder)bit>>=2;
while(bit)
{
if(remainder>=root+bit){remainder-=root+bit;root=(root>>1)+bit;}
else root>>=1;
bit>>=2;
}
return root;
}
struct BiomeSelectionV2
{
std::size_t primary=0,neighbor=0;
std::uint32_t primaryWeightQ16=static_cast<std::uint32_t>(Q16);
std::int64_t warpedX=0,warpedY=0;
};
inline std::size_t ClassifyBiomeSiteV2(const WorldAssemblyRecipeV1& recipe,
std::int64_t siteX,std::int64_t siteY,std::uint32_t siteHash)
{
const std::int32_t relief=ReliefQ16(recipe.seed,siteX,siteY,
recipe.biomeClimateReliefWavelengthSubvoxels,recipe.biomeClimateReliefContrastQ16);
const std::int32_t moisture=MoistureQ16(recipe.seed,siteX,siteY,
recipe.biomeClimateMoistureWavelengthSubvoxels);
std::size_t best=0;
std::uint64_t bestScore=std::numeric_limits<std::uint64_t>::max();
for(std::size_t i=0;i<recipe.biomes.size();++i)
{
const auto& biome=recipe.biomes[i];
const std::int64_t dr=relief<biome.reliefMinimumQ16
?biome.reliefMinimumQ16-relief:(relief>biome.reliefMaximumQ16
?relief-biome.reliefMaximumQ16:0);
const std::int64_t dm=moisture<biome.moistureMinimumQ16
?biome.moistureMinimumQ16-moisture:(moisture>biome.moistureMaximumQ16
?moisture-biome.moistureMaximumQ16:0);
const std::uint32_t jitterHash=Mix32(siteHash^(0x9e3779b9u*static_cast<std::uint32_t>(i+1)));
const std::uint64_t jitter=(static_cast<std::uint64_t>(jitterHash>>8)*429497u)/0x1000000u;
const std::uint64_t score=static_cast<std::uint64_t>(dr*dr+dm*dm)+jitter;
if(score<bestScore){bestScore=score;best=i;}
}
return best;
}
inline BiomeSelectionV2 SelectBiomeV2(const WorldAssemblyRecipeV1& recipe,
std::int64_t x,std::int64_t y)
{
BiomeSelectionV2 out;
out.warpedX=x;out.warpedY=y;
if(recipe.biomes.size()<2)return out;
const std::int64_t xq=NoiseCoordinateQ16(x,recipe.biomeWarpWavelengthSubvoxels);
const std::int64_t yq=NoiseCoordinateQ16(y,recipe.biomeWarpWavelengthSubvoxels);
const std::int32_t wx=Perlin3DQ16(xq+SeedOffsetQ16(recipe.seed,27),
yq+3*Q16+Q16/10,SeedOffsetQ16(recipe.seed,110));
const std::int32_t wy=Perlin3DQ16(xq+7*Q16+7*Q16/10,
yq+SeedOffsetQ16(recipe.seed,61),SeedOffsetQ16(recipe.seed,230));
out.warpedX+=WarpDisplacement(wx,recipe.biomeWarpSubvoxels);
out.warpedY+=WarpDisplacement(wy,recipe.biomeWarpSubvoxels);
const std::int64_t cellSize=std::max<std::int64_t>(1,recipe.biomeCellSizeSubvoxels);
const std::int64_t cellX=FloorDiv(out.warpedX,cellSize);
const std::int64_t cellY=FloorDiv(out.warpedY,cellSize);
const std::uint32_t biomeSeed=recipe.seed^0x42494f4du;
std::uint64_t nearest=std::numeric_limits<std::uint64_t>::max();
std::uint64_t second=nearest;
for(std::int64_t dy=-1;dy<=1;++dy)
for(std::int64_t dx=-1;dx<=1;++dx)
{
const std::int64_t cx=cellX+dx,cy=cellY+dy;
std::uint32_t hash=biomeSeed;
hash^=Mix32(static_cast<std::uint32_t>(cx)+0x7fffffffu);
hash^=Mix32((static_cast<std::uint32_t>(cy)+0x7fffffffu)*2654435761u);
hash=Mix32(hash);
const std::uint32_t jx=hash&0xffffffu;
const std::uint32_t jy=Mix32(hash^0x68bc21ebu)&0xffffffu;
const std::int64_t siteX=cx*cellSize+(static_cast<std::int64_t>(jx)*cellSize)/0x1000000ll;
const std::int64_t siteY=cy*cellSize+(static_cast<std::int64_t>(jy)*cellSize)/0x1000000ll;
const std::int64_t dxq=siteX-out.warpedX,dyq=siteY-out.warpedY;
const std::uint64_t distance=static_cast<std::uint64_t>(dxq*dxq+dyq*dyq);
if(distance<nearest)
{
second=nearest;out.neighbor=out.primary;nearest=distance;
out.primary=ClassifyBiomeSiteV2(recipe,siteX,siteY,hash);
}
else if(distance<second)
{
second=distance;out.neighbor=ClassifyBiomeSiteV2(recipe,siteX,siteY,hash);
}
}
if(out.primary!=out.neighbor && recipe.biomeBorderBlendSubvoxels>0
&&second!=std::numeric_limits<std::uint64_t>::max())
{
const std::int64_t d1=static_cast<std::int64_t>(IntegerSqrt(nearest));
const std::int64_t d2=static_cast<std::int64_t>(IntegerSqrt(second));
const std::int64_t t=std::clamp<std::int64_t>((d2-d1)*Q16
/std::max<std::int64_t>(recipe.biomeBorderBlendSubvoxels,1),0,Q16);
const std::uint32_t neighborWeight=static_cast<std::uint32_t>(
(Q16-SmoothQ16(static_cast<std::uint32_t>(t)))/2);
out.primaryWeightQ16=static_cast<std::uint32_t>(Q16)-neighborWeight;
}
return out;
}
inline std::int64_t CapHeightV2(const WorldAssemblyRecipeV1& recipe,
const WorldAssemblyStratumPlanV1& plan,const WorldAssemblyBiomeProfileV1& biome,
std::int64_t x,std::int64_t y)
{
const std::int64_t base=biome.hasCapProfile?biome.capHeightSubvoxels:plan.capHeightSubvoxels;
const std::int64_t warpStrength=biome.hasCapProfile
?biome.capWarpStrengthSubvoxels:0;
const std::int64_t warpWave=biome.hasCapProfile
?biome.capWarpWavelengthSubvoxels:plan.capWavelengthSubvoxels;
const std::int64_t undulation=biome.hasCapProfile
?biome.capUndulationSubvoxels:plan.capUndulationSubvoxels;
const std::int64_t swellWave=biome.hasCapProfile
?biome.capWavelengthSubvoxels:plan.capWavelengthSubvoxels;
const std::int64_t detail=biome.hasCapProfile
?biome.capDetailAmplitudeSubvoxels:plan.capDetailAmplitudeSubvoxels;
const std::int64_t detailWave=biome.hasCapProfile
?biome.capDetailWavelengthSubvoxels:plan.capDetailWavelengthSubvoxels;
const std::int64_t ridge=biome.hasCapProfile
?biome.capRidgeAmplitudeSubvoxels:plan.capRidgeAmplitudeSubvoxels;
const std::int64_t ridgeWave=biome.hasCapProfile
?biome.capRidgeWavelengthSubvoxels:plan.capRidgeWavelengthSubvoxels;
std::int64_t qx=x,qy=y;
if(warpStrength>0)
{
const std::int64_t nx=NoiseCoordinateQ16(x,warpWave);
const std::int64_t ny=NoiseCoordinateQ16(y,warpWave);
const std::int32_t wx=Perlin3DQ16(nx+SeedOffsetQ16(recipe.seed,71),ny+2*Q16+Q16*3/10,
SeedOffsetQ16(recipe.seed,330));
const std::int32_t wy=Perlin3DQ16(nx+6*Q16+Q16/10,ny+SeedOffsetQ16(recipe.seed,19),
SeedOffsetQ16(recipe.seed,470));
qx+=WarpDisplacement(wx,warpStrength);qy+=WarpDisplacement(wy,warpStrength);
}
std::int64_t cap=base;
if(undulation!=0)
{
const std::int64_t nx=NoiseCoordinateQ16(qx,swellWave)+SeedOffsetQ16(recipe.seed,190);
const std::int64_t ny=NoiseCoordinateQ16(qy,swellWave)+13*Q16;
const std::int32_t swell=FractalNoiseQ16(nx,ny,SeedOffsetQ16(recipe.seed,50),3);
cap+=WarpDisplacement(swell,undulation);
}
if(detail>0)
{
const std::int64_t nx=NoiseCoordinateQ16(x,detailWave)+5*Q16;
const std::int64_t ny=NoiseCoordinateQ16(y,detailWave)+6*Q16;
const std::int32_t noise=FractalNoiseQ16(nx,ny,SeedOffsetQ16(recipe.seed,210),3);
cap-=WarpDisplacement(std::abs(noise),detail);
}
if(ridge>0)
{
const std::int64_t nx=NoiseCoordinateQ16(qx,ridgeWave)+31*Q16;
const std::int64_t ny=NoiseCoordinateQ16(qy,ridgeWave)+47*Q16;
const std::int32_t ridgeNoise=FractalNoiseQ16(nx,ny,SeedOffsetQ16(recipe.seed,110),4,true);
const std::int64_t hang=std::clamp<std::int64_t>((ridgeNoise+Q16)/2,0,Q16);
cap-=MulQ16(hang,ridge);
}
return cap;
}
inline std::uint32_t FeatureFadeQ16(const WorldAssemblyFeatureV1& feature,
std::int64_t x,std::int64_t y,std::int64_t feather)
{
const auto& lo=feature.minimumSubvoxels;const auto& hi=feature.maximumSubvoxels;
if(x<lo[0]||x>hi[0]||y<lo[1]||y>hi[1])return 0;
if(feather<=0)return static_cast<std::uint32_t>(Q16);
const std::int64_t edge=std::min({x-lo[0],hi[0]-x,y-lo[1],hi[1]-y});
return SmoothQ16(static_cast<std::uint32_t>(std::clamp<std::int64_t>(edge*Q16/feather,0,Q16)));
}
struct HeightfieldColumnCacheEntry
{
const WorldAssemblyRecipeV1* recipe = nullptr;
const WorldAssemblyStratumPlanV1* plan = nullptr;
std::uint64_t recipeHash = 0, planHash = 0;
std::int64_t x = 0, y = 0;
HeightfieldPointSample column{};
};
// Coarse LOD tiles revisit a wider set of gradient-sample columns than the full-resolution
// band. Keep those columns resident for a complete worker tile so the 3D mesher does not
// repeat the procedural biome evaluation for every Z sample.
inline thread_local std::array<HeightfieldColumnCacheEntry, 65536> HeightfieldColumnCache{};
inline thread_local HeightfieldColumnCacheStats HeightfieldColumnStats{};
inline HeightfieldPointSample EvaluateColumn(const WorldAssemblyRecipeV1& recipe,
const WorldAssemblyStratumPlanV1& plan,std::int64_t x,std::int64_t y)
{
HeightfieldPointSample out;
if(recipe.evaluatorVersion>=2)
{
const BiomeSelectionV2 selection=SelectBiomeV2(recipe,x,y);
const auto& primary=recipe.biomes[selection.primary];
const auto& neighbor=recipe.biomes[selection.neighbor];
const std::int64_t primaryHeight=SurfaceHeightV2(recipe,plan,primary,x,y);
const std::int64_t neighborHeight=selection.primary==selection.neighbor?primaryHeight
:SurfaceHeightV2(recipe,plan,neighbor,x,y);
std::int64_t ground=primaryHeight+((neighborHeight-primaryHeight)
*(static_cast<std::int64_t>(Q16)-selection.primaryWeightQ16))/Q16;
const std::int64_t primaryCap=CapHeightV2(recipe,plan,primary,x,y);
const std::int64_t neighborCap=selection.primary==selection.neighbor?primaryCap
:CapHeightV2(recipe,plan,neighbor,x,y);
std::int64_t cap=primaryCap+((neighborCap-primaryCap)
*(static_cast<std::int64_t>(Q16)-selection.primaryWeightQ16))/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)ground+=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);
cap+=MulQ16(MulQ16(noise,feature.parametersSubvoxels[0]),fade);
}
}
}
out.groundHeightSubvoxels=ground;
out.capHeightSubvoxels=cap;
out.biomeId=primary.stableId;
out.neighborBiomeId=neighbor.stableId;
out.biomeWeightQ16=selection.primaryWeightQ16;
out.waterSurfaceSubvoxels=plan.waterSurfaceSubvoxels;
return out;
}
const std::int64_t warpX=MulQ16(recipe.biomeWarpSubvoxels,
Noise2D(x,y,recipe.biomeWarpWavelengthSubvoxels,recipe.seed,0x574152505f585f31ull));
const std::int64_t warpY=MulQ16(recipe.biomeWarpSubvoxels,
Noise2D(x,y,recipe.biomeWarpWavelengthSubvoxels,recipe.seed,0x574152505f595f31ull));
const std::int64_t warpedX=x+warpX,warpedY=y+warpY;
const std::int64_t cellX=FloorDiv(warpedX,recipe.biomeCellSizeSubvoxels);
const std::int64_t cellY=FloorDiv(warpedY,recipe.biomeCellSizeSubvoxels);
std::uint64_t nearestDistance=std::numeric_limits<std::uint64_t>::max();
std::uint64_t secondDistance=std::numeric_limits<std::uint64_t>::max();
std::uint32_t nearestId=0,secondId=0;
const std::uint32_t jitterSeed=recipe.seed^0xa341316cu;
const std::int64_t cellSize=recipe.biomeCellSizeSubvoxels;
for(std::int64_t cy=cellY-2;cy<=cellY+2;++cy)
for(std::int64_t cx=cellX-2;cx<=cellX+2;++cx)
{
const std::uint64_t h=CoordinateHash(jitterSeed,cx,cy,0x42494f4d455f5349ull);
const std::int64_t jitterX=static_cast<std::int64_t>(h&0xffffu)*(cellSize/2)/65535-cellSize/4;
const std::int64_t jitterY=static_cast<std::int64_t>((h>>16)&0xffffu)*(cellSize/2)/65535-cellSize/4;
const std::int64_t siteX=cx*cellSize+cellSize/2+jitterX;
const std::int64_t siteY=cy*cellSize+cellSize/2+jitterY;
const std::int64_t dx=warpedX-siteX,dy=warpedY-siteY;
const std::uint64_t distance=static_cast<std::uint64_t>(dx*dx+dy*dy);
const std::uint32_t biomeId=recipe.biomes[CoordinateHash(jitterSeed,cx,cy,
0x42494f4d455f4944ull)%recipe.biomes.size()].stableId;
if(distance<nearestDistance)
{secondDistance=nearestDistance;secondId=nearestId;nearestDistance=distance;nearestId=biomeId;}
else if(distance<secondDistance&&biomeId!=nearestId)
{secondDistance=distance;secondId=biomeId;}
}
const auto findBiome=[&](std::uint32_t id)->const WorldAssemblyBiomeProfileV1*
{
const auto found=std::find_if(recipe.biomes.begin(),recipe.biomes.end(),
[id](const auto& biome){return biome.stableId==id;});
return found==recipe.biomes.end()?nullptr:&*found;
};
const auto* primary=findBiome(nearestId);
const auto* secondary=findBiome(secondId?secondId:nearestId);
if(!primary||!secondary)return out;
std::uint32_t primaryWeight=65536;
if(recipe.biomeBorderBlendSubvoxels>0&&nearestDistance<secondDistance)
{
const std::uint64_t difference=secondDistance-nearestDistance;
const std::uint64_t denominator=static_cast<std::uint64_t>(2*cellSize)
*static_cast<std::uint64_t>(recipe.biomeBorderBlendSubvoxels);
primaryWeight=SmoothQ16(RatioQ16(difference,denominator));
}
const std::int64_t primaryHeight=ProfileHeight(*primary,warpedX,warpedY,recipe.seed);
const std::int64_t secondaryHeight=ProfileHeight(*secondary,warpedX,warpedY,recipe.seed);
out.groundHeightSubvoxels=(primaryHeight*primaryWeight
+secondaryHeight*(65536-primaryWeight))/65536;
out.biomeId=primary->stableId;out.neighborBiomeId=secondary->stableId;
out.biomeWeightQ16=primaryWeight;
std::int64_t cap=plan.capHeightSubvoxels+MulQ16(plan.capUndulationSubvoxels,
Noise2D(x,y,plan.capWavelengthSubvoxels,recipe.seed,0x4341505f554e4431ull));
out.capHeightSubvoxels=cap;
out.waterSurfaceSubvoxels=plan.waterSurfaceSubvoxels;
return out;
}
inline HeightfieldPointSample CachedColumn(const WorldAssemblyRecipeV1& recipe,
const WorldAssemblyStratumPlanV1& plan,std::int64_t x,std::int64_t y)
{
const std::uint64_t mixed=(static_cast<std::uint64_t>(x)*0x9E3779B185EBCA87ull)
^std::rotl(static_cast<std::uint64_t>(y)*0xC2B2AE3D27D4EB4Full,23)
^static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(&recipe))
^std::rotl(static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(&plan)),41);
auto& entry=HeightfieldColumnCache[static_cast<std::size_t>(mixed)&
(HeightfieldColumnCache.size()-1)];
if(entry.recipe==&recipe&&entry.plan==&plan&&entry.recipeHash==recipe.contentHash
&&entry.planHash==plan.contentHash&&entry.x==x&&entry.y==y)
{++HeightfieldColumnStats.columnHits;return entry.column;}
const auto start=std::chrono::steady_clock::now();
HeightfieldPointSample column=EvaluateColumn(recipe,plan,x,y);
HeightfieldColumnStats.columnComputeMilliseconds+=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-start).count();
++HeightfieldColumnStats.columnMisses;
if(HeightfieldColumnStats.captureCoordinates&&HeightfieldColumnStats.coordinateCaptureComplete)
{
try
{
HeightfieldColumnStats.evaluatedCoordinates.push_back({x,y});
}
catch(...)
{
HeightfieldColumnStats.coordinateCaptureComplete=false;
}
}
entry={&recipe,&plan,recipe.contentHash,plan.contentHash,x,y,column};
return column;
}
inline std::int64_t CliffShelfDensity(const WorldAssemblyFeatureV1& feature,
const WorldAssemblyRecipeV1& recipe,const WorldAssemblyStratumPlanV1& plan,
std::int64_t x,std::int64_t y,std::int64_t z,
const HeightfieldPointSample& localColumn,bool enforceMinimumThickness)
{
const auto& lo=feature.minimumSubvoxels;const auto& hi=feature.maximumSubvoxels;
if(x<lo[0]||x>hi[0]||y<lo[1]||y>hi[1]||z<lo[2]||z>hi[2])
return std::numeric_limits<std::int64_t>::max()/4;
constexpr std::int64_t Feather=8*WorldAssemblySubvoxelsPerVoxel;
const std::int64_t dirX=feature.parametersSubvoxels[0]/WorldAssemblySubvoxelsPerVoxel;
const std::int64_t dirY=feature.parametersSubvoxels[1]/WorldAssemblySubvoxelsPerVoxel;
const std::int64_t height=feature.parametersSubvoxels[2];
const std::int64_t reach=feature.parametersSubvoxels[3];
if((dirX==0&&dirY==0)||height<=0||reach<=0)return std::numeric_limits<std::int64_t>::max()/4;
const std::int64_t centerX=(lo[0]+hi[0])/2,centerY=(lo[1]+hi[1])/2;
const std::int64_t edgeX=dirX>0?lo[0]+Feather:dirX<0?hi[0]-Feather:centerX;
const std::int64_t edgeY=dirY>0?lo[1]+Feather:dirY<0?hi[1]-Feather:centerY;
const std::int64_t dx=x-edgeX,dy=y-edgeY;
const std::int64_t along=dx*dirX+dy*dirY;
const std::int64_t lateral=-dx*dirY+dy*dirX;
const std::int64_t halfWidth=std::max<std::int64_t>(0,
(dirX!=0?(hi[1]-lo[1]):(hi[0]-lo[0]))/2-Feather);
const std::int64_t aboveGround=z-localColumn.groundHeightSubvoxels;
if(aboveGround<=0||aboveGround>=height||along<0||along>reach
||std::abs(lateral)>halfWidth)
return std::numeric_limits<std::int64_t>::max()/4;
const std::uint32_t vertical=SmoothQ16(static_cast<std::uint32_t>(
std::clamp<std::int64_t>(aboveGround*Q16/height,0,Q16)));
// Evaluator v4 samples the source column on the shelf's root line. The offset
// therefore follows horizontal progress through the block and stays fixed for
// every z slice. Advancing it with z can make the shelf switch on at its top;
// sampling the far end everywhere instead can pull a distant cliff cap across
// the whole shelf. Earlier evaluators retain their height-dependent taper.
const std::int64_t sourceOffset=enforceMinimumThickness
?std::clamp<std::int64_t>(along,0,reach):MulQ16(reach,vertical);
const std::int64_t sourceX=x-dirX*sourceOffset;
const std::int64_t sourceY=y-dirY*sourceOffset;
const HeightfieldPointSample source=CachedColumn(recipe,plan,sourceX,sourceY);
if(source.groundHeightSubvoxels<=localColumn.groundHeightSubvoxels)
return std::numeric_limits<std::int64_t>::max()/4;
const std::uint32_t edgeFade=FeatureFadeQ16(feature,x,y,Feather);
const std::int64_t edgeInset=static_cast<std::int64_t>(Q16-edgeFade)*Feather/Q16;
const std::int64_t sourceTop=enforceMinimumThickness
?std::max(source.groundHeightSubvoxels,
localColumn.groundHeightSubvoxels+MinimumCliffShelfThicknessSubvoxels)
:source.groundHeightSubvoxels;
std::int64_t candidate=std::max({localColumn.groundHeightSubvoxels-z,
z-sourceTop,-aboveGround,aboveGround-height});
if(enforceMinimumThickness)
{
// The feather narrows the shelf footprint. It must not offset the top and
// bottom surfaces, or their vertical separation collapses into MC sheets.
candidate=std::max({candidate,-along+edgeInset,along-reach+edgeInset,
std::abs(lateral)-halfWidth+edgeInset});
}
else
candidate=std::max({candidate,-along,along-reach,
std::abs(lateral)-halfWidth})+edgeInset;
return candidate;
}
inline std::int64_t SmoothMinSubvoxels(std::int64_t a,std::int64_t b,
std::int64_t width)
{
if(width<=0)return std::min(a,b);
const std::int64_t h=std::max<std::int64_t>(width-std::abs(a-b),0);
const std::int64_t smoothQ16=h*Q16/width;
return std::min(a,b)-MulQ16(MulQ16(smoothQ16,smoothQ16),width/4);
}
inline bool InInterval(const FieldInterval& interval, std::int64_t density)
{ return interval.known && density >= interval.minimumDensitySubvoxels
&& density <= interval.maximumDensitySubvoxels; }
}
namespace detail_world_assembly
{
inline std::optional<WorldAssemblyManifest> ParseNestedManifest(const VFJson::Value& root,
std::string& error)
{
const VFJson::Value& payload = root.Get("world_assembly");
if (!payload.IsObject()) return std::nullopt;
return world_assembly_detail::ParseManifestValue(payload, error);
}
}
std::optional<WorldAssemblyManifest> WorldAssemblyManifest::FromJson(
std::string_view json, std::string& error)
{
VFJson::Value root;
if (!VFJson::Parser(std::string(json)).Parse(root, error)) return std::nullopt;
return world_assembly_detail::ParseManifestValue(root, error);
}
bool WorldAssemblyManifest::finalize(std::string& error)
{
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; }
if ((plan.strates.empty() && floatingIslands.empty())
|| plan.strates.size() > world_assembly_detail::MaxStrateCount
|| floatingIslands.size() > world_assembly_detail::MaxStrateCount)
{ error = "world assembly must contain SurfaceWorld or floating-island plans within the sixty-four-strate limit"; return false; }
std::vector<std::int32_t> strateIds;
for (auto& stratum : plan.strates)
{
if (stratum.hasRecipeOverride)
{
if (!world_assembly_detail::ValidateRecipe(stratum.recipeOverride, error)) return false;
if (stratum.recipeOverride.seed != recipe.seed)
{ 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, error)) return false;
if (recipe.evaluatorVersion >= 4)
{
if (stratum.routeConstraints.version != 1 || stratum.route.size() < 2)
{ error = "world assembly evaluator 4 requires a versioned entrance-to-exit route"; return false; }
const std::int64_t bottomZ = static_cast<std::int64_t>(stratum.bottomChunkZ)
* 32 * WorldAssemblySubvoxelsPerVoxel;
const std::int64_t topZ = (static_cast<std::int64_t>(stratum.topChunkZ) + 1)
* 32 * WorldAssemblySubvoxelsPerVoxel;
const std::int64_t halfHeight = stratum.routeConstraints.capsuleHeightSubvoxels / 2;
for (const auto& point : stratum.route)
{
if (point.positionSubvoxels[2] - halfHeight < bottomZ
|| point.positionSubvoxels[2] + halfHeight > topZ)
{ error = "world assembly route capsule escapes its finite stratum Z bounds"; return false; }
}
for (const auto& feature : stratum.features)
if (feature.type == 4 && feature.parametersSubvoxels[2]
< 2 * WorldAssemblySubvoxelsPerVoxel)
{ error = "world assembly evaluator 4 cliff shelf is thinner than its two-voxel minimum"; return false; }
}
if (std::find(strateIds.begin(), strateIds.end(), stratum.strateIndex) != strateIds.end())
{ error = "world assembly plan contains a duplicate strate index"; return false; }
strateIds.push_back(stratum.strateIndex);
stratum.contentHash = world_assembly_detail::HashStratum(stratum);
}
std::vector<std::int32_t> islandStrateIds;
for (auto& islands : floatingIslands)
{
if (islands.strateIndex < 0 || islands.topChunkZ < islands.bottomChunkZ
|| std::find(islandStrateIds.begin(), islandStrateIds.end(), islands.strateIndex) != islandStrateIds.end())
{ error = "world assembly contains invalid or duplicate floating-island strate bounds"; return false; }
if (!islands.manifest.validate(error)) return false;
if (islands.manifest.recipe.seed != recipe.seed)
{ error = "floating-island seed does not match the world assembly season seed"; return false; }
const auto& Scale = islands.manifest.recipe;
if (Scale.ownerToCoreScaleNumerator == 0 || Scale.ownerToCoreScaleDenominator == 0)
{ error = "floating-island owner-to-Core scale is invalid"; return false; }
const double CoreVoxelsPerOwnerChunk = static_cast<double>(CoreTileVoxelCount)
* Scale.ownerToCoreScaleNumerator / Scale.ownerToCoreScaleDenominator;
const std::int64_t bottomQ = static_cast<std::int64_t>(std::llround(
static_cast<double>(islands.bottomChunkZ) * CoreVoxelsPerOwnerChunk
* WorldAssemblySubvoxelsPerVoxel));
const std::int64_t topQ = static_cast<std::int64_t>(std::llround(
static_cast<double>(static_cast<std::int64_t>(islands.topChunkZ) + 1)
* CoreVoxelsPerOwnerChunk * WorldAssemblySubvoxelsPerVoxel));
if (islands.manifest.recipe.layoutMinimumZSubvoxels < bottomQ
|| islands.manifest.recipe.layoutMaximumZSubvoxels > topQ)
{ error = "floating-island recipe layout escapes the resolved owner strate bounds"; return false; }
islandStrateIds.push_back(islands.strateIndex);
}
recipe.contentHash = world_assembly_detail::HashRecipe(recipe);
plan.contentHash = world_assembly_detail::HashPlan(plan);
contentHash = world_assembly_detail::HashManifest(*this);
return true;
}
bool WorldAssemblyManifest::validate(std::string& error) const
{
WorldAssemblyManifest copy = *this;
const std::uint64_t expectedRecipe = recipe.contentHash;
const std::uint64_t expectedPlan = plan.contentHash;
const std::uint64_t expectedManifest = contentHash;
std::vector<std::uint64_t> expectedStrates;
std::vector<std::uint64_t> expectedStratumRecipes;
for (const auto& stratum : plan.strates)
{
expectedStrates.push_back(stratum.contentHash);
expectedStratumRecipes.push_back(stratum.hasRecipeOverride
? stratum.recipeOverride.contentHash : 0);
}
std::vector<std::uint64_t> expectedIslandManifests;
for (const auto& islands : floatingIslands) expectedIslandManifests.push_back(islands.manifest.contentHash);
if (!copy.finalize(error)) return false;
if (expectedRecipe != copy.recipe.contentHash || expectedPlan != copy.plan.contentHash
|| expectedManifest != copy.contentHash || expectedStrates.size() != copy.plan.strates.size()
|| expectedStratumRecipes.size() != copy.plan.strates.size()
|| expectedIslandManifests.size() != copy.floatingIslands.size())
{ error = "world assembly recipe, plan or manifest content hash does not match its fields"; return false; }
for (std::size_t i = 0; i < expectedStrates.size(); ++i)
if (expectedStrates[i] != copy.plan.strates[i].contentHash
|| expectedStratumRecipes[i] != (copy.plan.strates[i].hasRecipeOverride
? copy.plan.strates[i].recipeOverride.contentHash : 0))
{ error = "world assembly stratum content hash does not match its fields"; return false; }
for (std::size_t i = 0; i < expectedIslandManifests.size(); ++i)
if (expectedIslandManifests[i] != copy.floatingIslands[i].manifest.contentHash)
{ error = "world assembly floating-island content hash does not match its fields"; return false; }
return true;
}
std::string WorldAssemblyManifest::ToJson() const
{
std::string error;
WorldAssemblyManifest value = *this;
if (!value.finalize(error)) return {};
std::ostringstream out;
out << "{\"schema_version\":" << value.schemaVersion
<< ",\"content_hash\":\"" << world_assembly_detail::HashText(value.contentHash)
<< "\",\"recipe\":" << world_assembly_detail::RecipeJson(value.recipe)
<< ",\"plan\":{\"schema_version\":" << value.plan.schemaVersion
<< ",\"content_hash\":\"" << world_assembly_detail::HashText(value.plan.contentHash)
<< "\",\"strates\":[";
for (std::size_t i = 0; i < value.plan.strates.size(); ++i)
{
const auto& stratum = value.plan.strates[i];
if (i) out << ',';
out << "{\"strate_index\":" << stratum.strateIndex << ",\"top_chunk_z\":" << stratum.topChunkZ
<< ",\"bottom_chunk_z\":" << stratum.bottomChunkZ
<< ",\"cap_height_q\":" << stratum.capHeightSubvoxels
<< ",\"cap_undulation_q\":" << stratum.capUndulationSubvoxels
<< ",\"cap_wavelength_q\":" << stratum.capWavelengthSubvoxels
;
const bool hasExtendedCap = stratum.capDetailAmplitudeSubvoxels != 0
|| stratum.capRidgeAmplitudeSubvoxels != 0 || stratum.waterEnabled
|| stratum.waterSurfaceSubvoxels != 0;
if (hasExtendedCap)
out << ",\"cap_detail_amplitude_q\":" << stratum.capDetailAmplitudeSubvoxels
<< ",\"cap_detail_wavelength_q\":" << stratum.capDetailWavelengthSubvoxels
<< ",\"cap_ridge_amplitude_q\":" << stratum.capRidgeAmplitudeSubvoxels
<< ",\"cap_ridge_wavelength_q\":" << stratum.capRidgeWavelengthSubvoxels
<< ",\"water_surface_q\":" << stratum.waterSurfaceSubvoxels
<< ",\"water_enabled\":" << (stratum.waterEnabled ? "true" : "false");
if (stratum.hasRecipeOverride)
out << ",\"recipe_override\":"
<< world_assembly_detail::RecipeJson(stratum.recipeOverride);
out
<< ",\"content_hash\":\"" << world_assembly_detail::HashText(stratum.contentHash)
<< "\",\"features\":[";
for (std::size_t f = 0; f < stratum.features.size(); ++f)
{
const auto& feature = stratum.features[f];
if (f) out << ',';
out << "{\"stable_id\":\"" << world_assembly_detail::HashText(feature.stableId)
<< "\",\"type\":" << feature.type << ",\"min_q\":["
<< feature.minimumSubvoxels[0] << ',' << feature.minimumSubvoxels[1] << ',' << feature.minimumSubvoxels[2]
<< "],\"max_q\":[" << feature.maximumSubvoxels[0] << ',' << feature.maximumSubvoxels[1]
<< ',' << feature.maximumSubvoxels[2] << "],\"parameters_q\":["
<< feature.parametersSubvoxels[0] << ',' << feature.parametersSubvoxels[1] << ','
<< feature.parametersSubvoxels[2] << ',' << feature.parametersSubvoxels[3] << "]}";
}
out << ']';
if (stratum.routeConstraints.version != 0)
{
const auto& constraints = stratum.routeConstraints;
out << ",\"route_constraints\":{\"version\":" << constraints.version
<< ",\"capsule_radius_q\":" << constraints.capsuleRadiusSubvoxels
<< ",\"capsule_height_q\":" << constraints.capsuleHeightSubvoxels
<< ",\"maximum_grade_q16\":" << constraints.maximumGradeQ16
<< ",\"sweep_spacing_q\":" << constraints.sweepSpacingSubvoxels
<< ",\"support_probe_radius_q\":" << constraints.supportProbeRadiusSubvoxels
<< ",\"support_search_depth_q\":" << constraints.supportSearchDepthSubvoxels
<< ",\"minimum_clearance_q\":" << constraints.minimumClearanceSubvoxels
<< ",\"minimum_support_probes\":" << constraints.minimumSupportProbes << '}';
}
out << ",\"route\":[";
for (std::size_t p = 0; p < stratum.route.size(); ++p)
{
const auto& point = stratum.route[p];
if (p) out << ',';
out << "{\"feature_id\":\"" << world_assembly_detail::HashText(point.stableFeatureId)
<< "\",\"position_q\":[" << point.positionSubvoxels[0] << ','
<< point.positionSubvoxels[1] << ',' << point.positionSubvoxels[2]
<< "],\"radius_q\":" << point.radiusSubvoxels << '}';
}
out << "]}";
}
if (value.floatingIslands.empty()) out << "]}}";
else
{
out << "]},\"floating_islands\":[";
for (std::size_t i = 0; i < value.floatingIslands.size(); ++i)
{
const auto& islands = value.floatingIslands[i];
if (i) out << ',';
const std::string nested = islands.manifest.ToJson();
if (nested.empty()) return {};
out << "{\"strate_index\":" << islands.strateIndex
<< ",\"top_chunk_z\":" << islands.topChunkZ
<< ",\"bottom_chunk_z\":" << islands.bottomChunkZ
<< ",\"manifest\":" << nested << '}';
}
out << "]}";
}
return out.str();
}
Heightfield2D::Heightfield2D(const WorldAssemblyRecipeV1& recipe,
const WorldAssemblyStratumPlanV1& plan) noexcept
: recipe_(plan.hasRecipeOverride ? &plan.recipeOverride : &recipe), plan_(&plan)
{}
bool Heightfield2D::isValid() const noexcept
{
if (!recipe_ || !plan_ || recipe_->schemaVersion != WorldAssemblySchemaVersion
|| recipe_->evaluatorVersion == 0
|| recipe_->evaluatorVersion > WorldAssemblyEvaluatorVersion
|| recipe_->unitsPerVoxel != WorldAssemblySubvoxelsPerVoxel
|| recipe_->biomes.empty() || recipe_->biomes.size() > world_assembly_detail::MaxBiomeCount
|| recipe_->biomeCellSizeSubvoxels <= 0 || recipe_->biomeWarpWavelengthSubvoxels <= 0
|| recipe_->biomeWarpSubvoxels < 0 || recipe_->biomeBorderBlendSubvoxels < 0
|| plan_->strateIndex < 0 || plan_->topChunkZ < plan_->bottomChunkZ
|| plan_->capWavelengthSubvoxels <= 0 || plan_->capUndulationSubvoxels < 0)
return false;
return std::all_of(recipe_->biomes.begin(), recipe_->biomes.end(), [](const auto& biome)
{
return biome.stableId != 0 && biome.continentAmplitudeSubvoxels >= 0
&& biome.mountainAmplitudeSubvoxels >= 0 && biome.detailAmplitudeSubvoxels >= 0
&& biome.continentWavelengthSubvoxels > 0 && biome.mountainWavelengthSubvoxels > 0
&& biome.detailWavelengthSubvoxels > 0 && biome.reliefWavelengthSubvoxels > 0
&& biome.heightWarpWavelengthSubvoxels > 0 && biome.cliffSampleDistanceSubvoxels > 0;
});
}
std::array<std::int64_t,2> Heightfield2D::capHeightBounds() const noexcept
{
if(!isValid())return {0,0};
long double Minimum=std::numeric_limits<long double>::infinity();
long double Maximum=-std::numeric_limits<long double>::infinity();
const auto IncludeBiome=[&](const WorldAssemblyBiomeProfileV1& Biome)
{
const long double Base=Biome.hasCapProfile
?static_cast<long double>(Biome.capHeightSubvoxels)
:static_cast<long double>(plan_->capHeightSubvoxels);
const long double Undulation=Biome.hasCapProfile
?static_cast<long double>(Biome.capUndulationSubvoxels)
:static_cast<long double>(plan_->capUndulationSubvoxels);
const long double Detail=Biome.hasCapProfile
?static_cast<long double>(Biome.capDetailAmplitudeSubvoxels)
:static_cast<long double>(plan_->capDetailAmplitudeSubvoxels);
const long double Ridge=Biome.hasCapProfile
?static_cast<long double>(Biome.capRidgeAmplitudeSubvoxels)
:static_cast<long double>(plan_->capRidgeAmplitudeSubvoxels);
Minimum=std::min(Minimum,Base-Undulation-Detail-Ridge);
Maximum=std::max(Maximum,Base+Undulation);
};
if(recipe_->biomes.empty())
{
const WorldAssemblyBiomeProfileV1 Fallback{};
IncludeBiome(Fallback);
}
else for(const auto& Biome:recipe_->biomes)IncludeBiome(Biome);
long double FeatureAmplitude=0.0L;
for(const auto& Feature:plan_->features)
if(Feature.type==3)
FeatureAmplitude+=std::abs(static_cast<long double>(Feature.parametersSubvoxels[0]));
Minimum-=FeatureAmplitude;Maximum+=FeatureAmplitude;
if(recipe_->evaluatorVersion>=5)
{
const long double TopSubvoxels=(static_cast<long double>(plan_->topChunkZ)+1.0L)
*CoreTileVoxelCount*WorldAssemblySubvoxelsPerVoxel;
Minimum=std::min(Minimum,TopSubvoxels);
Maximum=std::min(Maximum,TopSubvoxels);
}
const long double Int64Minimum=static_cast<long double>(std::numeric_limits<std::int64_t>::min());
const long double Int64Maximum=static_cast<long double>(std::numeric_limits<std::int64_t>::max());
return {static_cast<std::int64_t>(std::clamp(Minimum,Int64Minimum,Int64Maximum)),
static_cast<std::int64_t>(std::clamp(Maximum,Int64Minimum,Int64Maximum))};
}
std::int64_t Heightfield2D::farSurfaceCapHeightSubvoxels(std::int64_t x,
std::int64_t y) const noexcept
{
if(!isValid())return 0;
const HeightfieldPointSample column=point(x,y,0);
if(recipe_->evaluatorVersion<5)return column.capHeightSubvoxels;
// Clip only the far render surface. Keep the near density evaluator bit-identical.
// Borner uniquement le plafond lointain. L'evaluateur de densite proche reste identique.
const std::int64_t ownedUpperBoundary=(static_cast<std::int64_t>(plan_->topChunkZ)+1)
*CoreTileVoxelCount*WorldAssemblySubvoxelsPerVoxel;
return std::min(column.capHeightSubvoxels,ownedUpperBoundary);
}
WorldAssemblyFeatureTileListV1 Heightfield2D::buildFeatureTileList(
const std::array<std::int64_t,3>& minimum,
const std::array<std::int64_t,3>& maximum) const
{
WorldAssemblyFeatureTileListV1 result;
// One shelf in the staged owner plan has no useful scan to shorten. Keep its full evaluator.
// Une seule corniche dans le plan auteur ne justifie pas la selection par echantillon.
result.candidateEvaluationUseful=plan_->features.size()>1;
result.minimumSubvoxels=minimum;
result.maximumSubvoxels=maximum;
std::vector<WorldAssemblyFeatureBoundsV1> bounds;
bounds.reserve(plan_->features.size());
for(const auto& feature:plan_->features)
{
WorldAssemblyFeatureBoundsV1 bound{feature.stableId,
feature.minimumSubvoxels,feature.maximumSubvoxels};
// Column writers affect every Z; finite volumes have an exact no-op outside their box.
// Les modificateurs de colonne agissent sur tout Z; les volumes sont neutres hors boite.
if(feature.type==1||feature.type==3)
{
bound.minimumSubvoxels[2]=std::numeric_limits<std::int64_t>::min();
bound.maximumSubvoxels[2]=std::numeric_limits<std::int64_t>::max();
}
else if(feature.type!=2&&feature.type!=4)
{
// Unknown future operations must opt into a proof before they can be culled.
// Une operation inconnue reste active tant que sa preuve manque.
bound.minimumSubvoxels.fill(std::numeric_limits<std::int64_t>::min());
bound.maximumSubvoxels.fill(std::numeric_limits<std::int64_t>::max());
}
bounds.push_back(bound);
}
result.featureIndices=BuildWorldAssemblyFeatureTileListV1(bounds,minimum,maximum);
return result;
}
HeightfieldPointSample Heightfield2D::point(std::int64_t x, std::int64_t y,
std::int64_t z) const noexcept
{
HeightfieldPointSample out;
if (!isValid() || world_assembly_detail::OutsideRange(x)
|| world_assembly_detail::OutsideRange(y)
|| world_assembly_detail::OutsideRange(z)) return out;
using namespace world_assembly_detail;
++HeightfieldColumnStats.pointCalls;
out=CachedColumn(*recipe_,*plan_,x,y);
out.densitySubvoxels=std::min(z-out.groundHeightSubvoxels,
out.capHeightSubvoxels-z);
if(recipe_->evaluatorVersion>=2)
{
const auto* list=ScopedWorldAssemblyFeatureQueriesV1::find(recipe_,plan_,x,y,z);
for(const auto& feature:FeatureRange{plan_->features,list})
{
if(feature.type==2)
{
const auto& lo=feature.minimumSubvoxels;const auto& hi=feature.maximumSubvoxels;
if(x<lo[0]||x>hi[0]||y<lo[1]||y>hi[1]||z<lo[2]||z>hi[2])continue;
const std::int64_t depth=std::min({x-lo[0],hi[0]-x,y-lo[1],hi[1]-y,
z-lo[2],hi[2]-z});
out.densitySubvoxels=std::min(out.densitySubvoxels,-depth);
}
else if(feature.type==4&&recipe_->evaluatorVersion>=3)
{
const std::int64_t shelf=CliffShelfDensity(feature,*recipe_,*plan_,x,y,z,out,
recipe_->evaluatorVersion>=4);
out.densitySubvoxels=SmoothMinSubvoxels(out.densitySubvoxels,shelf,
4*WorldAssemblySubvoxelsPerVoxel);
}
}
}
if(recipe_->evaluatorVersion>=2&&plan_->waterEnabled&&out.densitySubvoxels>=0
&&z>out.groundHeightSubvoxels&&z<plan_->waterSurfaceSubvoxels
&&out.groundHeightSubvoxels<plan_->waterSurfaceSubvoxels)
out.waterFillSubvoxels=std::min(z-out.groundHeightSubvoxels,
plan_->waterSurfaceSubvoxels-z);
return out;
}
void Heightfield2D::resetThreadColumnCacheStats(bool captureCoordinates, bool clearCache,
std::uint32_t octaveDrop) noexcept
{
// Diagnostic probes may clear old tile entries before comparing isolated sample grids.
// Les sondes de mesure peuvent vider les entrees d'une tuile avant de comparer deux grilles.
if(clearCache)world_assembly_detail::HeightfieldColumnCache.fill(
world_assembly_detail::HeightfieldColumnCacheEntry{});
auto& Stats=world_assembly_detail::HeightfieldColumnStats;
world_assembly_detail::HeightfieldColumnOctaveDrop=octaveDrop;
Stats.pointCalls=0;
Stats.columnHits=0;
Stats.columnMisses=0;
Stats.columnComputeMilliseconds=0.0;
Stats.captureCoordinates=captureCoordinates;
Stats.coordinateCaptureComplete=true;
Stats.evaluatedCoordinates.clear();
}
const HeightfieldColumnCacheStats& Heightfield2D::threadColumnCacheStats() noexcept
{ return world_assembly_detail::HeightfieldColumnStats; }
HeightfieldBoxClass Heightfield2D::classifyLatticeBox(std::int64_t minimumX,
std::int64_t minimumY,std::int64_t minimumZ,std::int64_t maximumX,
std::int64_t maximumY,std::int64_t maximumZ,std::int32_t stepVoxels) const noexcept
{
using namespace world_assembly_detail;
if(!isValid()||stepVoxels<=0||minimumX>maximumX||minimumY>maximumY||minimumZ>maximumZ
||OutsideRange(minimumX)||OutsideRange(minimumY)||OutsideRange(minimumZ)
||OutsideRange(maximumX)||OutsideRange(maximumY)||OutsideRange(maximumZ))
return HeightfieldBoxClass::unknown;
for(const auto& feature:plan_->features)
{
if(feature.type!=2&&feature.type!=4)continue;
if(feature.minimumSubvoxels[0]<=maximumX&&feature.maximumSubvoxels[0]>=minimumX
&&feature.minimumSubvoxels[1]<=maximumY&&feature.maximumSubvoxels[1]>=minimumY
&&feature.minimumSubvoxels[2]<=maximumZ&&feature.maximumSubvoxels[2]>=minimumZ)
return HeightfieldBoxClass::mixed;
}
const std::int64_t stride=static_cast<std::int64_t>(stepVoxels)
*WorldAssemblySubvoxelsPerVoxel;
if((maximumX-minimumX)%stride!=0||(maximumY-minimumY)%stride!=0
||(maximumX-minimumX)/stride>2048||(maximumY-minimumY)/stride>2048)
return HeightfieldBoxClass::unknown;
std::int64_t groundMin=std::numeric_limits<std::int64_t>::max();
std::int64_t groundMax=std::numeric_limits<std::int64_t>::min();
std::int64_t capMin=std::numeric_limits<std::int64_t>::max();
std::int64_t capMax=std::numeric_limits<std::int64_t>::min();
for(std::int64_t y=minimumY;;y+=stride)
{
for(std::int64_t x=minimumX;;x+=stride)
{
const auto column=point(x,y,0);
groundMin=std::min(groundMin,column.groundHeightSubvoxels);
groundMax=std::max(groundMax,column.groundHeightSubvoxels);
capMin=std::min(capMin,column.capHeightSubvoxels);
capMax=std::max(capMax,column.capHeightSubvoxels);
if(x==maximumX)break;
}
if(y==maximumY)break;
}
if(minimumZ>groundMax&&maximumZ<capMin)return HeightfieldBoxClass::allAir;
if(maximumZ<groundMin||minimumZ>capMax)return HeightfieldBoxClass::allSolid;
return HeightfieldBoxClass::mixed;
}
FieldInterval Heightfield2D::interval(std::int64_t minX, std::int64_t minY,
std::int64_t minZ, std::int64_t maxX, std::int64_t maxY, std::int64_t maxZ) const noexcept
{
FieldInterval out;
if (!isValid() || minX > maxX || minY > maxY || minZ > maxZ
|| world_assembly_detail::OutsideRange(minX)
|| world_assembly_detail::OutsideRange(minY)
|| world_assembly_detail::OutsideRange(minZ)
|| world_assembly_detail::OutsideRange(maxX)
|| world_assembly_detail::OutsideRange(maxY)
|| world_assembly_detail::OutsideRange(maxZ)) return out;
std::int64_t groundMin = std::numeric_limits<std::int64_t>::max();
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)
{
for(const auto& biome:recipe_->biomes)
{
const std::int64_t displacement=biome.continentAmplitudeSubvoxels
+biome.mountainAmplitudeSubvoxels+2*biome.detailAmplitudeSubvoxels
+biome.cliffMaximumDisplacementSubvoxels;
groundMin=std::min(groundMin,biome.baseHeightSubvoxels-displacement);
groundMax=std::max(groundMax,biome.baseHeightSubvoxels+displacement);
if(plan_->waterEnabled&&biome.beachWidthSubvoxels>0)
{
groundMin=std::min(groundMin,plan_->waterSurfaceSubvoxels-biome.beachWidthSubvoxels);
groundMax=std::max(groundMax,plan_->waterSurfaceSubvoxels+biome.beachWidthSubvoxels);
}
if(biome.hasCapProfile)
{
const std::int64_t profileCapDown=biome.capUndulationSubvoxels*5/2
+biome.capDetailAmplitudeSubvoxels*5/2+biome.capRidgeAmplitudeSubvoxels;
const std::int64_t profileCapUp=biome.capUndulationSubvoxels*5/2;
capMin=std::min(capMin,biome.capHeightSubvoxels-profileCapDown);
capMax=std::max(capMax,biome.capHeightSubvoxels+profileCapUp);
}
}
std::int64_t localCliffMagnitude=0,localCapMagnitude=0;
for(const auto& feature:plan_->features)
{
if(feature.type==1)localCliffMagnitude=std::max(localCliffMagnitude,
std::abs(feature.parametersSubvoxels[0]));
if(feature.type==3)localCapMagnitude+=std::abs(feature.parametersSubvoxels[0]);
}
groundMin-=localCliffMagnitude;groundMax+=localCliffMagnitude;
const std::int64_t capDown=plan_->capUndulationSubvoxels*5/2
+plan_->capDetailAmplitudeSubvoxels*5/2+plan_->capRidgeAmplitudeSubvoxels;
const std::int64_t capUp=plan_->capUndulationSubvoxels*5/2+localCapMagnitude;
capMin=std::min(capMin,plan_->capHeightSubvoxels-capDown-localCapMagnitude);
capMax=std::max(capMax,plan_->capHeightSubvoxels+capUp);
}
for (const auto& biome : recipe_->biomes)
{
if(recipe_->evaluatorVersion<2)
{
const std::int64_t amplitude=biome.continentAmplitudeSubvoxels
+biome.mountainAmplitudeSubvoxels+biome.detailAmplitudeSubvoxels;
groundMin=std::min(groundMin,biome.baseHeightSubvoxels-amplitude);
groundMax=std::max(groundMax,biome.baseHeightSubvoxels+amplitude);
}
}
const std::int64_t groundLower = minZ - groundMax;
const std::int64_t groundUpper = maxZ - groundMin;
const std::int64_t capLower = capMin - maxZ;
const std::int64_t capUpper = capMax - minZ;
out.known = true;
out.minimumDensitySubvoxels = std::min(groundLower, capLower);
out.maximumDensitySubvoxels = std::min(groundUpper, capUpper);
if(recipe_->evaluatorVersion>=2)
{
for(const auto& feature:plan_->features)
{
if(feature.type!=2&&feature.type!=4)continue;
bool overlap=true;
for(std::size_t axis=0;axis<3;++axis)
overlap=overlap&&feature.minimumSubvoxels[axis]<=std::array<std::int64_t,3>{maxX,maxY,maxZ}[axis]
&&feature.maximumSubvoxels[axis]>=std::array<std::int64_t,3>{minX,minY,minZ}[axis];
if(!overlap)continue;
if(feature.type==2)
{
const std::int64_t depth=std::min({
(feature.maximumSubvoxels[0]-feature.minimumSubvoxels[0])/2,
(feature.maximumSubvoxels[1]-feature.minimumSubvoxels[1])/2,
(feature.maximumSubvoxels[2]-feature.minimumSubvoxels[2])/2});
out.minimumDensitySubvoxels=std::min(out.minimumDensitySubvoxels,-depth);
}
else
out.minimumDensitySubvoxels=std::min(out.minimumDensitySubvoxels,
-feature.parametersSubvoxels[2]-WorldAssemblySubvoxelsPerVoxel);
}
}
return out;
}
FieldInfluenceBounds Heightfield2D::influenceBounds() const noexcept
{
FieldInfluenceBounds out;
if (!isValid()) return out;
out.minimumZSubvoxels = static_cast<std::int64_t>(plan_->bottomChunkZ) * 32 * WorldAssemblySubvoxelsPerVoxel;
out.maximumZSubvoxels = (static_cast<std::int64_t>(plan_->topChunkZ) + 1) * 32 * WorldAssemblySubvoxelsPerVoxel;
return out;
}
FieldDetailEnvelope Heightfield2D::detailEnvelope() const noexcept
{
FieldDetailEnvelope out;
if (!isValid()) 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();
for (const auto& biome : recipe_->biomes)
{
const std::int64_t surfaceAmplitude=biome.continentAmplitudeSubvoxels
+biome.mountainAmplitudeSubvoxels+biome.detailAmplitudeSubvoxels
+biome.cliffMaximumDisplacementSubvoxels+biome.beachWidthSubvoxels;
maximum = std::max(maximum,surfaceAmplitude);
if(biome.hasCapProfile)
maximum=std::max(maximum,biome.capUndulationSubvoxels*5/2
+biome.capDetailAmplitudeSubvoxels*5/2+biome.capRidgeAmplitudeSubvoxels);
minimumFeature = std::min({minimumFeature, biome.continentWavelengthSubvoxels/8,
biome.mountainWavelengthSubvoxels/8, biome.detailWavelengthSubvoxels/4,
biome.reliefWavelengthSubvoxels/2, biome.heightWarpWavelengthSubvoxels,
biome.cliffSampleDistanceSubvoxels});
}
minimumFeature=std::min({minimumFeature,plan_->capWavelengthSubvoxels/4,
plan_->capDetailWavelengthSubvoxels/4,plan_->capRidgeWavelengthSubvoxels/8});
for(const auto& feature:plan_->features)
{
if(feature.type==1)maximum=std::max(maximum,std::abs(feature.parametersSubvoxels[0]));
if(feature.type==2)
maximum=std::max(maximum,std::min({
(feature.maximumSubvoxels[0]-feature.minimumSubvoxels[0])/2,
(feature.maximumSubvoxels[1]-feature.minimumSubvoxels[1])/2,
(feature.maximumSubvoxels[2]-feature.minimumSubvoxels[2])/2}));
if(feature.type==4)maximum=std::max(maximum,feature.parametersSubvoxels[2]
+WorldAssemblySubvoxelsPerVoxel);
if(feature.type==4&&recipe_->evaluatorVersion>=4)
minimumFeature=std::min(minimumFeature,
world_assembly_detail::MinimumCliffShelfThicknessSubvoxels);
if(feature.type==3)maximum=std::max(maximum,std::abs(feature.parametersSubvoxels[0]));
}
out.maximumAbsoluteDisplacementSubvoxels = maximum;
out.minimumFeatureSizeSubvoxels = std::max<std::int64_t>(1,
std::min(minimumFeature, recipe_->biomeBorderBlendSubvoxels > 0
? recipe_->biomeBorderBlendSubvoxels : minimumFeature));
return out;
}
FieldBlockContract Heightfield2D::contract() const noexcept
{
return {ChannelBit(FieldChannel::biome),
ChannelBit(FieldChannel::height) | ChannelBit(FieldChannel::rock)
| ChannelBit(FieldChannel::water) | ChannelBit(FieldChannel::waterSurface),
recipe_?recipe_->evaluatorVersion:WorldAssemblyEvaluatorVersion};
}
WorldAssemblyBoundsAuditResult AuditHeightfieldBounds(const Heightfield2D& block,
std::uint32_t boxCount, std::uint64_t seed)
{
WorldAssemblyBoundsAuditResult result;
const auto* recipe=block.recipe();
const auto* plan=block.plan();
if (!block.isValid() || !recipe || !plan) return result;
boxCount = std::clamp<std::uint32_t>(boxCount, 1, 100000);
const auto random = [&seed]()
{
seed = world_assembly_detail::Mix64(seed);
return seed;
};
const auto signedVoxel = [&random]()
{ return static_cast<std::int64_t>(random() % 12001) - 6000; };
const auto testBox=[&](std::int64_t x,std::int64_t y,std::int64_t z,
std::int64_t halfX,std::int64_t halfY,std::int64_t halfZ,
std::uint64_t* category,bool countExceptionPoints,const char* tag)
{
const FieldInterval bounds=block.interval(x-halfX,y-halfY,z-halfZ,
x+halfX,y+halfY,z+halfZ);
++result.boxesTested;
if(category)++*category;
if(x<0||y<0)++result.negativeCoordinateBoxes;
if(!bounds.known){++result.unknownBounds;return;}
for(int iz=0;iz<=2;++iz)
for(int iy=0;iy<=2;++iy)
for(int ix=0;ix<=2;++ix)
{
const std::int64_t px=x-halfX+halfX*ix;
const std::int64_t py=y-halfY+halfY*iy;
const std::int64_t pz=z-halfZ+halfZ*iz;
const auto sample=block.point(px,py,pz);
++result.pointSamples;
if(countExceptionPoints)++result.exceptionPointSamples;
if(!world_assembly_detail::InInterval(bounds,sample.densitySubvoxels))
{
++result.missedBounds;
if(result.missedBounds<=6)
std::fprintf(stderr,"[WorldAssemblerBoundMiss] tag=%s box=(%lld,%lld,%lld) half=(%lld,%lld,%lld) bounds=[%lld,%lld] point=(%lld,%lld,%lld) density=%lld\n",
tag,static_cast<long long>(x),static_cast<long long>(y),static_cast<long long>(z),
static_cast<long long>(halfX),static_cast<long long>(halfY),static_cast<long long>(halfZ),
static_cast<long long>(bounds.minimumDensitySubvoxels),
static_cast<long long>(bounds.maximumDensitySubvoxels),
static_cast<long long>(px),static_cast<long long>(py),static_cast<long long>(pz),
static_cast<long long>(sample.densitySubvoxels));
}
}
};
for (std::uint32_t i = 0; i < boxCount; ++i)
{
std::int64_t x = signedVoxel() * WorldAssemblySubvoxelsPerVoxel;
std::int64_t y = signedVoxel() * WorldAssemblySubvoxelsPerVoxel;
if(i%4==0){x=-std::abs(x);y=-std::abs(y);}
const auto column = block.point(x, y, 0);
const std::int64_t z = column.groundHeightSubvoxels
+ (static_cast<std::int64_t>(random() % 257) - 128) * WorldAssemblySubvoxelsPerVoxel;
const std::int64_t halfX = static_cast<std::int64_t>(1 + random() % 64) * WorldAssemblySubvoxelsPerVoxel;
const std::int64_t halfY = static_cast<std::int64_t>(1 + random() % 64) * WorldAssemblySubvoxelsPerVoxel;
const std::int64_t halfZ = static_cast<std::int64_t>(1 + random() % 64) * WorldAssemblySubvoxelsPerVoxel;
testBox(x,y,z,halfX,halfY,halfZ,nullptr,false,"random");
}
const std::int64_t cell=recipe->biomeCellSizeSubvoxels;
const std::int64_t searchExtent=2*cell;
const std::int64_t borderStep=std::max<std::int64_t>(cell/8,
32*WorldAssemblySubvoxelsPerVoxel);
for(std::int64_t y=-searchExtent;y<=searchExtent&&result.biomeBorderBoxes<16;y+=borderStep)
for(std::int64_t x=-searchExtent;x<=searchExtent&&result.biomeBorderBoxes<16;x+=borderStep)
{
const auto column=block.point(x,y,0);
if(column.biomeId==column.neighborBiomeId||column.biomeWeightQ16>=65536)continue;
testBox(x,y,column.groundHeightSubvoxels,16*WorldAssemblySubvoxelsPerVoxel,
16*WorldAssemblySubvoxelsPerVoxel,8*WorldAssemblySubvoxelsPerVoxel,
&result.biomeBorderBoxes,false,"border");
}
const std::int64_t capStep=std::max<std::int64_t>(cell/2,
64*WorldAssemblySubvoxelsPerVoxel);
for(std::int64_t y=-capStep;y<=capStep;y+=capStep)
for(std::int64_t x=-capStep;x<=capStep;x+=capStep)
{
const auto column=block.point(x,y,0);
testBox(x,y,column.capHeightSubvoxels,8*WorldAssemblySubvoxelsPerVoxel,
8*WorldAssemblySubvoxelsPerVoxel,16*WorldAssemblySubvoxelsPerVoxel,
&result.capBoxes,false,"cap");
}
bool hasCliffProfile=false;
for(const auto& biome:recipe->biomes)hasCliffProfile|=biome.cliffStrengthQ16>0;
if(hasCliffProfile)
{
const std::int64_t d=16*WorldAssemblySubvoxelsPerVoxel;
const std::int64_t step=std::max<std::int64_t>(cell/10,
64*WorldAssemblySubvoxelsPerVoxel);
for(std::int64_t y=-2*cell;y<=2*cell&&result.cliffBoxes<16;y+=step)
for(std::int64_t x=-2*cell;x<=2*cell&&result.cliffBoxes<16;x+=step)
{
const auto center=block.point(x,y,0);
const auto xp=block.point(x+d,y,0),xm=block.point(x-d,y,0);
const auto yp=block.point(x,y+d,0),ym=block.point(x,y-d,0);
const std::int64_t gradient=std::max(std::abs(xp.groundHeightSubvoxels-xm.groundHeightSubvoxels),
std::abs(yp.groundHeightSubvoxels-ym.groundHeightSubvoxels));
if(gradient<d/2)continue;
testBox(x,y,center.groundHeightSubvoxels,d,d,8*WorldAssemblySubvoxelsPerVoxel,
&result.cliffBoxes,false,"cliff");
}
}
for(const auto& feature:plan->features)
{
if(feature.type!=2&&feature.type!=4)continue;
const std::int64_t x=feature.minimumSubvoxels[0]
+(feature.maximumSubvoxels[0]-feature.minimumSubvoxels[0])/2;
const std::int64_t y=feature.minimumSubvoxels[1]
+(feature.maximumSubvoxels[1]-feature.minimumSubvoxels[1])/2;
const std::int64_t z=feature.minimumSubvoxels[2]
+(feature.maximumSubvoxels[2]-feature.minimumSubvoxels[2])/2;
testBox(x,y,z,(feature.maximumSubvoxels[0]-feature.minimumSubvoxels[0])/2,
(feature.maximumSubvoxels[1]-feature.minimumSubvoxels[1])/2,
(feature.maximumSubvoxels[2]-feature.minimumSubvoxels[2])/2,
&result.exceptionBoxes,true,"exception");
}
const auto ground = block.point(0, 0, 0).groundHeightSubvoxels;
const auto controlPoint = block.point(0, 0, ground + 17).densitySubvoxels;
// Negative control: this fabricated one-sided range excludes its known sample. The same
// containment check used by the brute-force audit must reject the deliberate lie.
// 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
||result.biomeBorderBoxes>0;
const bool cliffCoverage=!hasCliffProfile||result.cliffBoxes>0;
const bool exceptionCoverage=std::none_of(plan->features.begin(),plan->features.end(),
[](const auto& feature){return feature.type==2||feature.type==4;})||result.exceptionBoxes>0;
result.passed = result.boxesTested >= boxCount && result.negativeCoordinateBoxes>0
&& borderCoverage&&cliffCoverage&&result.capBoxes>0&&exceptionCoverage
&& result.missedBounds == 0
&& result.unknownBounds == 0 && result.negativeControlDetected;
return result;
}
WorldAssemblyShelfThicknessAuditResult AuditHeightfieldShelfThickness(const Heightfield2D& block)
{
WorldAssemblyShelfThicknessAuditResult result;
const auto* recipe=block.recipe();
const auto* plan=block.plan();
if(!block.isValid()||!recipe||!plan||recipe->evaluatorVersion<4)return result;
constexpr std::int64_t sampleStep=WorldAssemblySubvoxelsPerVoxel/4;
constexpr std::int64_t xyStep=WorldAssemblySubvoxelsPerVoxel/2;
std::int64_t minimumObserved=std::numeric_limits<std::int64_t>::max();
for(std::size_t featureIndex=0;featureIndex<plan->features.size();++featureIndex)
{
const auto& feature=plan->features[featureIndex];
if(feature.type!=4)continue;
++result.featuresTested;
auto baselinePlan=*plan;
baselinePlan.features.erase(baselinePlan.features.begin()+featureIndex);
const Heightfield2D baseline(*recipe,baselinePlan);
const std::int64_t firstX=feature.minimumSubvoxels[0]+WorldAssemblySubvoxelsPerVoxel;
const std::int64_t lastX=feature.maximumSubvoxels[0]-WorldAssemblySubvoxelsPerVoxel;
const std::int64_t firstY=feature.minimumSubvoxels[1]+WorldAssemblySubvoxelsPerVoxel;
const std::int64_t lastY=feature.maximumSubvoxels[1]-WorldAssemblySubvoxelsPerVoxel;
for(std::int64_t y=firstY;y<=lastY;y+=xyStep)
for(std::int64_t x=firstX;x<=lastX;x+=xyStep)
{
++result.columnsTested;
const std::int64_t ground=baseline.point(x,y,0).groundHeightSubvoxels;
const std::int64_t range=std::max<std::int64_t>(feature.parametersSubvoxels[2],
world_assembly_detail::MinimumCliffShelfThicknessSubvoxels)+sampleStep;
std::int64_t currentRun=0,legacyCurrentRun=0;
std::int64_t currentStart=0,legacyCurrentStart=0;
bool columnActive=false,columnSubVoxel=false,legacyColumnSubVoxel=false;
const auto recordRun=[&](std::int64_t run,std::int64_t start,bool legacy)
{
if(run<=0)return;
if(legacy)
{
if(run<WorldAssemblySubvoxelsPerVoxel)
{++result.legacySubVoxelRuns;legacyColumnSubVoxel=true;}
return;
}
columnActive=true;
++result.negativeRuns;
if(run<minimumObserved)
{
minimumObserved=run;
result.minimumThicknessColumnXSubvoxels=x;
result.minimumThicknessColumnYSubvoxels=y;
result.minimumThicknessGroundSubvoxels=ground;
result.minimumThicknessBottomSubvoxels=start;
result.minimumThicknessTopSubvoxels=start+run-sampleStep;
}
if(run<WorldAssemblySubvoxelsPerVoxel)
{++result.subVoxelRuns;columnSubVoxel=true;}
};
for(std::int64_t dz=sampleStep;dz<=range;dz+=sampleStep)
{
const std::int64_t z=ground+dz;
const auto baselineSample=baseline.point(x,y,z);
if(baselineSample.densitySubvoxels<=0)
{
recordRun(currentRun,currentStart,false);currentRun=0;
recordRun(legacyCurrentRun,legacyCurrentStart,true);legacyCurrentRun=0;
continue;
}
const auto composedSample=block.point(x,y,z);
if(composedSample.densitySubvoxels<0)
{
if(currentRun==0)currentStart=z;
currentRun+=sampleStep;
}
else {recordRun(currentRun,currentStart,false);currentRun=0;}
const std::int64_t oldShelf=world_assembly_detail::CliffShelfDensity(feature,*recipe,baselinePlan,
x,y,z,baselineSample,false);
const std::int64_t legacyDensity=world_assembly_detail::SmoothMinSubvoxels(
baselineSample.densitySubvoxels,oldShelf,4*WorldAssemblySubvoxelsPerVoxel);
if(legacyDensity<0)
{if(legacyCurrentRun==0)legacyCurrentStart=z;legacyCurrentRun+=sampleStep;}
else {recordRun(legacyCurrentRun,legacyCurrentStart,true);legacyCurrentRun=0;}
}
recordRun(currentRun,currentStart,false);
recordRun(legacyCurrentRun,legacyCurrentStart,true);
if(columnActive)++result.activeColumns;
if(columnSubVoxel)++result.subVoxelColumns;
if(legacyColumnSubVoxel)++result.legacySubVoxelColumns;
}
}
if(minimumObserved!=std::numeric_limits<std::int64_t>::max())
result.minimumObservedThicknessSubvoxels=minimumObserved;
result.negativeControlDetected=result.legacySubVoxelColumns>0;
result.passed=result.featuresTested>0&&result.activeColumns>0
&&result.minimumObservedThicknessSubvoxels>=world_assembly_detail::MinimumCliffShelfThicknessSubvoxels
&&result.subVoxelRuns==0&&result.negativeControlDetected;
return result;
}
WorldAssemblyRouteAuditResult AuditHeightfieldRoute(const Heightfield2D& block)
{
WorldAssemblyRouteAuditResult result;
const auto* plan=block.plan();
if(!block.isValid()||!plan||plan->routeConstraints.version!=1||plan->route.size()<2)
return result;
using namespace world_assembly_detail;
const auto& constraints=plan->routeConstraints;
const std::int64_t radius=constraints.capsuleRadiusSubvoxels;
const std::int64_t halfHeight=constraints.capsuleHeightSubvoxels/2;
const std::int64_t axisHalf=std::max<std::int64_t>(0,halfHeight-radius);
const std::int64_t spacing=std::min<std::int64_t>(constraints.sweepSpacingSubvoxels,
WorldAssemblySubvoxelsPerVoxel/2);
const std::int64_t supportSpacing=std::min<std::int64_t>(spacing,
WorldAssemblySubvoxelsPerVoxel/4);
std::int64_t minimumClearance=std::numeric_limits<std::int64_t>::max();
std::int64_t maximumSupportDepth=0;
const std::array<std::array<std::int64_t,2>,5> supportOffsets{{
{{0,0}},{{constraints.supportProbeRadiusSubvoxels,0}},
{{-constraints.supportProbeRadiusSubvoxels,0}},
{{0,constraints.supportProbeRadiusSubvoxels}},
{{0,-constraints.supportProbeRadiusSubvoxels}}}};
const auto sweepCapsule=[&](const std::array<std::int64_t,3>& center)
{
++result.centerSamples;
const std::int64_t footZ=center[2]-halfHeight;
const std::int64_t firstDz=-halfHeight+constraints.minimumClearanceSubvoxels;
for(std::int64_t dz=firstDz;dz<=halfHeight;dz+=spacing)
{
const std::int64_t capOffset=std::max<std::int64_t>(0,std::abs(dz)-axisHalf);
const std::int64_t radialSquared=radius*radius-capOffset*capOffset;
if(radialSquared<0)continue;
for(std::int64_t oy=-radius;oy<=radius;oy+=spacing)
for(std::int64_t ox=-radius;ox<=radius;ox+=spacing)
{
if(ox*ox+oy*oy>radialSquared)continue;
const auto sample=block.point(center[0]+ox,center[1]+oy,center[2]+dz);
++result.capsuleFieldSamples;
minimumClearance=std::min(minimumClearance,sample.densitySubvoxels);
if(sample.densitySubvoxels<constraints.minimumClearanceSubvoxels)
++result.blockedCapsuleSamples;
}
}
std::uint32_t supportedProbes=0;
for(const auto& offset:supportOffsets)
{
++result.supportProbeSamples;
bool supported=false;
for(std::int64_t depth=supportSpacing;
depth<=constraints.supportSearchDepthSubvoxels;depth+=supportSpacing)
{
const auto sample=block.point(center[0]+offset[0],center[1]+offset[1],footZ-depth);
if(sample.densitySubvoxels<0)
{
supported=true;
maximumSupportDepth=std::max(maximumSupportDepth,depth);
break;
}
}
if(supported)++supportedProbes;
}
if(supportedProbes<constraints.minimumSupportProbes)
++result.unsupportedCenterSamples;
};
for(std::size_t segment=1;segment<plan->route.size();++segment)
{
++result.segmentsTested;
const auto& from=plan->route[segment-1].positionSubvoxels;
const auto& to=plan->route[segment].positionSubvoxels;
const std::int64_t dx=to[0]-from[0],dy=to[1]-from[1],dz=to[2]-from[2];
const std::uint64_t horizontalSquared=static_cast<std::uint64_t>(dx*dx+dy*dy);
const std::uint64_t horizontalLength=IntegerSqrt(horizontalSquared);
const std::uint64_t rise=static_cast<std::uint64_t>(std::abs(dz));
if(horizontalLength==0||rise*static_cast<std::uint64_t>(Q16)
>horizontalLength*static_cast<std::uint64_t>(constraints.maximumGradeQ16))
++result.gradeFailures;
const std::uint64_t length=IntegerSqrt(horizontalSquared
+static_cast<std::uint64_t>(dz*dz));
const std::uint64_t sampleCount=std::max<std::uint64_t>(1,
(length+static_cast<std::uint64_t>(spacing)-1)/static_cast<std::uint64_t>(spacing));
const std::uint64_t first=segment==1?0:1;
for(std::uint64_t i=first;i<=sampleCount;++i)
{
const auto interpolate=[&](std::size_t axis)
{
const std::int64_t delta=to[axis]-from[axis];
return from[axis]+delta*static_cast<std::int64_t>(i)
/static_cast<std::int64_t>(sampleCount);
};
sweepCapsule({interpolate(0),interpolate(1),interpolate(2)});
}
}
result.minimumClearanceSubvoxels=minimumClearance==std::numeric_limits<std::int64_t>::max()
?0:minimumClearance;
result.maximumSupportDepthSubvoxels=maximumSupportDepth;
result.passed=result.segmentsTested>0&&result.centerSamples>0
&&result.blockedCapsuleSamples==0&&result.unsupportedCenterSamples==0
&&result.gradeFailures==0;
return result;
}
}