#pragma once #include "Json.h" #include "VoxelForgeCore/VoxelForgeCore.h" #include #include #include #include #include #include #include #include #include namespace VoxelForgeCore { namespace { thread_local const ScopedWorldAssemblyFeatureQueriesV1* ActiveAssemblyQueries = nullptr; } ScopedWorldAssemblyFeatureQueriesV1::ScopedWorldAssemblyFeatureQueriesV1( std::span 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 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 BuildWorldAssemblyFeatureTileListV1( std::span features, const std::array& tileMinimumSubvoxels, const std::array& tileMaximumSubvoxels) { if (features.size() > std::numeric_limits::max()) throw std::length_error("world-assembly feature list exceeds 32-bit indexing"); const auto allFeatures = [&]() { std::vector result; result.reserve(features.size()); for (std::size_t index = 0; index < features.size(); ++index) result.push_back(static_cast(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 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(index)); } return result; } WorldAssemblyFeatureColumnRangeV1 BuildWorldAssemblyFeatureColumnRangeV1( std::span 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(xbound.maximumSubvoxels[0] ||ybound.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& 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(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(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::max()) return false; out = static_cast(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::min() || parsed > std::numeric_limits::max()) return false; out = static_cast(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(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 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 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 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(stratum.bottomChunkZ) * 32 * WorldAssemblySubvoxelsPerVoxel; const std::int64_t stratumMaxZ = (static_cast(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 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(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(recipe.biomeCellSizeSubvoxels), 8); HashWord(hash, static_cast(recipe.biomeWarpSubvoxels), 8); HashWord(hash, static_cast(recipe.biomeWarpWavelengthSubvoxels), 8); HashWord(hash, static_cast(recipe.biomeBorderBlendSubvoxels), 8); if (recipe.evaluatorVersion >= 2) { HashWord(hash, 0x535552465632ull, 8); HashWord(hash, static_cast(recipe.biomeClimateReliefWavelengthSubvoxels), 8); HashWord(hash, static_cast(recipe.biomeClimateMoistureWavelengthSubvoxels), 8); HashWord(hash, static_cast(recipe.biomeClimateReliefContrastQ16), 8); } HashWord(hash, recipe.biomes.size(), 4); for (const auto& biome : recipe.biomes) { HashWord(hash, biome.stableId, 4); HashWord(hash, static_cast(biome.baseHeightSubvoxels), 8); HashWord(hash, static_cast(biome.continentAmplitudeSubvoxels), 8); HashWord(hash, static_cast(biome.continentWavelengthSubvoxels), 8); HashWord(hash, static_cast(biome.mountainAmplitudeSubvoxels), 8); HashWord(hash, static_cast(biome.mountainWavelengthSubvoxels), 8); HashWord(hash, static_cast(biome.detailAmplitudeSubvoxels), 8); HashWord(hash, static_cast(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(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(value), 8); } } return hash; } inline std::uint64_t HashStratum(const WorldAssemblyStratumPlanV1& stratum) { std::uint64_t hash = StartHash(); HashWord(hash, static_cast(stratum.strateIndex), 4); HashWord(hash, static_cast(stratum.topChunkZ), 4); HashWord(hash, static_cast(stratum.bottomChunkZ), 4); if (stratum.hasRecipeOverride) { HashWord(hash, 0x5354524154455250ull, 8); HashWord(hash, HashRecipe(stratum.recipeOverride), 8); } HashWord(hash, static_cast(stratum.capHeightSubvoxels), 8); HashWord(hash, static_cast(stratum.capUndulationSubvoxels), 8); HashWord(hash, static_cast(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(stratum.capDetailAmplitudeSubvoxels), 8); HashWord(hash, static_cast(stratum.capDetailWavelengthSubvoxels), 8); HashWord(hash, static_cast(stratum.capRidgeAmplitudeSubvoxels), 8); HashWord(hash, static_cast(stratum.capRidgeWavelengthSubvoxels), 8); HashWord(hash, static_cast(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(value), 8); for (const auto value : feature.maximumSubvoxels) HashWord(hash, static_cast(value), 8); for (const auto value : feature.parametersSubvoxels) HashWord(hash, static_cast(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(constraints.capsuleRadiusSubvoxels), 8); HashWord(hash, static_cast(constraints.capsuleHeightSubvoxels), 8); HashWord(hash, static_cast(constraints.maximumGradeQ16), 8); HashWord(hash, static_cast(constraints.sweepSpacingSubvoxels), 8); HashWord(hash, static_cast(constraints.supportProbeRadiusSubvoxels), 8); HashWord(hash, static_cast(constraints.supportSearchDepthSubvoxels), 8); HashWord(hash, static_cast(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(value), 8); HashWord(hash, static_cast(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(islands.strateIndex), 4); HashWord(hash, static_cast(islands.topChunkZ), 4); HashWord(hash, static_cast(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(ch) << std::dec; else out << static_cast(ch); break; } } out << '"'; } inline void AppendJsonValue(std::ostringstream& out, const VFJson::Value& value) { if (std::holds_alternative(value.Data)) out << "null"; else if (const auto* boolean = std::get_if(&value.Data)) out << (*boolean ? "true" : "false"); else if (const auto* number = std::get_if(&value.Data)) { char buffer[64]; const auto result = std::to_chars(buffer, buffer + sizeof(buffer), *number, std::chars_format::general, std::numeric_limits::max_digits10); if (result.ec == std::errc{}) out.write(buffer, result.ptr - buffer); else out << '0'; } else if (const auto* string = std::get_if(&value.Data)) AppendJsonString(out, *string); else if (const auto* array = std::get_if(&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(value.Data); std::vector 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(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(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(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(field.Data)) return true; return field.IsNumber() && ReadInteger(field, output); }; const VFJson::Value& waterEnabled = item.Get("water_enabled"); const bool hasWaterBoolean=std::holds_alternative(waterEnabled.Data); const bool hasWaterNumber=waterEnabled.IsNumber(); const bool hasWaterNull=std::holds_alternative(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(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 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(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(seed) ^ salt); hash ^= Mix64(static_cast(x) + 0x517cc1b727220a95ull); hash ^= Mix64(static_cast(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(CoordinateHash(seed, x, y, salt) >> 48) & 0x1ffffu; return static_cast(bits) - 65536; } inline std::int32_t LerpQ16(std::int32_t a, std::int32_t b, std::uint32_t t) { return static_cast(a + (static_cast(b - a) * t) / Q16); } inline std::uint32_t SmoothQ16(std::uint32_t t) { const std::int64_t t2 = (static_cast(t) * t) / Q16; return static_cast((t2 * (3 * Q16 - 2 * static_cast(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((FloorMod(x, wavelength) * Q16) / wavelength); const std::uint32_t fractionY = static_cast((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(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(x) * 0x9E3779B1u ^ static_cast(y) * 0x85EBCA77u ^ static_cast(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(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(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(FloorMod(xQ16, Q16)); const std::int32_t fy = static_cast(FloorMod(yQ16, Q16)); const std::int32_t fz = static_cast(FloorMod(zQ16, Q16)); const std::int32_t sx = FadeQ16(fx), sy = FadeQ16(fy), sz = FadeQ16(fz); const std::int32_t fx1 = fx - static_cast(Q16); const std::int32_t fy1 = fy - static_cast(Q16); const std::int32_t fz1 = fz - static_cast(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(sx)); const std::int32_t x10=LerpQ16(n010,n110,static_cast(sx)); const std::int32_t x01=LerpQ16(n001,n101,static_cast(sx)); const std::int32_t x11=LerpQ16(n011,n111,static_cast(sx)); const std::int32_t y0=LerpQ16(x00,x10,static_cast(sy)); const std::int32_t y1=LerpQ16(x01,x11,static_cast(sy)); return LerpQ16(y0,y1,static_cast(sz)); } inline std::int64_t NoiseCoordinateQ16(std::int64_t positionSubvoxels, std::int64_t wavelengthSubvoxels) { return FloorDiv(positionSubvoxels * Q16, wavelengthSubvoxels); } 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; for (std::uint32_t octave = 0; octave < octaves; ++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(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(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(SmoothQ16(static_cast( std::clamp(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::clamp(Q16/2+noise/2,0,Q16)); } inline std::int64_t WarpDisplacement(std::int32_t noiseQ16, std::int64_t amplitudeSubvoxels) { return (static_cast(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(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((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(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(root); const std::int64_t threshold=std::max(1,biome.cliffSlopeThresholdQ16); const std::int64_t gate=std::clamp( (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( 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(std::clamp( 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(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::max(); for(std::size_t i=0;ibiome.reliefMaximumQ16 ?relief-biome.reliefMaximumQ16:0); const std::int64_t dm=moisturebiome.moistureMaximumQ16 ?moisture-biome.moistureMaximumQ16:0); const std::uint32_t jitterHash=Mix32(siteHash^(0x9e3779b9u*static_cast(i+1))); const std::uint64_t jitter=(static_cast(jitterHash>>8)*429497u)/0x1000000u; const std::uint64_t score=static_cast(dr*dr+dm*dm)+jitter; if(score(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::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(cx)+0x7fffffffu); hash^=Mix32((static_cast(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(jx)*cellSize)/0x1000000ll; const std::int64_t siteY=cy*cellSize+(static_cast(jy)*cellSize)/0x1000000ll; const std::int64_t dxq=siteX-out.warpedX,dyq=siteY-out.warpedY; const std::uint64_t distance=static_cast(dxq*dxq+dyq*dyq); if(distance0 &&second!=std::numeric_limits::max()) { const std::int64_t d1=static_cast(IntegerSqrt(nearest)); const std::int64_t d2=static_cast(IntegerSqrt(second)); const std::int64_t t=std::clamp((d2-d1)*Q16 /std::max(recipe.biomeBorderBlendSubvoxels,1),0,Q16); const std::uint32_t neighborWeight=static_cast( (Q16-SmoothQ16(static_cast(t)))/2); out.primaryWeightQ16=static_cast(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((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(xhi[0]||yhi[1])return 0; if(feather<=0)return static_cast(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::clamp(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 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(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(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(1,feature.parametersSubvoxels[1]); if(fade) { const std::uint32_t featureSeed=static_cast(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::max(); std::uint64_t secondDistance=std::numeric_limits::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(h&0xffffu)*(cellSize/2)/65535-cellSize/4; const std::int64_t jitterY=static_cast((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(dx*dx+dy*dy); const std::uint32_t biomeId=recipe.biomes[CoordinateHash(jitterSeed,cx,cy, 0x42494f4d455f4944ull)%recipe.biomes.size()].stableId; if(distanceconst 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(2*cellSize) *static_cast(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; out.capHeightSubvoxels=plan.capHeightSubvoxels+MulQ16(plan.capUndulationSubvoxels, Noise2D(x,y,plan.capWavelengthSubvoxels,recipe.seed,0x4341505f554e4431ull)); 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(x)*0x9E3779B185EBCA87ull) ^std::rotl(static_cast(y)*0xC2B2AE3D27D4EB4Full,23) ^static_cast(reinterpret_cast(&recipe)) ^std::rotl(static_cast(reinterpret_cast(&plan)),41); auto& entry=HeightfieldColumnCache[static_cast(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( std::chrono::steady_clock::now()-start).count(); ++HeightfieldColumnStats.columnMisses; 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(xhi[0]||yhi[1]||zhi[2]) return std::numeric_limits::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::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(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::max()/4; const std::uint32_t vertical=SmoothQ16(static_cast( std::clamp(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(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::max()/4; const std::uint32_t edgeFade=FeatureFadeQ16(feature,x,y,Feather); const std::int64_t edgeInset=static_cast(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(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 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::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 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(stratum.bottomChunkZ) * 32 * WorldAssemblySubvoxelsPerVoxel; const std::int64_t topZ = (static_cast(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 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(CoreTileVoxelCount) * Scale.ownerToCoreScaleNumerator / Scale.ownerToCoreScaleDenominator; const std::int64_t bottomQ = static_cast(std::llround( static_cast(islands.bottomChunkZ) * CoreVoxelsPerOwnerChunk * WorldAssemblySubvoxelsPerVoxel)); const std::int64_t topQ = static_cast(std::llround( static_cast(static_cast(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 expectedStrates; std::vector expectedStratumRecipes; for (const auto& stratum : plan.strates) { expectedStrates.push_back(stratum.contentHash); expectedStratumRecipes.push_back(stratum.hasRecipeOverride ? stratum.recipeOverride.contentHash : 0); } std::vector 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; }); } WorldAssemblyFeatureTileListV1 Heightfield2D::buildFeatureTileList( const std::array& minimum, const std::array& 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 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::min(); bound.maximumSubvoxels[2]=std::numeric_limits::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::min()); bound.maximumSubvoxels.fill(std::numeric_limits::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(xhi[0]||yhi[1]||zhi[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&&zwaterSurfaceSubvoxels &&out.groundHeightSubvoxelswaterSurfaceSubvoxels) out.waterFillSubvoxels=std::min(z-out.groundHeightSubvoxels, plan_->waterSurfaceSubvoxels-z); return out; } void Heightfield2D::resetThreadColumnCacheStats() noexcept { world_assembly_detail::HeightfieldColumnStats={}; } 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(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::max(); std::int64_t groundMax=std::numeric_limits::min(); std::int64_t capMin=std::numeric_limits::max(); std::int64_t capMax=std::numeric_limits::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&&maximumZcapMax)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::max(); std::int64_t groundMax = std::numeric_limits::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{maxX,maxY,maxZ}[axis] &&feature.maximumSubvoxels[axis]>=std::array{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(plan_->bottomChunkZ) * 32 * WorldAssemblySubvoxelsPerVoxel; out.maximumZSubvoxels = (static_cast(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::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(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(boxCount, 1, 100000); const auto random = [&seed]() { seed = world_assembly_detail::Mix64(seed); return seed; }; const auto signedVoxel = [&random]() { return static_cast(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(x),static_cast(y),static_cast(z), static_cast(halfX),static_cast(halfY),static_cast(halfZ), static_cast(bounds.minimumDensitySubvoxels), static_cast(bounds.maximumDensitySubvoxels), static_cast(px),static_cast(py),static_cast(pz), static_cast(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(random() % 257) - 128) * WorldAssemblySubvoxelsPerVoxel; const std::int64_t halfX = static_cast(1 + random() % 64) * WorldAssemblySubvoxelsPerVoxel; const std::int64_t halfY = static_cast(1 + random() % 64) * WorldAssemblySubvoxelsPerVoxel; const std::int64_t halfZ = static_cast(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(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(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(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(gradientfeatures) { 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::max(); for(std::size_t featureIndex=0;featureIndexfeatures.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(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::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(0,halfHeight-radius); const std::int64_t spacing=std::min(constraints.sweepSpacingSubvoxels, WorldAssemblySubvoxelsPerVoxel/2); const std::int64_t supportSpacing=std::min(spacing, WorldAssemblySubvoxelsPerVoxel/4); std::int64_t minimumClearance=std::numeric_limits::max(); std::int64_t maximumSupportDepth=0; const std::array,5> supportOffsets{{ {{0,0}},{{constraints.supportProbeRadiusSubvoxels,0}}, {{-constraints.supportProbeRadiusSubvoxels,0}}, {{0,constraints.supportProbeRadiusSubvoxels}}, {{0,-constraints.supportProbeRadiusSubvoxels}}}}; const auto sweepCapsule=[&](const std::array& 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(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.densitySubvoxelsroute.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(dx*dx+dy*dy); const std::uint64_t horizontalLength=IntegerSqrt(horizontalSquared); const std::uint64_t rise=static_cast(std::abs(dz)); if(horizontalLength==0||rise*static_cast(Q16) >horizontalLength*static_cast(constraints.maximumGradeQ16)) ++result.gradeFailures; const std::uint64_t length=IntegerSqrt(horizontalSquared +static_cast(dz*dz)); const std::uint64_t sampleCount=std::max(1, (length+static_cast(spacing)-1)/static_cast(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(i) /static_cast(sampleCount); }; sweepCapsule({interpolate(0),interpolate(1),interpolate(2)}); } } result.minimumClearanceSubvoxels=minimumClearance==std::numeric_limits::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; } }