Files
VoxelForge/Standalone/Core/src/FloatingIslandsInternal.h
T
Fr0zkaandClaude Opus 5.5 07b3fe2852 wip(L27, side branch): passages cross open voids inside shared natural rock columns
Unsupported passage runs > 12.5 m become a solid relief-bearing column (ceiling/island to
ground, flared base) with the helix inside, for every recipe; FI anchor uses the same path.
All 4 styles x both recipes: 0/0/0, 0 open-air, naturalness pass; no-long-bridge audit (L26
fails it). Deterministic, Clang = GCC. Look approved; cost +35% SW / +19% FI blocks merge (L28).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
2026-10-21 04:14:14 +02:00

2397 lines
133 KiB
C++

#pragma once
#include "VoxelForgeCore/ThreadLocalStorage.h"
#include "Json.h"
#include "VoxelForgeCore/VoxelForgeCore.h"
#include <algorithm>
#include <array>
#include <charconv>
#include <cmath>
#include <cstdint>
#include <limits>
#include <numeric>
#include <sstream>
#include <string_view>
#include <unordered_map>
#include <unordered_set>
namespace VoxelForgeCore
{
namespace floating_island_detail
{
constexpr std::int64_t Q = WorldAssemblySubvoxelsPerVoxel;
constexpr std::int64_t Q16 = 1ll << 16;
constexpr std::int64_t MaxScalar = 1000000000000ll;
constexpr std::int64_t MaxDensity = MaxScalar;
constexpr double Pi = 3.141592653589793238462643383279502884;
inline std::uint64_t StartHash() { return 14695981039346656037ull; }
inline void HashWord(std::uint64_t& hash, std::uint64_t word, int bytes)
{
for (int i = 0; i < bytes; ++i)
{
hash ^= static_cast<std::uint8_t>(word >> (i * 8));
hash *= 1099511628211ull;
}
}
inline std::uint64_t Mix(std::uint64_t value)
{
value += 0x9e3779b97f4a7c15ull;
value = (value ^ (value >> 30)) * 0xbf58476d1ce4e5b9ull;
value = (value ^ (value >> 27)) * 0x94d049bb133111ebull;
return value ^ (value >> 31);
}
inline std::uint64_t Roll(std::uint32_t seed, std::uint64_t site, std::uint64_t salt)
{ return Mix((static_cast<std::uint64_t>(seed) << 32) ^ site ^ salt); }
inline std::uint32_t UnitQ16(std::uint32_t seed, std::uint64_t site, std::uint64_t salt)
{ return static_cast<std::uint32_t>(Roll(seed, site, salt) >> 48); }
inline std::int64_t SignedRange(std::uint32_t seed, std::uint64_t site,
std::uint64_t salt, std::int64_t magnitude)
{
const std::uint32_t unit = UnitQ16(seed, site, salt);
return (static_cast<std::int64_t>(unit) * 2 * magnitude / Q16) - magnitude;
}
inline std::string HashText(std::uint64_t hash)
{
constexpr char Hex[] = "0123456789abcdef";
std::string output(16, '0');
for (int i = 15; i >= 0; --i)
{
output[static_cast<std::size_t>(i)] = Hex[hash & 0xfu];
hash >>= 4;
}
return output;
}
inline bool ReadInteger(const VFJson::Value& value, std::int64_t& output)
{
if (!value.IsNumber()) return false;
const double number = value.Number();
if (!std::isfinite(number) || std::trunc(number) != number
|| std::abs(number) > 9007199254740991.0) return false;
output = static_cast<std::int64_t>(number);
return true;
}
inline bool ReadU32(const VFJson::Value& value, std::uint32_t& output)
{
std::int64_t parsed = 0;
if (!ReadInteger(value, parsed) || parsed < 0
|| parsed > std::numeric_limits<std::uint32_t>::max()) return false;
output = static_cast<std::uint32_t>(parsed);
return true;
}
inline bool ReadHash(const VFJson::Value& value, std::uint64_t& output)
{
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(), output, 16);
return result.ec == std::errc{} && result.ptr == text.data() + text.size();
}
inline bool ReadArray(const VFJson::Value& value, std::array<std::int64_t, 3>& output)
{
if (!value.IsArray() || value.AsArray().size() != output.size()) return false;
for (std::size_t i = 0; i < output.size(); ++i)
if (!ReadInteger(value.AsArray()[i], output[i])) return false;
return true;
}
inline bool ValidScalar(std::int64_t value)
{ return value >= -MaxScalar && value <= MaxScalar; }
inline double Q16ToDouble(std::int64_t value)
{ return static_cast<double>(value) / static_cast<double>(Q16); }
inline std::int64_t RoundQ(double value)
{
if (!std::isfinite(value)) return value < 0.0 ? -MaxDensity : MaxDensity;
return static_cast<std::int64_t>(std::llround(std::clamp(value,
-static_cast<double>(MaxDensity), static_cast<double>(MaxDensity))));
}
inline double SmoothStep(double value)
{
value = std::clamp(value, 0.0, 1.0);
return value * value * (3.0 - 2.0 * value);
}
inline double SmoothMin(double a, double b, double k)
{
if (!(k > 0.0)) return std::min(a, b);
const double h = std::max(k - std::abs(a - b), 0.0) / k;
return std::min(a, b) - h * h * k * 0.25;
}
inline std::uint64_t StableId(std::uint32_t seed, std::uint64_t site, std::uint64_t salt)
{
std::uint64_t hash = StartHash();
HashWord(hash, seed, 4); HashWord(hash, site, 8); HashWord(hash, salt, 8);
return hash == 0 ? 1 : hash;
}
inline std::uint64_t HashRouteConstraints(const WorldAssemblyRouteConstraintsV1& value)
{
std::uint64_t hash = StartHash();
HashWord(hash, value.version, 4);
HashWord(hash, static_cast<std::uint64_t>(value.capsuleRadiusSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(value.capsuleHeightSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(value.maximumGradeQ16), 8);
HashWord(hash, static_cast<std::uint64_t>(value.sweepSpacingSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(value.supportProbeRadiusSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(value.supportSearchDepthSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(value.minimumClearanceSubvoxels), 8);
HashWord(hash, value.minimumSupportProbes, 4);
return hash;
}
inline std::uint64_t HashRecipe(const FloatingIslandRecipeV1& recipe)
{
std::uint64_t hash = StartHash();
HashWord(hash, recipe.schemaVersion, 4);
HashWord(hash, recipe.unitsPerVoxel, 4);
HashWord(hash, recipe.ownerVoxelSizeMicrons, 4);
HashWord(hash, recipe.coreVoxelSizeMicrons, 4);
HashWord(hash, recipe.ownerToCoreScaleNumerator, 4);
HashWord(hash, recipe.ownerToCoreScaleDenominator, 4);
HashWord(hash, recipe.seed, 4);
HashWord(hash, static_cast<std::uint64_t>(recipe.layoutOriginXSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.layoutOriginYSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.layoutCenterTopZSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.layoutMinimumZSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.layoutMaximumZSubvoxels), 8);
if(recipe.geometryVersion>=4)
HashWord(hash,static_cast<std::uint64_t>(recipe.layoutChunkWidthSubvoxels),8);
HashWord(hash, recipe.primaryIslandCount, 4);
HashWord(hash, static_cast<std::uint64_t>(recipe.islandDensityQ16), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.spacingSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.minimumRadiusSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.maximumRadiusSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.thicknessRatioQ16), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.verticalJitterQ16), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.topFlattenQ16), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.taperEndMinimumQ16), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.taperEndMaximumQ16), 8);
if (recipe.geometryVersion >= 2)
{
HashWord(hash, recipe.geometryVersion, 4);
HashWord(hash, static_cast<std::uint64_t>(recipe.crustDepthBonusQ16), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.rootDepthRatioQ16), 8);
}
if (recipe.geometryVersion >= 5)
{
HashWord(hash, recipe.anchorIslandOrdinal, 4);
HashWord(hash, static_cast<std::uint64_t>(recipe.anchorPlacementRadiusSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.anchorRootRadiusSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.anchorIslandMinimumRadiusSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.anchorRootBottomZSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.anchorMouthOffsetSubvoxels), 8);
}
HashWord(hash, static_cast<std::uint64_t>(recipe.blendRadiusSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.contourWarpSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.roughnessSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.boundarySealSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.bridgeHalfWidthSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.bridgeThicknessSubvoxels), 8);
HashWord(hash, HashRouteConstraints(recipe.routeConstraints), 8);
return hash;
}
inline std::uint64_t HashPlan(const FloatingIslandPlanV1& plan)
{
std::uint64_t hash = StartHash();
HashWord(hash, plan.islands.size(), 4);
for (const auto& island : plan.islands)
{
HashWord(hash, island.stableId, 8); HashWord(hash, island.ordinal, 4);
for (auto value : island.centerSubvoxels) HashWord(hash, static_cast<std::uint64_t>(value), 8);
HashWord(hash, static_cast<std::uint64_t>(island.radiusSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(island.topHalfSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(island.topZSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(island.bottomZSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(island.taperEndQ16), 8);
for (auto value : island.influenceMinimumSubvoxels) HashWord(hash, static_cast<std::uint64_t>(value), 8);
for (auto value : island.influenceMaximumSubvoxels) HashWord(hash, static_cast<std::uint64_t>(value), 8);
}
HashWord(hash, plan.clusterEdges.size(), 4);
for (const auto& edge : plan.clusterEdges)
{
HashWord(hash, edge.stableId, 8); HashWord(hash, edge.fromIslandId, 8);
HashWord(hash, edge.toIslandId, 8);
for (auto value : edge.startSubvoxels) HashWord(hash, static_cast<std::uint64_t>(value), 8);
for (auto value : edge.endSubvoxels) HashWord(hash, static_cast<std::uint64_t>(value), 8);
HashWord(hash, static_cast<std::uint64_t>(edge.halfWidthSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(edge.thicknessSubvoxels), 8);
for (auto value : edge.influenceMinimumSubvoxels) HashWord(hash, static_cast<std::uint64_t>(value), 8);
for (auto value : edge.influenceMaximumSubvoxels) HashWord(hash, static_cast<std::uint64_t>(value), 8);
HashWord(hash, edge.sharpTagReady ? 1u : 0u, 1);
}
HashWord(hash, plan.route.size(), 4);
for (const auto& point : plan.route)
{
HashWord(hash, point.stableFeatureId, 8);
for (auto value : point.positionSubvoxels) HashWord(hash, static_cast<std::uint64_t>(value), 8);
HashWord(hash, static_cast<std::uint64_t>(point.radiusSubvoxels), 8);
}
return hash;
}
inline std::uint64_t HashManifest(const FloatingIslandManifestV1& manifest)
{
std::uint64_t hash = StartHash();
HashWord(hash, manifest.recipe.contentHash, 8);
HashWord(hash, manifest.plan.contentHash, 8);
return hash;
}
inline bool ValidateRecipe(const FloatingIslandRecipeV1& recipe, std::string& error)
{
const std::array<std::int64_t, 32> values = {
recipe.layoutOriginXSubvoxels, recipe.layoutOriginYSubvoxels,
recipe.layoutCenterTopZSubvoxels, recipe.layoutMinimumZSubvoxels,
recipe.layoutMaximumZSubvoxels, recipe.layoutChunkWidthSubvoxels, recipe.islandDensityQ16,
recipe.spacingSubvoxels, recipe.minimumRadiusSubvoxels,
recipe.maximumRadiusSubvoxels, recipe.thicknessRatioQ16,
recipe.verticalJitterQ16, recipe.topFlattenQ16,
recipe.taperEndMinimumQ16, recipe.taperEndMaximumQ16,
recipe.crustDepthBonusQ16, recipe.rootDepthRatioQ16,
recipe.blendRadiusSubvoxels, recipe.contourWarpSubvoxels,
recipe.roughnessSubvoxels, recipe.boundarySealSubvoxels,
recipe.bridgeHalfWidthSubvoxels, recipe.bridgeThicknessSubvoxels,
static_cast<std::int64_t>(recipe.anchorIslandOrdinal),
recipe.anchorPlacementRadiusSubvoxels, recipe.anchorRootRadiusSubvoxels,
recipe.anchorIslandMinimumRadiusSubvoxels, recipe.anchorRootBottomZSubvoxels,
recipe.anchorMouthOffsetSubvoxels,
recipe.routeConstraints.capsuleRadiusSubvoxels,
recipe.routeConstraints.capsuleHeightSubvoxels,
recipe.routeConstraints.maximumGradeQ16};
const std::uint32_t ScaleGcd = std::gcd(recipe.ownerVoxelSizeMicrons,
recipe.coreVoxelSizeMicrons);
const std::uint32_t ExpectedNumerator = ScaleGcd > 0
? recipe.ownerVoxelSizeMicrons / ScaleGcd : 0;
const std::uint32_t ExpectedDenominator = ScaleGcd > 0
? recipe.coreVoxelSizeMicrons / ScaleGcd : 0;
if (recipe.schemaVersion != 1 || recipe.geometryVersion < 1 || recipe.geometryVersion > 8
|| (recipe.geometryVersion == 1
&& (recipe.crustDepthBonusQ16 != 0 || recipe.rootDepthRatioQ16 != 0))
|| recipe.unitsPerVoxel != Q
|| recipe.ownerVoxelSizeMicrons != 125000
|| recipe.coreVoxelSizeMicrons < 125000 || recipe.coreVoxelSizeMicrons > 250000
|| recipe.ownerToCoreScaleNumerator != ExpectedNumerator
|| recipe.ownerToCoreScaleDenominator != ExpectedDenominator
|| recipe.primaryIslandCount < 3 || recipe.primaryIslandCount > 9
|| recipe.layoutChunkWidthSubvoxels <= 0
|| static_cast<long double>(recipe.layoutChunkWidthSubvoxels)*recipe.coreVoxelSizeMicrons
< 250000.0L*Q
|| static_cast<long double>(recipe.layoutChunkWidthSubvoxels)*recipe.coreVoxelSizeMicrons
> 64000000.0L*Q
|| recipe.layoutMaximumZSubvoxels <= recipe.layoutMinimumZSubvoxels
|| recipe.layoutMaximumZSubvoxels - recipe.layoutMinimumZSubvoxels > 2048 * Q
|| std::any_of(values.begin(), values.end(), [](auto value) { return !ValidScalar(value); }))
{ error = "floating-island recipe version, scale, layout or scalar range is invalid"; return false; }
const auto& constraints = recipe.routeConstraints;
const std::int64_t minimumSpacing = recipe.geometryVersion >= 2 ? 32 * Q : 64 * Q;
const std::int64_t minimumRadius = recipe.geometryVersion >= 2 ? 12 * Q : 24 * Q;
if (recipe.islandDensityQ16 < 6554 || recipe.islandDensityQ16 > (1ll << 15)
|| recipe.spacingSubvoxels < minimumSpacing || recipe.spacingSubvoxels > 256 * Q
|| recipe.minimumRadiusSubvoxels < minimumRadius
|| recipe.maximumRadiusSubvoxels < recipe.minimumRadiusSubvoxels
|| recipe.maximumRadiusSubvoxels > 64 * Q
|| recipe.thicknessRatioQ16 < 29491 || recipe.thicknessRatioQ16 > 49152
|| recipe.verticalJitterQ16 < 13107 || recipe.verticalJitterQ16 > 39322
|| recipe.topFlattenQ16 < 39322 || recipe.topFlattenQ16 > 52429
|| recipe.taperEndMinimumQ16 < 26214 || recipe.taperEndMaximumQ16 > 52429
|| recipe.taperEndMinimumQ16 > recipe.taperEndMaximumQ16
|| (recipe.geometryVersion >= 2
&& (recipe.crustDepthBonusQ16 < 6554 || recipe.crustDepthBonusQ16 > 16384
|| recipe.rootDepthRatioQ16 < 32768 || recipe.rootDepthRatioQ16 > 65536))
|| recipe.blendRadiusSubvoxels < 2 * Q || recipe.blendRadiusSubvoxels > 6 * Q
|| recipe.contourWarpSubvoxels <= 0
|| recipe.contourWarpSubvoxels > recipe.maximumRadiusSubvoxels / 2
|| recipe.roughnessSubvoxels < 0
|| recipe.roughnessSubvoxels > recipe.minimumRadiusSubvoxels / 4
|| recipe.boundarySealSubvoxels < 2 * Q
|| recipe.bridgeHalfWidthSubvoxels < constraints.capsuleRadiusSubvoxels + Q
|| recipe.bridgeThicknessSubvoxels < 2 * Q
|| constraints.version != 1 || constraints.capsuleRadiusSubvoxels <= 0
|| constraints.capsuleHeightSubvoxels < 2 * constraints.capsuleRadiusSubvoxels
|| constraints.maximumGradeQ16 <= 0 || constraints.maximumGradeQ16 > Q16
|| constraints.sweepSpacingSubvoxels <= 0 || constraints.sweepSpacingSubvoxels > Q / 2
|| !ValidScalar(constraints.sweepSpacingSubvoxels)
|| !ValidScalar(constraints.supportProbeRadiusSubvoxels)
|| !ValidScalar(constraints.supportSearchDepthSubvoxels)
|| !ValidScalar(constraints.minimumClearanceSubvoxels)
|| constraints.supportProbeRadiusSubvoxels <= 0
|| constraints.supportSearchDepthSubvoxels <= 0
|| constraints.minimumClearanceSubvoxels < 0
|| constraints.minimumSupportProbes == 0 || constraints.minimumSupportProbes > 5)
{ error = "floating-island shape or route parameters are outside the supported range"; return false; }
if (recipe.geometryVersion >= 5
&& (recipe.anchorIslandOrdinal != recipe.primaryIslandCount - 1
|| recipe.anchorPlacementRadiusSubvoxels <= 0
|| recipe.anchorRootRadiusSubvoxels < recipe.anchorMouthOffsetSubvoxels
+ recipe.routeConstraints.capsuleRadiusSubvoxels + Q
|| recipe.anchorIslandMinimumRadiusSubvoxels
< recipe.anchorMouthOffsetSubvoxels
+ recipe.routeConstraints.capsuleRadiusSubvoxels + Q
|| recipe.anchorIslandMinimumRadiusSubvoxels > 64 * Q
|| recipe.anchorRootBottomZSubvoxels < recipe.layoutMinimumZSubvoxels
+ recipe.boundarySealSubvoxels
|| recipe.anchorRootBottomZSubvoxels >= recipe.layoutCenterTopZSubvoxels
|| recipe.anchorMouthOffsetSubvoxels <= 0
|| recipe.anchorMouthOffsetSubvoxels > 64 * Q))
{ error = "floating-island bedrock anchor is outside its passage or rock-thickness bounds"; return false; }
return true;
}
inline bool Contains(const std::array<std::int64_t, 3>& minimum,
const std::array<std::int64_t, 3>& maximum,
const std::array<std::int64_t, 3>& point)
{
for (std::size_t axis = 0; axis < 3; ++axis)
if (point[axis] < minimum[axis] || point[axis] > maximum[axis]) return false;
return true;
}
inline bool Intersects(const std::array<std::int64_t, 3>& minimumA,
const std::array<std::int64_t, 3>& maximumA,
const std::array<std::int64_t, 3>& minimumB,
const std::array<std::int64_t, 3>& maximumB)
{
for (std::size_t axis = 0; axis < 3; ++axis)
if (maximumA[axis] < minimumB[axis] || minimumA[axis] > maximumB[axis]) return false;
return true;
}
inline std::int64_t IslandBlendMargin(const FloatingIslandRecipeV1& recipe)
{ return 3 * recipe.blendRadiusSubvoxels; }
inline double LobeOffset(const FloatingIslandRecipeV1& recipe,
const FloatingIslandAnchorV1& island,
double dx, double dy, double zSubvoxels)
{
const double angle = std::atan2(dy, dx);
const double z = zSubvoxels / static_cast<double>(Q);
const double seed = static_cast<double>(Roll(recipe.seed, island.stableId, 0x4c4f4245ull)
& 0xffffffffull) * (2.0 * Pi / 4294967296.0);
const double a = std::sin(3.0 * angle + seed + z * 0.017);
const double b = std::sin(5.0 * angle - seed * 0.73 - z * 0.023);
const double c = std::sin(8.0 * angle + seed * 1.41 + z * 0.011);
return static_cast<double>(recipe.contourWarpSubvoxels) * (0.50 * a + 0.30 * b + 0.20 * c);
}
inline double LobePhase(const FloatingIslandRecipeV1& recipe,
const FloatingIslandAnchorV1& island)
{
return static_cast<double>(Roll(recipe.seed, island.stableId, 0x4c4f4245ull)
& 0xffffffffull) * (2.0 * Pi / 4294967296.0);
}
inline double LobeOffsetWithPhase(const FloatingIslandRecipeV1& recipe,
double angle,double zSubvoxels,double phase)
{
const double z = zSubvoxels / static_cast<double>(Q);
const double a = std::sin(3.0 * angle + phase + z * 0.017);
const double b = std::sin(5.0 * angle - phase * 0.73 - z * 0.023);
const double c = std::sin(8.0 * angle + phase * 1.41 + z * 0.011);
return static_cast<double>(recipe.contourWarpSubvoxels) * (0.50 * a + 0.30 * b + 0.20 * c);
}
inline double LobeOffsetAtAngle(const FloatingIslandRecipeV1& recipe,
const FloatingIslandAnchorV1& island,
double angle,double zSubvoxels)
{
return LobeOffsetWithPhase(recipe,angle,zSubvoxels,LobePhase(recipe,island));
}
inline double Roughness(const FloatingIslandRecipeV1& recipe,
std::uint64_t featureId, double x, double y, double z)
{
const double phase = static_cast<double>(Roll(recipe.seed, featureId, 0x524f5547ull)
& 0xffffffffull) * (2.0 * Pi / 4294967296.0);
const double xv = x / static_cast<double>(Q);
const double yv = y / static_cast<double>(Q);
const double zv = z / static_cast<double>(Q);
if (recipe.geometryVersion < 2)
{
const double oldNoise = 0.52 * std::sin(xv * 0.071 + yv * 0.053 + zv * 0.089 + phase)
+ 0.31 * std::sin(xv * 0.127 - yv * 0.097 + zv * 0.061 - phase * 0.73)
+ 0.17 * std::cos(xv * 0.043 + yv * 0.139 - zv * 0.113 + phase * 1.39);
return static_cast<double>(recipe.roughnessSubvoxels) * oldNoise;
}
// Four oblique bands span about 7 m down to 0.9 m at 25 cm per voxel.
// Their absolute weights sum to one, keeping displacement inside the 3-voxel envelope.
const double noise = 0.52 * std::sin(xv * 0.071 + yv * 0.053 + zv * 0.089 + phase)
+ 0.31 * std::sin(xv * 0.127 - yv * 0.097 + zv * 0.061 - phase * 0.73)
+ 0.17 * std::cos(xv * 0.043 + yv * 0.139 - zv * 0.113 + phase * 1.39);
const double fine = 0.46 * std::sin(xv * 0.22 + yv * 0.31 - zv * 0.17 + phase * 0.41)
+ 0.32 * std::cos(xv * 0.38 - yv * 0.21 + zv * 0.29 - phase * 0.67)
+ 0.22 * std::sin(xv * 0.74 + yv * 0.53 + zv * 0.61 + phase * 1.17);
return static_cast<double>(recipe.roughnessSubvoxels)
* std::clamp(0.42 * noise + 0.58 * fine, -1.0, 1.0);
}
inline double IslandTopSurface(const FloatingIslandRecipeV1& recipe,
const FloatingIslandAnchorV1& island,
double x, double y)
{
const double dx = x - island.centerSubvoxels[0];
const double dy = y - island.centerSubvoxels[1];
const double radial = std::hypot(dx, dy);
const double effectiveRadius = std::max(1.0,
static_cast<double>(island.radiusSubvoxels) + LobeOffset(recipe, island, dx, dy,
static_cast<double>(island.topZSubvoxels)));
const double edge = std::clamp(radial / effectiveRadius, 0.0, 1.0);
const double drop = (1.0 - Q16ToDouble(recipe.topFlattenQ16))
* static_cast<double>(island.topHalfSubvoxels) * 2.0 * edge * edge;
return static_cast<double>(island.topZSubvoxels) - drop;
}
struct FIslandColumnCacheEntry
{
bool bValid=false;
const FloatingIslandRecipeV1* RecipeIdentity=nullptr;
const FloatingIslandPlanV1* PlanIdentity=nullptr;
std::uint64_t RecipeContentHash=0,PlanContentHash=0;
std::uint32_t Seed=0;
std::uint64_t StableId=0;
std::int64_t SampleX=0,SampleY=0,CenterX=0,CenterY=0,TopZ=0,Radius=0,TopHalf=0;
std::int64_t TopFlattenQ16=0,ContourWarp=0;
double DeltaX=0.0,DeltaY=0.0,Radial=0.0,Angle=0.0,LobePhase=0.0,TopSurface=0.0;
};
using FSharedIslandColumnCache =
VoxelForgeCoreShared::TSharedColumnValueCache<FIslandColumnCacheEntry,16384,4>;
inline const FIslandColumnCacheEntry& IslandColumnCache(
const FloatingIslandRecipeV1& recipe,const FloatingIslandPlanV1& plan,
const FloatingIslandAnchorV1& island,double x,double y)
{
// Density samples arrive on integer Core lattice columns; reuse the exact column metrics
// across Z samples without changing the field value or touching shared world state.
// Les echantillons suivent les colonnes Core entieres; reutiliser les memes metriques XY.
constexpr std::size_t Ways=4;
constexpr std::size_t SetCount=2048;
static_assert((SetCount&(SetCount-1))==0);
static thread_local VoxelForgeCoreThreadLocal::FLazy<
std::array<FIslandColumnCacheEntry,SetCount*Ways>> Cache;
auto& cache=Cache.Get();
const std::int64_t SampleX=static_cast<std::int64_t>(x);
const std::int64_t SampleY=static_cast<std::int64_t>(y);
std::uint64_t IndexKey=island.stableId
^(static_cast<std::uint64_t>(SampleX)*0x9e3779b97f4a7c15ull)
^(static_cast<std::uint64_t>(SampleY)*0xc2b2ae3d27d4eb4full)
^(static_cast<std::uint64_t>(recipe.seed)<<23)
^recipe.contentHash^std::rotl(plan.contentHash,17)
^static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(&recipe))
^std::rotl(static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(&plan)),31);
IndexKey^=IndexKey>>33;
IndexKey*=0xff51afd7ed558ccdull;
IndexKey^=IndexKey>>33;
const std::size_t Base=static_cast<std::size_t>(IndexKey&(SetCount-1))*Ways;
FIslandColumnCacheEntry* Replacement=&cache[Base+((IndexKey>>11)&(Ways-1))];
for(std::size_t Way=0;Way<Ways;++Way)
{
FIslandColumnCacheEntry& Entry=cache[Base+Way];
const bool bKeyMatches=Entry.bValid&&Entry.RecipeIdentity==&recipe
&&Entry.PlanIdentity==&plan&&Entry.RecipeContentHash==recipe.contentHash
&&Entry.PlanContentHash==plan.contentHash&&Entry.Seed==recipe.seed
&&Entry.StableId==island.stableId
&&Entry.SampleX==SampleX&&Entry.SampleY==SampleY
&&Entry.CenterX==island.centerSubvoxels[0]
&&Entry.CenterY==island.centerSubvoxels[1]
&&Entry.TopZ==island.topZSubvoxels
&&Entry.Radius==island.radiusSubvoxels
&&Entry.TopHalf==island.topHalfSubvoxels
&&Entry.TopFlattenQ16==recipe.topFlattenQ16
&&Entry.ContourWarp==recipe.contourWarpSubvoxels;
if(bKeyMatches)return Entry;
if(!Entry.bValid)Replacement=&Entry;
}
const auto build=[&]()
{
FIslandColumnCacheEntry Entry;
Entry.bValid=true;
Entry.RecipeIdentity=&recipe;
Entry.PlanIdentity=&plan;
Entry.RecipeContentHash=recipe.contentHash;
Entry.PlanContentHash=plan.contentHash;
Entry.Seed=recipe.seed;
Entry.StableId=island.stableId;
Entry.SampleX=SampleX;
Entry.SampleY=SampleY;
Entry.CenterX=island.centerSubvoxels[0];
Entry.CenterY=island.centerSubvoxels[1];
Entry.TopZ=island.topZSubvoxels;
Entry.Radius=island.radiusSubvoxels;
Entry.TopHalf=island.topHalfSubvoxels;
Entry.TopFlattenQ16=recipe.topFlattenQ16;
Entry.ContourWarp=recipe.contourWarpSubvoxels;
Entry.DeltaX=x-static_cast<double>(island.centerSubvoxels[0]);
Entry.DeltaY=y-static_cast<double>(island.centerSubvoxels[1]);
Entry.Radial=std::hypot(Entry.DeltaX,Entry.DeltaY);
Entry.Angle=std::atan2(Entry.DeltaY,Entry.DeltaX);
Entry.LobePhase=LobePhase(recipe,island);
const double EffectiveRadius=std::max(1.0,static_cast<double>(island.radiusSubvoxels)
+LobeOffsetWithPhase(recipe,Entry.Angle,static_cast<double>(island.topZSubvoxels),
Entry.LobePhase));
const double Edge=std::clamp(Entry.Radial/EffectiveRadius,0.0,1.0);
const double Drop=(1.0-Q16ToDouble(recipe.topFlattenQ16))
*static_cast<double>(island.topHalfSubvoxels)*2.0*Edge*Edge;
Entry.TopSurface=static_cast<double>(island.topZSubvoxels)-Drop;
return Entry;
};
FIslandColumnCacheEntry Entry{};
if(auto* shared=FSharedIslandColumnCache::threadActive())
{
const VoxelForgeCoreShared::FColumnCacheKey key{
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(&recipe)),
recipe.contentHash,
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(&plan)),
plan.contentHash,SampleX,SampleY,island.stableId};
bool computed=false;
Entry=shared->getOrCompute(key,build,computed);
}
else Entry=build();
*Replacement=Entry;
return *Replacement;
}
inline std::int64_t ScaledQ16(std::int64_t value, std::int64_t scaleQ16)
{ return (value * scaleQ16 + Q16 / 2) / Q16; }
inline std::int64_t IslandCrustBottom(const FloatingIslandRecipeV1& recipe,
const FloatingIslandAnchorV1& island)
{
const std::int64_t ratio = recipe.thicknessRatioQ16
+ (recipe.geometryVersion >= 2 ? recipe.crustDepthBonusQ16 : 0);
return island.topZSubvoxels - ScaledQ16(island.radiusSubvoxels, ratio);
}
inline std::int64_t IslandBottom(const FloatingIslandRecipeV1& recipe,
std::int64_t topZ, std::int64_t radius)
{
const std::int64_t crustRatio = recipe.thicknessRatioQ16
+ (recipe.geometryVersion >= 2 ? recipe.crustDepthBonusQ16 : 0);
const std::int64_t rootRatio = recipe.geometryVersion >= 2
? recipe.rootDepthRatioQ16 : 0;
return topZ - ScaledQ16(radius, crustRatio) - ScaledQ16(radius, rootRatio);
}
inline std::int32_t RootCount(const FloatingIslandRecipeV1& recipe,
const FloatingIslandAnchorV1& island)
{
// Keep four separate side lobes and a full-depth central buttress to close the measured
// underside cavity without driving the outer roots to one point.
// Garder quatre lobes lateraux et un renfort central profond pour fermer la cavite mesuree.
return 5;
}
inline double UnitHash(std::uint64_t hash)
{ return static_cast<double>(hash & 0xffffull) / 65535.0; }
struct FRootLobeParameters
{
double RootX=0.0,RootY=0.0,CenterZ=0.0;
double RadiusXSquared=1.0,RadiusYSquared=1.0,VerticalRadiusSquared=1.0;
double MinimumRadius=1.0;
};
struct FRootLobeCacheEntry
{
bool bValid=false;
std::uint32_t Seed=0;
std::uint64_t StableId=0;
std::int32_t RootIndex=0,GeometryVersion=0;
std::array<std::int64_t,3> Center{};
std::int64_t TopZ=0,Radius=0,TaperEndQ16=0;
std::int64_t ThicknessRatioQ16=0,CrustDepthBonusQ16=0,RootDepthRatioQ16=0;
FRootLobeParameters Parameters{};
};
inline double RootLobeDensity(const FloatingIslandRecipeV1& recipe,
const FloatingIslandAnchorV1& island,
std::int32_t rootIndex, double x, double y, double z)
{
// Cache derived root constants per worker. The complete immutable input key keeps this
// value-only cache safe across worlds, epochs and worker schedules.
// Mettre en cache les constantes par worker; la cle complete evite tout partage d'etat.
constexpr std::size_t CacheSize=1024;
static_assert((CacheSize&(CacheSize-1))==0);
static thread_local VoxelForgeCoreThreadLocal::FLazy<
std::array<FRootLobeCacheEntry,CacheSize>> Cache;
auto& cache=Cache.Get();
const std::uint64_t IndexKey=island.stableId^(island.stableId>>33)
^(static_cast<std::uint64_t>(recipe.seed)<<17)
^(static_cast<std::uint64_t>(static_cast<std::uint32_t>(rootIndex))*0x9e3779b97f4a7c15ull);
FRootLobeCacheEntry& Entry=cache[static_cast<std::size_t>(IndexKey)&(CacheSize-1)];
const bool bKeyMatches=Entry.bValid&&Entry.Seed==recipe.seed
&&Entry.StableId==island.stableId&&Entry.RootIndex==rootIndex
&&Entry.GeometryVersion==recipe.geometryVersion
&&Entry.Center==island.centerSubvoxels
&&Entry.TopZ==island.topZSubvoxels&&Entry.Radius==island.radiusSubvoxels
&&Entry.TaperEndQ16==island.taperEndQ16
&&Entry.ThicknessRatioQ16==recipe.thicknessRatioQ16
&&Entry.CrustDepthBonusQ16==recipe.crustDepthBonusQ16
&&Entry.RootDepthRatioQ16==recipe.rootDepthRatioQ16;
if(!bKeyMatches)
{
constexpr std::array<std::array<double, 2>, 4> pattern{{
{{-0.92, -0.12}}, {{0.28, 0.98}}, {{0.83, -0.42}}, {{-0.16, -0.94}}}};
const std::uint64_t salt = 0x524f4f540000ull + static_cast<std::uint64_t>(rootIndex);
const std::uint64_t roll = Roll(recipe.seed, island.stableId, salt);
const double radius = static_cast<double>(island.radiusSubvoxels);
const double rootDepth = radius * Q16ToDouble(recipe.rootDepthRatioQ16);
const double crustBottom = static_cast<double>(IslandCrustBottom(recipe, island));
const bool centralButtress = rootIndex == 4;
const double orbit = centralButtress ? 0.0
: radius * (0.17 + 0.07 * UnitHash(roll));
const double rootX = static_cast<double>(island.centerSubvoxels[0])
+ (centralButtress ? 0.0 : pattern[static_cast<std::size_t>(rootIndex)][0] * orbit);
const double rootY = static_cast<double>(island.centerSubvoxels[1])
+ (centralButtress ? 0.0 : pattern[static_cast<std::size_t>(rootIndex)][1] * orbit);
const double shortRootVariation = UnitHash(Roll(recipe.seed, island.stableId, salt ^ 0x4c454eull));
const double lengthRatio = centralButtress ? 1.0 : (rootIndex == 0 ? 1.0
: Q16ToDouble(island.taperEndQ16) * (0.82 + 0.18 * shortRootVariation));
const double lowerDepth = rootDepth * lengthRatio;
const double embedDepth = rootDepth * (centralButtress ? 0.22 : (rootIndex == 0 ? 0.30 : 0.22));
const double verticalRadius = 0.5 * (lowerDepth + embedDepth);
const double centerZ = crustBottom + 0.5 * (embedDepth - lowerDepth);
const double rx = radius * (centralButtress ? 0.25
: 0.15 + 0.055 * UnitHash(Roll(recipe.seed, island.stableId, salt ^ 0x5258ull)));
const double ry = radius * (centralButtress ? 0.25
: 0.15 + 0.065 * UnitHash(Roll(recipe.seed, island.stableId, salt ^ 0x5259ull)));
Entry.bValid=true;
Entry.Seed=recipe.seed;
Entry.StableId=island.stableId;
Entry.RootIndex=rootIndex;
Entry.GeometryVersion=recipe.geometryVersion;
Entry.Center=island.centerSubvoxels;
Entry.TopZ=island.topZSubvoxels;
Entry.Radius=island.radiusSubvoxels;
Entry.TaperEndQ16=island.taperEndQ16;
Entry.ThicknessRatioQ16=recipe.thicknessRatioQ16;
Entry.CrustDepthBonusQ16=recipe.crustDepthBonusQ16;
Entry.RootDepthRatioQ16=recipe.rootDepthRatioQ16;
Entry.Parameters={rootX,rootY,centerZ,rx*rx,ry*ry,
verticalRadius*verticalRadius,std::min({rx,ry,verticalRadius})};
}
const FRootLobeParameters& Parameters=Entry.Parameters;
const double q = (x - Parameters.RootX) * (x - Parameters.RootX) / Parameters.RadiusXSquared
+ (y - Parameters.RootY) * (y - Parameters.RootY) / Parameters.RadiusYSquared
+ (z - Parameters.CenterZ) * (z - Parameters.CenterZ) / Parameters.VerticalRadiusSquared;
return (std::sqrt(std::max(q, 0.0)) - 1.0) * Parameters.MinimumRadius;
}
inline double ApplySurfaceDetail(const FloatingIslandRecipeV1& recipe,
std::uint64_t featureId, double structural,
double x, double y, double z, double detailScale = 1.0)
{
if (recipe.geometryVersion < 2)
return structural + Roughness(recipe, featureId, x, y, z);
const double amplitude = static_cast<double>(recipe.roughnessSubvoxels) * detailScale;
const double band = 2.0 * amplitude;
if (!(amplitude > 0.0) || std::abs(structural) >= band) return structural;
const double fade = 1.0 - SmoothStep(std::abs(structural) / band);
return structural + Roughness(recipe, featureId, x, y, z) * detailScale * fade;
}
inline double IslandDensity(const FloatingIslandRecipeV1& recipe,
const FloatingIslandPlanV1& plan,
const FloatingIslandAnchorV1& island,
double x, double y, double z)
{
if (recipe.geometryVersion < 2)
{
const double dx = x - static_cast<double>(island.centerSubvoxels[0]);
const double dy = y - static_cast<double>(island.centerSubvoxels[1]);
const double radial = std::hypot(dx, dy);
const double topZ = static_cast<double>(island.topZSubvoxels);
const double bottomZ = static_cast<double>(island.bottomZSubvoxels);
const double verticalSpan = std::max(topZ - bottomZ, 1.0);
const double height = std::clamp((z - bottomZ) / verticalSpan, 0.0, 1.0);
const double taperEnd = Q16ToDouble(island.taperEndQ16);
const double taper = SmoothStep(std::clamp(height / std::max(taperEnd, 0.01), 0.0, 1.0));
const double effectiveRadius = std::max(0.0,
static_cast<double>(island.radiusSubvoxels) + LobeOffset(recipe, island, dx, dy, z));
const double envelope = effectiveRadius * taper;
const double top = IslandTopSurface(recipe, island,
static_cast<double>(island.centerSubvoxels[0]) + dx,
static_cast<double>(island.centerSubvoxels[1]) + dy);
return ApplySurfaceDetail(recipe, island.stableId,
std::max({z - top, radial - envelope, bottomZ - z}), x, y, z);
}
const FIslandColumnCacheEntry& Column=IslandColumnCache(recipe,plan,island,x,y);
const double dx=Column.DeltaX;
const double dy=Column.DeltaY;
const double radial=Column.Radial;
const double topZ = static_cast<double>(island.topZSubvoxels);
const double radius = static_cast<double>(island.radiusSubvoxels);
const double effectiveRadius = std::max(0.0,
radius + LobeOffsetWithPhase(recipe,Column.Angle,z,Column.LobePhase));
const double edge = std::clamp(radial / std::max(effectiveRadius, 1.0), 0.0, 1.0);
const double lowerSurface = static_cast<double>(IslandCrustBottom(recipe, island))
+ radius * 0.16 * edge * edge;
const double top = Column.TopSurface;
const double topDistance = z - top;
const double sideDistance = radial - effectiveRadius;
const double undersideDistance = lowerSurface - z;
double sdf = std::max({topDistance, sideDistance, undersideDistance});
const std::int32_t rootCount = RootCount(recipe, island);
const double rootBlend = std::clamp(radius * 0.12, 2.0 * Q, 5.0 * Q);
for (std::int32_t root = 0; root < rootCount; ++root)
sdf = SmoothMin(sdf, RootLobeDensity(recipe, island, root, x, y, z), rootBlend);
if (recipe.geometryVersion >= 5 && recipe.geometryVersion < 8
&& island.ordinal == recipe.anchorIslandOrdinal
&& recipe.anchorRootBottomZSubvoxels < island.bottomZSubvoxels)
{
// The central buttress must meet the island crust underside. IslandBottom
// also includes the four shorter root lobes and sits below that surface.
const double rootTop = static_cast<double>(IslandCrustBottom(recipe, island));
const double rootBottom = static_cast<double>(recipe.anchorRootBottomZSubvoxels);
const double depth = std::max(1.0, rootTop - rootBottom);
const double blend = std::clamp(static_cast<double>(recipe.anchorRootRadiusSubvoxels)
* 0.12, 3.0 * Q, 6.0 * Q);
// A slightly flared lower buttress joins the island's own root lobes to bedrock.
// Une base legerement evasee relie les racines de l'ile a la roche de fond.
const double t = std::clamp((rootTop - z) / depth, 0.0, 1.0);
const double rootRadius = static_cast<double>(recipe.anchorRootRadiusSubvoxels)
* (0.92 + 0.08 * SmoothStep(t));
double rootSdf = std::max({Column.Radial - rootRadius,
rootBottom - z, z - rootTop});
rootSdf = ApplySurfaceDetail(recipe, island.stableId ^ 0x414e43484f52524full,
rootSdf, x, y, z, 0.75);
sdf = SmoothMin(sdf, rootSdf, blend);
}
const double topDetailScale = topDistance >= std::max(sideDistance, undersideDistance)
|| z >= static_cast<double>(IslandCrustBottom(recipe, island)) + radius * 0.45
? 0.25 : 1.0;
return ApplySurfaceDetail(recipe, island.stableId, sdf, x, y, z, topDetailScale);
}
inline double ConnectorDensity(const FloatingIslandRecipeV1& recipe,
const FloatingIslandConnectorV1& edge,
double x, double y, double z,
bool* topSurface = nullptr,
double* alongFraction = nullptr)
{
const double ax = static_cast<double>(edge.startSubvoxels[0]);
const double ay = static_cast<double>(edge.startSubvoxels[1]);
const double az = static_cast<double>(edge.startSubvoxels[2]);
const double dx = static_cast<double>(edge.endSubvoxels[0]) - ax;
const double dy = static_cast<double>(edge.endSubvoxels[1]) - ay;
const double length = std::hypot(dx, dy);
if (!(length > 0.0)) return MaxDensity;
const double ux = dx / length, uy = dy / length;
const double px = x - ax, py = y - ay;
const double along = px * ux + py * uy;
const double side = std::abs(-px * uy + py * ux);
const double qx = side - static_cast<double>(edge.halfWidthSubvoxels);
const double qy = std::max(-along, along - length);
const double outside = std::hypot(std::max(qx, 0.0), std::max(qy, 0.0));
const double horizontal = outside + std::min(std::max(qx, qy), 0.0);
const double t = std::clamp(along / length, 0.0, 1.0);
if (alongFraction) *alongFraction = t;
const double deck = az + (static_cast<double>(edge.endSubvoxels[2]) - az) * t;
const double arch = recipe.geometryVersion >= 2
? 2.0 * static_cast<double>(edge.thicknessSubvoxels) * (4.0 * t * (1.0 - t))
: 0.0;
const double upper = z - deck;
const double lower = deck - static_cast<double>(edge.thicknessSubvoxels) - arch - z;
if (topSurface) *topSurface = upper >= std::max(horizontal, lower);
return std::max({horizontal, upper, lower});
}
inline std::int64_t FieldDensity(const FloatingIslandRecipeV1& recipe,
const FloatingIslandPlanV1& plan,
std::int64_t x, std::int64_t y, std::int64_t z)
{
if (plan.islands.empty()) return MaxDensity;
const double blend = static_cast<double>(recipe.blendRadiusSubvoxels);
double result = static_cast<double>(MaxDensity);
for (const auto& island : plan.islands)
{
double group = IslandDensity(recipe, plan, island,
static_cast<double>(x), static_cast<double>(y), static_cast<double>(z));
for (const auto& edge : plan.clusterEdges)
{
if (edge.fromIslandId != island.stableId) continue;
bool topSurface = false;
double alongFraction = 0.0;
double connector = ConnectorDensity(recipe, edge,
static_cast<double>(x), static_cast<double>(y), static_cast<double>(z),
&topSurface, &alongFraction);
if (recipe.geometryVersion >= 2)
connector = ApplySurfaceDetail(recipe, edge.stableId, connector,
static_cast<double>(x), static_cast<double>(y), static_cast<double>(z),
topSurface || alongFraction < 0.12 || alongFraction > 0.88 ? 0.25 : 1.0);
group = SmoothMin(group, connector, blend);
}
result = std::min(result, group);
}
return std::clamp(RoundQ(result), -MaxDensity, MaxDensity);
}
inline std::int64_t FieldDensityForTile(const FloatingIslandRecipeV1& recipe,
const FloatingIslandPlanV1& plan, std::int64_t x, std::int64_t y, std::int64_t z,
const FloatingIslandTileFeatureListV1& featureList)
{
if (plan.islands.empty()) return MaxDensity;
if (featureList.groupBounds.size() != plan.islands.size())
return FieldDensity(recipe, plan, x, y, z);
const auto& featureIndices = featureList.featureIndices;
const std::size_t islandCount = plan.islands.size();
const double blend = static_cast<double>(recipe.blendRadiusSubvoxels);
double result = static_cast<double>(MaxDensity);
const auto evaluateLocalGroup = [&](std::size_t islandIndex)
{
const auto& island = plan.islands[islandIndex];
double group = IslandDensity(recipe, plan, island,
static_cast<double>(x), static_cast<double>(y), static_cast<double>(z));
for (const std::uint32_t edgeIndex : featureIndices)
{
if (edgeIndex < islandCount) continue;
const std::size_t planIndex = static_cast<std::size_t>(edgeIndex) - islandCount;
if (planIndex >= plan.clusterEdges.size()) continue;
const auto& edge = plan.clusterEdges[planIndex];
if (edge.fromIslandId != island.stableId) continue;
bool topSurface = false;
double alongFraction = 0.0;
double connector = ConnectorDensity(recipe, edge,
static_cast<double>(x), static_cast<double>(y), static_cast<double>(z),
&topSurface, &alongFraction);
if (recipe.geometryVersion >= 2)
connector = ApplySurfaceDetail(recipe, edge.stableId, connector,
static_cast<double>(x), static_cast<double>(y), static_cast<double>(z),
topSurface || alongFraction < 0.12 || alongFraction > 0.88 ? 0.25 : 1.0);
group = SmoothMin(group, connector, blend);
}
return group;
};
const auto evaluateDistantGroup = [&](std::size_t islandIndex)
{
const auto& island = plan.islands[islandIndex];
double group = IslandDensity(recipe, plan, island,
static_cast<double>(x), static_cast<double>(y), static_cast<double>(z));
for (const auto& edge : plan.clusterEdges)
{
if (edge.fromIslandId != island.stableId) continue;
bool topSurface = false;
double alongFraction = 0.0;
double connector = ConnectorDensity(recipe, edge,
static_cast<double>(x), static_cast<double>(y), static_cast<double>(z),
&topSurface, &alongFraction);
if (recipe.geometryVersion >= 2)
connector = ApplySurfaceDetail(recipe, edge.stableId, connector,
static_cast<double>(x), static_cast<double>(y), static_cast<double>(z),
topSurface || alongFraction < 0.12 || alongFraction > 0.88 ? 0.25 : 1.0);
group = SmoothMin(group, connector, blend);
}
return group;
};
// Each local group keeps its complete ordered connector chain in the tile list.
// Les connecteurs d'un groupe local restent complets et dans leur ordre canonique.
for (const std::uint32_t index : featureIndices)
{
if (index >= islandCount) break;
result = std::min(result, evaluateLocalGroup(index));
}
// Air-side density magnitudes are part of the canonical field too. The per-tile list
// handles nearby groups; conservative group-box lower bounds prune distant exact SDFs.
// Les magnitudes d'air restent canoniques; les bornes de groupes eliminent les SDF lointains.
if (result >= 0.0)
{
if ((featureList.groupSearchOrder.size() != islandCount
|| featureList.groupDistanceLowerBoundsSubvoxels.size() != islandCount)
&& featureList.groupSearchComplete)
return FieldDensity(recipe, plan, x, y, z);
const std::size_t searchCount = std::min(featureList.groupSearchOrder.size(),
featureList.groupDistanceLowerBoundsSubvoxels.size());
for (std::size_t orderIndex = 0; orderIndex < searchCount; ++orderIndex)
{
const std::size_t islandIndex = featureList.groupSearchOrder[orderIndex];
if (islandIndex >= islandCount) return FieldDensity(recipe, plan, x, y, z);
if (std::binary_search(featureIndices.begin(), featureIndices.end(),
static_cast<std::uint32_t>(islandIndex))) continue;
const auto& bounds = featureList.groupBounds[islandIndex];
for (std::size_t axis = 0; axis < 3; ++axis)
if (bounds.minimumSubvoxels[axis] > bounds.maximumSubvoxels[axis])
return FieldDensity(recipe, plan, x, y, z);
if (static_cast<double>(featureList.groupDistanceLowerBoundsSubvoxels[orderIndex]) > result)
break;
result = std::min(result, evaluateDistantGroup(islandIndex));
}
}
return std::clamp(RoundQ(result), -MaxDensity, MaxDensity);
}
inline void SetIslandBounds(const FloatingIslandRecipeV1& recipe,
FloatingIslandAnchorV1& island)
{
const std::int64_t pad = IslandBlendMargin(recipe) + recipe.roughnessSubvoxels;
const bool isAnchorRoot=recipe.geometryVersion>=5&&recipe.geometryVersion<8
&&island.ordinal==recipe.anchorIslandOrdinal;
const std::int64_t reach = std::max(island.radiusSubvoxels,
isAnchorRoot?recipe.anchorRootRadiusSubvoxels:std::int64_t{0})
+ recipe.contourWarpSubvoxels + pad;
island.influenceMinimumSubvoxels = {island.centerSubvoxels[0] - reach,
island.centerSubvoxels[1] - reach,
(isAnchorRoot?std::min(island.bottomZSubvoxels,recipe.anchorRootBottomZSubvoxels)
:island.bottomZSubvoxels)-pad};
island.influenceMaximumSubvoxels = {island.centerSubvoxels[0] + reach,
island.centerSubvoxels[1] + reach, island.topZSubvoxels + pad};
}
inline void SetConnectorBounds(const FloatingIslandRecipeV1& recipe,
FloatingIslandConnectorV1& edge)
{
const std::int64_t pad = recipe.blendRadiusSubvoxels + recipe.roughnessSubvoxels;
const std::int64_t archDepth = recipe.geometryVersion >= 2
? 2 * edge.thicknessSubvoxels : 0;
const std::int64_t xyPad = edge.halfWidthSubvoxels + pad;
edge.influenceMinimumSubvoxels = {
std::min(edge.startSubvoxels[0], edge.endSubvoxels[0]) - xyPad,
std::min(edge.startSubvoxels[1], edge.endSubvoxels[1]) - xyPad,
std::min(edge.startSubvoxels[2], edge.endSubvoxels[2]) - edge.thicknessSubvoxels - pad};
edge.influenceMinimumSubvoxels[2] -= archDepth;
edge.influenceMaximumSubvoxels = {
std::max(edge.startSubvoxels[0], edge.endSubvoxels[0]) + xyPad,
std::max(edge.startSubvoxels[1], edge.endSubvoxels[1]) + xyPad,
std::max(edge.startSubvoxels[2], edge.endSubvoxels[2]) + pad};
}
inline const FloatingIslandAnchorV1* FindIsland(const FloatingIslandPlanV1& plan,
std::uint64_t id)
{
const auto found = std::find_if(plan.islands.begin(), plan.islands.end(),
[id](const auto& island) { return island.stableId == id; });
return found == plan.islands.end() ? nullptr : &*found;
}
inline bool ValidateManifest(const FloatingIslandManifestV1& manifest, std::string& error)
{
if (!ValidateRecipe(manifest.recipe, error)) return false;
const auto& recipe = manifest.recipe;
const auto& plan = manifest.plan;
if (plan.islands.size() < recipe.primaryIslandCount
|| plan.islands.size() > recipe.primaryIslandCount + 2
|| plan.clusterEdges.size() + 1 < plan.islands.size()
|| plan.clusterEdges.size() > plan.islands.size() + 1
|| plan.route.size() < 2 || plan.route.size() > 4096)
{ error = "floating-island plan has an invalid bounded island graph or route"; return false; }
std::unordered_set<std::uint64_t> islandIds;
std::unordered_set<std::uint32_t> ordinals;
const FloatingIslandAnchorV1* anchorIsland = nullptr;
for (const auto& island : plan.islands)
{
const bool isAnchor=recipe.geometryVersion>=5
&&island.ordinal==recipe.anchorIslandOrdinal;
if(isAnchor)anchorIsland=&island;
if (island.stableId == 0 || !islandIds.insert(island.stableId).second
|| !ordinals.insert(island.ordinal).second
|| island.radiusSubvoxels < (isAnchor?recipe.anchorIslandMinimumRadiusSubvoxels
:recipe.minimumRadiusSubvoxels)
|| island.radiusSubvoxels > (isAnchor
?std::max(recipe.anchorIslandMinimumRadiusSubvoxels,recipe.maximumRadiusSubvoxels)
:recipe.maximumRadiusSubvoxels)
|| island.topHalfSubvoxels <= 0 || island.topZSubvoxels <= island.bottomZSubvoxels
|| island.taperEndQ16 < recipe.taperEndMinimumQ16
|| island.taperEndQ16 > recipe.taperEndMaximumQ16
|| island.centerSubvoxels[2] != island.topZSubvoxels - island.topHalfSubvoxels)
{ error = "floating-island anchor has an invalid or unstable identity or profile"; return false; }
if (recipe.geometryVersion >= 2
&& static_cast<double>(island.topZSubvoxels - island.bottomZSubvoxels)
/ (2.0 * static_cast<double>(island.radiusSubvoxels)) < 0.70)
{ error = "floating-island crust/root depth is below its minimum width ratio"; return false; }
for (std::size_t axis = 0; axis < 3; ++axis)
{
if (!ValidScalar(island.centerSubvoxels[axis])
|| island.influenceMinimumSubvoxels[axis] > island.influenceMaximumSubvoxels[axis])
{ error = "floating-island anchor has invalid coordinates or an inverted bound"; return false; }
}
const std::int64_t pad = IslandBlendMargin(recipe) + recipe.roughnessSubvoxels;
const bool hasAnchorRoot=recipe.geometryVersion>=5&&recipe.geometryVersion<8&&isAnchor;
const std::int64_t reach = std::max(island.radiusSubvoxels,
hasAnchorRoot?recipe.anchorRootRadiusSubvoxels:std::int64_t{0})
+ recipe.contourWarpSubvoxels + pad;
if (island.influenceMinimumSubvoxels[0] > island.centerSubvoxels[0] - reach
|| island.influenceMaximumSubvoxels[0] < island.centerSubvoxels[0] + reach
|| island.influenceMinimumSubvoxels[1] > island.centerSubvoxels[1] - reach
|| island.influenceMaximumSubvoxels[1] < island.centerSubvoxels[1] + reach
|| island.influenceMinimumSubvoxels[2] > (hasAnchorRoot
?std::min(island.bottomZSubvoxels,recipe.anchorRootBottomZSubvoxels)
:island.bottomZSubvoxels) - pad
|| island.influenceMaximumSubvoxels[2] < island.topZSubvoxels + pad
|| island.bottomZSubvoxels < recipe.layoutMinimumZSubvoxels + recipe.boundarySealSubvoxels
|| island.topZSubvoxels > recipe.layoutMaximumZSubvoxels - recipe.boundarySealSubvoxels)
{ error = "floating-island bound omits its taper, lobe warp, blend or finite layout margin"; return false; }
}
for (std::uint32_t ordinal = 0; ordinal < recipe.primaryIslandCount; ++ordinal)
if (!ordinals.contains(ordinal))
{ error = "floating-island route chain is missing a primary seeded anchor"; return false; }
if(recipe.geometryVersion>=5)
{
if(!anchorIsland)
{error="floating-island plan is missing its unique passage anchor island";return false;}
const double anchorDistance=std::hypot(
static_cast<double>(anchorIsland->centerSubvoxels[0]),
static_cast<double>(anchorIsland->centerSubvoxels[1]));
if(anchorDistance>recipe.anchorPlacementRadiusSubvoxels
||recipe.anchorRootBottomZSubvoxels>=anchorIsland->bottomZSubvoxels
||recipe.anchorMouthOffsetSubvoxels+recipe.routeConstraints.capsuleRadiusSubvoxels
>=anchorIsland->radiusSubvoxels)
{error="floating-island anchor island or its mouth leaves the passage radius or rock envelope";return false;}
}
std::unordered_set<std::uint64_t> edgeIds;
std::unordered_map<std::uint64_t, std::vector<std::uint64_t>> adjacency;
for (const auto& edge : plan.clusterEdges)
{
if (edge.stableId == 0 || !edgeIds.insert(edge.stableId).second
|| edge.fromIslandId == edge.toIslandId || !islandIds.contains(edge.fromIslandId)
|| !islandIds.contains(edge.toIslandId) || !edge.sharpTagReady
|| edge.halfWidthSubvoxels < recipe.routeConstraints.capsuleRadiusSubvoxels + Q
|| edge.thicknessSubvoxels < 2 * Q)
{ error = "floating-island graph edge is invalid, unconnected or missing its sharp tag"; return false; }
for (std::size_t axis = 0; axis < 3; ++axis)
if (!ValidScalar(edge.startSubvoxels[axis]) || !ValidScalar(edge.endSubvoxels[axis])
|| edge.influenceMinimumSubvoxels[axis] > edge.influenceMaximumSubvoxels[axis])
{ error = "floating-island connector has invalid endpoint or influence data"; return false; }
const std::int64_t pad = recipe.blendRadiusSubvoxels + recipe.roughnessSubvoxels;
const std::int64_t xyPad = edge.halfWidthSubvoxels + pad;
if (edge.influenceMinimumSubvoxels[0] > std::min(edge.startSubvoxels[0], edge.endSubvoxels[0]) - xyPad
|| edge.influenceMaximumSubvoxels[0] < std::max(edge.startSubvoxels[0], edge.endSubvoxels[0]) + xyPad
|| edge.influenceMinimumSubvoxels[1] > std::min(edge.startSubvoxels[1], edge.endSubvoxels[1]) - xyPad
|| edge.influenceMaximumSubvoxels[1] < std::max(edge.startSubvoxels[1], edge.endSubvoxels[1]) + xyPad
|| edge.influenceMinimumSubvoxels[2] > std::min(edge.startSubvoxels[2], edge.endSubvoxels[2])
- edge.thicknessSubvoxels - pad
- (recipe.geometryVersion >= 2 ? 2 * edge.thicknessSubvoxels : 0)
|| edge.influenceMaximumSubvoxels[2] < std::max(edge.startSubvoxels[2], edge.endSubvoxels[2]) + pad)
{ error = "floating-island connector bound omits its swept bridge, blend or thickness"; return false; }
adjacency[edge.fromIslandId].push_back(edge.toIslandId);
adjacency[edge.toIslandId].push_back(edge.fromIslandId);
}
std::unordered_set<std::uint64_t> reached;
std::vector<std::uint64_t> pending{plan.islands.front().stableId};
while (!pending.empty())
{
const std::uint64_t current = pending.back(); pending.pop_back();
if (!reached.insert(current).second) continue;
for (const auto next : adjacency[current]) if (!reached.contains(next)) pending.push_back(next);
}
if (reached.size() != plan.islands.size())
{ error = "floating-island cluster graph is disconnected"; return false; }
std::unordered_set<std::uint64_t> routeIds;
for (const auto& point : plan.route)
{
if (point.stableFeatureId == 0 || !routeIds.insert(point.stableFeatureId).second
|| point.radiusSubvoxels < recipe.routeConstraints.capsuleRadiusSubvoxels
|| !std::all_of(point.positionSubvoxels.begin(), point.positionSubvoxels.end(), ValidScalar))
{ error = "floating-island route point has invalid identity, clearance or position"; return false; }
const std::int64_t halfHeight = recipe.routeConstraints.capsuleHeightSubvoxels / 2;
if (point.positionSubvoxels[2] - halfHeight < recipe.layoutMinimumZSubvoxels
|| point.positionSubvoxels[2] + halfHeight > recipe.layoutMaximumZSubvoxels)
{ error = "floating-island route capsule escapes its finite vertical layout"; return false; }
}
for (std::size_t i = 1; i < plan.route.size(); ++i)
{
double square = 0.0;
for (std::size_t axis = 0; axis < 3; ++axis)
{
const std::int64_t delta = plan.route[i].positionSubvoxels[axis]
- plan.route[i - 1].positionSubvoxels[axis];
if (std::abs(delta) > 4096 * Q)
{ error = "floating-island route segment exceeds its bounded sweep length"; return false; }
square += static_cast<double>(delta) * static_cast<double>(delta);
}
if (!(square > 0.0))
{ error = "floating-island route has a zero-length segment"; return false; }
}
return true;
}
inline double TopLevel(const FloatingIslandRecipeV1& recipe, std::uint64_t site,
double centerZ, std::int64_t radius, double maxRise)
{
const std::int64_t signedDelta = SignedRange(recipe.seed, site, 0x4c4556454cull,
static_cast<std::int64_t>(std::llround(maxRise)));
return centerZ + static_cast<double>(signedDelta);
}
inline FloatingIslandAnchorV1 MakeIsland(const FloatingIslandRecipeV1& recipe,
std::uint32_t ordinal, std::int64_t x, std::int64_t y, std::int64_t topZ,
std::uint64_t siteSalt)
{
FloatingIslandAnchorV1 island;
island.ordinal = ordinal;
island.stableId = StableId(recipe.seed, siteSalt, 0x49534c414e44ull);
const std::uint32_t radiusRoll = UnitQ16(recipe.seed, siteSalt, 0x524144495553ull);
island.radiusSubvoxels = recipe.minimumRadiusSubvoxels
+ (recipe.maximumRadiusSubvoxels - recipe.minimumRadiusSubvoxels)
* radiusRoll / 65535;
island.topHalfSubvoxels = std::max<std::int64_t>(1, island.radiusSubvoxels / 5);
island.topZSubvoxels = topZ;
island.bottomZSubvoxels = IslandBottom(recipe, topZ, island.radiusSubvoxels);
island.centerSubvoxels = {x, y, topZ - island.topHalfSubvoxels};
island.taperEndQ16 = recipe.taperEndMinimumQ16
+ (recipe.taperEndMaximumQ16 - recipe.taperEndMinimumQ16)
* UnitQ16(recipe.seed, siteSalt, 0x5441504552ull) / 65535;
SetIslandBounds(recipe, island);
return island;
}
inline FloatingIslandConnectorV1 MakeConnector(const FloatingIslandRecipeV1& recipe,
const FloatingIslandAnchorV1& from, const FloatingIslandAnchorV1& to,
std::uint64_t siteSalt)
{
FloatingIslandConnectorV1 edge;
edge.stableId = StableId(recipe.seed, siteSalt, 0x425249444745ull);
edge.fromIslandId = from.stableId;
edge.toIslandId = to.stableId;
const double dx = static_cast<double>(to.centerSubvoxels[0] - from.centerSubvoxels[0]);
const double dy = static_cast<double>(to.centerSubvoxels[1] - from.centerSubvoxels[1]);
const double length = std::hypot(dx, dy);
const double ux = dx / std::max(length, 1.0);
const double uy = dy / std::max(length, 1.0);
// Land each connector well inside the broad top so the full capsule footprint remains
// supported across the bridge-to-island transition, including bounded surface roughness.
// Les connecteurs recouvrent le plateau pour soutenir toute la capsule au raccord.
// Keep the narrow span exposed to open sky; a smaller fraction buries most of it in the island tops.
// Garder la passerelle etroite visible dans le vide ; une fraction plus faible l'enfouit dans les plateaux.
const double landingFraction = recipe.geometryVersion >= 2 ? 0.52 : 0.62;
const double fromReach = static_cast<double>(from.radiusSubvoxels) * landingFraction;
const double toReach = static_cast<double>(to.radiusSubvoxels) * landingFraction;
const double sx = from.centerSubvoxels[0] + ux * fromReach;
const double sy = from.centerSubvoxels[1] + uy * fromReach;
const double ex = to.centerSubvoxels[0] - ux * toReach;
const double ey = to.centerSubvoxels[1] - uy * toReach;
const double routeDeckLift = recipe.geometryVersion >= 2 ? 1.0 * Q : 0.0;
const double sz = IslandTopSurface(recipe, from, sx, sy) + routeDeckLift;
const double ez = IslandTopSurface(recipe, to, ex, ey) + routeDeckLift;
edge.startSubvoxels = {RoundQ(sx), RoundQ(sy), RoundQ(sz)};
edge.endSubvoxels = {RoundQ(ex), RoundQ(ey), RoundQ(ez)};
edge.halfWidthSubvoxels = recipe.bridgeHalfWidthSubvoxels;
edge.thicknessSubvoxels = recipe.bridgeThicknessSubvoxels;
SetConnectorBounds(recipe, edge);
return edge;
}
inline std::optional<std::int64_t> FindSurfaceZ(const FloatingIslandRecipeV1& recipe,
const FloatingIslandPlanV1& plan, std::int64_t x, std::int64_t y)
{
const std::int64_t step = std::max<std::int64_t>(1, recipe.routeConstraints.sweepSpacingSubvoxels);
std::int64_t aboveZ = recipe.layoutMaximumZSubvoxels + 8 * Q;
std::int64_t aboveDensity = FieldDensity(recipe, plan, x, y, aboveZ);
if (aboveDensity < 0) return std::nullopt;
for (std::int64_t z = aboveZ - step; z >= recipe.layoutMinimumZSubvoxels - 8 * Q; z -= step)
{
const std::int64_t density = FieldDensity(recipe, plan, x, y, z);
if (density < 0)
{
std::int64_t outside = aboveZ, inside = z;
while (outside - inside > 1)
{
const std::int64_t middle = inside + (outside - inside) / 2;
if (FieldDensity(recipe, plan, x, y, middle) < 0) inside = middle;
else outside = middle;
}
return outside;
}
aboveZ = z;
aboveDensity = density;
}
return std::nullopt;
}
inline std::optional<std::int64_t> FindCapsuleFootingTopZ(
const FloatingIslandRecipeV1& recipe, const FloatingIslandPlanV1& plan,
std::int64_t x, std::int64_t y)
{
const double radius = static_cast<double>(recipe.routeConstraints.capsuleRadiusSubvoxels);
constexpr std::array<std::array<double, 2>, 17> footprint = {{{0.0, 0.0},
{0.6, 0.0}, {-0.6, 0.0}, {0.0, 0.6}, {0.0, -0.6},
{0.424264, 0.424264}, {-0.424264, 0.424264}, {0.424264, -0.424264}, {-0.424264, -0.424264},
{0.95, 0.0}, {-0.95, 0.0}, {0.0, 0.95}, {0.0, -0.95},
{0.671751, 0.671751}, {-0.671751, 0.671751}, {0.671751, -0.671751}, {-0.671751, -0.671751}}};
std::int64_t highest = std::numeric_limits<std::int64_t>::min();
for (const auto& offset : footprint)
{
const std::int64_t sampleX = RoundQ(static_cast<double>(x) + offset[0] * radius);
const std::int64_t sampleY = RoundQ(static_cast<double>(y) + offset[1] * radius);
const auto surface = FindSurfaceZ(recipe, plan, sampleX, sampleY);
if (!surface) return std::nullopt;
highest = std::max(highest, *surface);
}
return highest;
}
inline bool ReadConstraints(const VFJson::Value& value, WorldAssemblyRouteConstraintsV1& output)
{
std::uint32_t version = 0, probes = 0;
if (!ReadU32(value.Get("version"), version)
|| !ReadInteger(value.Get("capsule_radius_q"), output.capsuleRadiusSubvoxels)
|| !ReadInteger(value.Get("capsule_height_q"), output.capsuleHeightSubvoxels)
|| !ReadInteger(value.Get("maximum_grade_q16"), output.maximumGradeQ16)
|| !ReadInteger(value.Get("sweep_spacing_q"), output.sweepSpacingSubvoxels)
|| !ReadInteger(value.Get("support_probe_radius_q"), output.supportProbeRadiusSubvoxels)
|| !ReadInteger(value.Get("support_search_depth_q"), output.supportSearchDepthSubvoxels)
|| !ReadInteger(value.Get("minimum_clearance_q"), output.minimumClearanceSubvoxels)
|| !ReadU32(value.Get("minimum_support_probes"), probes)) return false;
output.version = version; output.minimumSupportProbes = probes;
return true;
}
inline void WriteConstraints(std::ostream& out, const WorldAssemblyRouteConstraintsV1& value)
{
out << "\"route_constraints\":{\"version\":" << value.version
<< ",\"capsule_radius_q\":" << value.capsuleRadiusSubvoxels
<< ",\"capsule_height_q\":" << value.capsuleHeightSubvoxels
<< ",\"maximum_grade_q16\":" << value.maximumGradeQ16
<< ",\"sweep_spacing_q\":" << value.sweepSpacingSubvoxels
<< ",\"support_probe_radius_q\":" << value.supportProbeRadiusSubvoxels
<< ",\"support_search_depth_q\":" << value.supportSearchDepthSubvoxels
<< ",\"minimum_clearance_q\":" << value.minimumClearanceSubvoxels
<< ",\"minimum_support_probes\":" << value.minimumSupportProbes << '}';
}
struct RandomStream
{
std::uint64_t state;
std::uint64_t next()
{
state = Mix(state);
return state;
}
};
}
std::optional<FloatingIslandManifestV1> FloatingIslandManifestV1::FromJson(
std::string_view json, std::string& error,
std::int64_t resolvedLayoutChunkWidthSubvoxels)
{
VFJson::Value root;
if (!VFJson::Parser(std::string(json)).Parse(root, error)) return std::nullopt;
FloatingIslandManifestV1 manifest;
std::uint32_t schema = 0;
const auto& recipeValue = root.Get("recipe");
const auto& planValue = root.Get("plan");
std::uint64_t manifestHash = 0, recipeHash = 0, planHash = 0;
const auto& geometryVersionValue = recipeValue.Get("geometry_version");
if (geometryVersionValue.IsNumber())
{
if (!floating_island_detail::ReadU32(geometryVersionValue, manifest.recipe.geometryVersion)
|| !floating_island_detail::ReadInteger(recipeValue.Get("crust_depth_bonus_q16"),
manifest.recipe.crustDepthBonusQ16)
|| !floating_island_detail::ReadInteger(recipeValue.Get("root_depth_ratio_q16"),
manifest.recipe.rootDepthRatioQ16))
{ error = "floating-island geometry profile is incomplete"; return std::nullopt; }
}
else
{
// Frozen M3 round-1 manifests omit profile metadata and retain their original field.
manifest.recipe.geometryVersion = 1;
manifest.recipe.crustDepthBonusQ16 = 0;
manifest.recipe.rootDepthRatioQ16 = 0;
}
if(manifest.recipe.geometryVersion>=4)
{
if(!floating_island_detail::ReadInteger(recipeValue.Get("layout_chunk_width_q"),
manifest.recipe.layoutChunkWidthSubvoxels))
{error="floating-island recipe is missing its resolved layout chunk width";return std::nullopt;}
if(resolvedLayoutChunkWidthSubvoxels>0
&& manifest.recipe.layoutChunkWidthSubvoxels!=resolvedLayoutChunkWidthSubvoxels)
{error="floating-island recipe layout width disagrees with the resolved world layout";return std::nullopt;}
}
if(manifest.recipe.geometryVersion>=5)
{
if(!floating_island_detail::ReadU32(recipeValue.Get("anchor_island_ordinal"),
manifest.recipe.anchorIslandOrdinal)
||!floating_island_detail::ReadInteger(recipeValue.Get("anchor_placement_radius_q"),
manifest.recipe.anchorPlacementRadiusSubvoxels)
||!floating_island_detail::ReadInteger(recipeValue.Get("anchor_root_radius_q"),
manifest.recipe.anchorRootRadiusSubvoxels)
||!floating_island_detail::ReadInteger(recipeValue.Get("anchor_island_minimum_radius_q"),
manifest.recipe.anchorIslandMinimumRadiusSubvoxels)
||!floating_island_detail::ReadInteger(recipeValue.Get("anchor_root_bottom_z_q"),
manifest.recipe.anchorRootBottomZSubvoxels)
||!floating_island_detail::ReadInteger(recipeValue.Get("anchor_mouth_offset_q"),
manifest.recipe.anchorMouthOffsetSubvoxels))
{error="floating-island geometry version 7 is missing a passage-rooted anchor field";return std::nullopt;}
}
if (!floating_island_detail::ReadU32(root.Get("schema_version"), schema)
|| !floating_island_detail::ReadHash(root.Get("content_hash"), manifestHash)
|| !floating_island_detail::ReadU32(recipeValue.Get("schema_version"), manifest.recipe.schemaVersion)
|| !floating_island_detail::ReadU32(recipeValue.Get("units_per_voxel"), manifest.recipe.unitsPerVoxel)
|| !floating_island_detail::ReadU32(recipeValue.Get("owner_voxel_size_microns"), manifest.recipe.ownerVoxelSizeMicrons)
|| !floating_island_detail::ReadU32(recipeValue.Get("core_voxel_size_microns"), manifest.recipe.coreVoxelSizeMicrons)
|| !floating_island_detail::ReadU32(recipeValue.Get("owner_to_core_scale_numerator"), manifest.recipe.ownerToCoreScaleNumerator)
|| !floating_island_detail::ReadU32(recipeValue.Get("owner_to_core_scale_denominator"), manifest.recipe.ownerToCoreScaleDenominator)
|| !floating_island_detail::ReadU32(recipeValue.Get("seed"), manifest.recipe.seed)
|| !floating_island_detail::ReadInteger(recipeValue.Get("origin_x_q"), manifest.recipe.layoutOriginXSubvoxels)
|| !floating_island_detail::ReadInteger(recipeValue.Get("origin_y_q"), manifest.recipe.layoutOriginYSubvoxels)
|| !floating_island_detail::ReadInteger(recipeValue.Get("center_top_z_q"), manifest.recipe.layoutCenterTopZSubvoxels)
|| !floating_island_detail::ReadInteger(recipeValue.Get("layout_min_z_q"), manifest.recipe.layoutMinimumZSubvoxels)
|| !floating_island_detail::ReadInteger(recipeValue.Get("layout_max_z_q"), manifest.recipe.layoutMaximumZSubvoxels)
|| !floating_island_detail::ReadU32(recipeValue.Get("primary_island_count"), manifest.recipe.primaryIslandCount)
|| !floating_island_detail::ReadInteger(recipeValue.Get("island_density_q16"), manifest.recipe.islandDensityQ16)
|| !floating_island_detail::ReadInteger(recipeValue.Get("spacing_q"), manifest.recipe.spacingSubvoxels)
|| !floating_island_detail::ReadInteger(recipeValue.Get("minimum_radius_q"), manifest.recipe.minimumRadiusSubvoxels)
|| !floating_island_detail::ReadInteger(recipeValue.Get("maximum_radius_q"), manifest.recipe.maximumRadiusSubvoxels)
|| !floating_island_detail::ReadInteger(recipeValue.Get("thickness_ratio_q16"), manifest.recipe.thicknessRatioQ16)
|| !floating_island_detail::ReadInteger(recipeValue.Get("vertical_jitter_q16"), manifest.recipe.verticalJitterQ16)
|| !floating_island_detail::ReadInteger(recipeValue.Get("top_flatten_q16"), manifest.recipe.topFlattenQ16)
|| !floating_island_detail::ReadInteger(recipeValue.Get("taper_end_min_q16"), manifest.recipe.taperEndMinimumQ16)
|| !floating_island_detail::ReadInteger(recipeValue.Get("taper_end_max_q16"), manifest.recipe.taperEndMaximumQ16)
|| !floating_island_detail::ReadInteger(recipeValue.Get("blend_radius_q"), manifest.recipe.blendRadiusSubvoxels)
|| !floating_island_detail::ReadInteger(recipeValue.Get("contour_warp_q"), manifest.recipe.contourWarpSubvoxels)
|| !floating_island_detail::ReadInteger(recipeValue.Get("roughness_q"), manifest.recipe.roughnessSubvoxels)
|| !floating_island_detail::ReadInteger(recipeValue.Get("boundary_seal_q"), manifest.recipe.boundarySealSubvoxels)
|| !floating_island_detail::ReadInteger(recipeValue.Get("bridge_half_width_q"), manifest.recipe.bridgeHalfWidthSubvoxels)
|| !floating_island_detail::ReadInteger(recipeValue.Get("bridge_thickness_q"), manifest.recipe.bridgeThicknessSubvoxels)
|| !floating_island_detail::ReadHash(recipeValue.Get("content_hash"), recipeHash)
|| !floating_island_detail::ReadConstraints(recipeValue.Get("route_constraints"), manifest.recipe.routeConstraints)
|| !floating_island_detail::ReadHash(planValue.Get("content_hash"), planHash)
|| schema != 1)
{ error = "floating-island manifest is missing a required versioned recipe or plan field"; return std::nullopt; }
if(manifest.recipe.geometryVersion<4 && resolvedLayoutChunkWidthSubvoxels>0)
manifest.recipe.layoutChunkWidthSubvoxels=resolvedLayoutChunkWidthSubvoxels;
else if(manifest.recipe.geometryVersion<4)
{ error="legacy floating-island manifest requires the world's resolved layout chunk width"; return std::nullopt; }
manifest.recipe.contentHash = recipeHash;
manifest.contentHash = manifestHash;
manifest.plan.contentHash = planHash;
const auto& islands = planValue.Get("islands").AsArray();
for (const auto& value : islands)
{
FloatingIslandAnchorV1 island;
if (!floating_island_detail::ReadHash(value.Get("stable_id"), island.stableId)
|| !floating_island_detail::ReadU32(value.Get("ordinal"), island.ordinal)
|| !floating_island_detail::ReadArray(value.Get("center_q"), island.centerSubvoxels)
|| !floating_island_detail::ReadInteger(value.Get("radius_q"), island.radiusSubvoxels)
|| !floating_island_detail::ReadInteger(value.Get("top_half_q"), island.topHalfSubvoxels)
|| !floating_island_detail::ReadInteger(value.Get("top_z_q"), island.topZSubvoxels)
|| !floating_island_detail::ReadInteger(value.Get("bottom_z_q"), island.bottomZSubvoxels)
|| !floating_island_detail::ReadInteger(value.Get("taper_end_q16"), island.taperEndQ16)
|| !floating_island_detail::ReadArray(value.Get("bounds_min_q"), island.influenceMinimumSubvoxels)
|| !floating_island_detail::ReadArray(value.Get("bounds_max_q"), island.influenceMaximumSubvoxels))
{ error = "floating-island manifest contains a malformed seeded anchor"; return std::nullopt; }
manifest.plan.islands.push_back(island);
}
const auto& edges = planValue.Get("cluster_edges").AsArray();
for (const auto& value : edges)
{
FloatingIslandConnectorV1 edge;
if (!floating_island_detail::ReadHash(value.Get("stable_id"), edge.stableId)
|| !floating_island_detail::ReadHash(value.Get("from_island_id"), edge.fromIslandId)
|| !floating_island_detail::ReadHash(value.Get("to_island_id"), edge.toIslandId)
|| !floating_island_detail::ReadArray(value.Get("start_q"), edge.startSubvoxels)
|| !floating_island_detail::ReadArray(value.Get("end_q"), edge.endSubvoxels)
|| !floating_island_detail::ReadInteger(value.Get("half_width_q"), edge.halfWidthSubvoxels)
|| !floating_island_detail::ReadInteger(value.Get("thickness_q"), edge.thicknessSubvoxels)
|| !floating_island_detail::ReadArray(value.Get("bounds_min_q"), edge.influenceMinimumSubvoxels)
|| !floating_island_detail::ReadArray(value.Get("bounds_max_q"), edge.influenceMaximumSubvoxels)
|| !std::holds_alternative<bool>(value.Get("sharp_tag_ready").Data))
{ error = "floating-island manifest contains a malformed graph connector"; return std::nullopt; }
edge.sharpTagReady = value.Get("sharp_tag_ready").Boolean();
manifest.plan.clusterEdges.push_back(edge);
}
const auto& route = planValue.Get("route").AsArray();
for (const auto& value : route)
{
WorldAssemblyRoutePointV1 point;
if (!floating_island_detail::ReadHash(value.Get("feature_id"), point.stableFeatureId)
|| !floating_island_detail::ReadArray(value.Get("position_q"), point.positionSubvoxels)
|| !floating_island_detail::ReadInteger(value.Get("radius_q"), point.radiusSubvoxels))
{ error = "floating-island manifest contains a malformed route waypoint"; return std::nullopt; }
manifest.plan.route.push_back(point);
}
if (!manifest.validate(error)) return std::nullopt;
return manifest;
}
bool FloatingIslandManifestV1::finalize(std::string& error)
{
if (!floating_island_detail::ValidateManifest(*this, error)) return false;
recipe.contentHash = floating_island_detail::HashRecipe(recipe);
plan.contentHash = floating_island_detail::HashPlan(plan);
contentHash = floating_island_detail::HashManifest(*this);
return true;
}
bool FloatingIslandManifestV1::validate(std::string& error) const
{
FloatingIslandManifestV1 copy = *this;
const std::uint64_t expectedRecipe = recipe.contentHash;
const std::uint64_t expectedPlan = plan.contentHash;
const std::uint64_t expectedManifest = contentHash;
if (!copy.finalize(error)) return false;
if (expectedRecipe != copy.recipe.contentHash || expectedPlan != copy.plan.contentHash
|| expectedManifest != copy.contentHash)
{ error = "floating-island recipe, cluster plan or manifest hash does not match its data"; return false; }
return true;
}
std::string FloatingIslandManifestV1::ToJson() const
{
std::string error;
FloatingIslandManifestV1 value = *this;
if (!value.finalize(error)) return {};
const auto& recipe = value.recipe;
const auto& plan = value.plan;
std::ostringstream out;
out << "{\"schema_version\":1,\"content_hash\":\""
<< floating_island_detail::HashText(value.contentHash) << "\",\"recipe\":{"
<< "\"schema_version\":" << recipe.schemaVersion
<< ",\"units_per_voxel\":" << recipe.unitsPerVoxel
<< ",\"owner_voxel_size_microns\":" << recipe.ownerVoxelSizeMicrons
<< ",\"core_voxel_size_microns\":" << recipe.coreVoxelSizeMicrons
<< ",\"owner_to_core_scale_numerator\":" << recipe.ownerToCoreScaleNumerator
<< ",\"owner_to_core_scale_denominator\":" << recipe.ownerToCoreScaleDenominator
<< ",\"seed\":" << recipe.seed
<< ",\"origin_x_q\":" << recipe.layoutOriginXSubvoxels
<< ",\"origin_y_q\":" << recipe.layoutOriginYSubvoxels
<< ",\"center_top_z_q\":" << recipe.layoutCenterTopZSubvoxels
<< ",\"layout_min_z_q\":" << recipe.layoutMinimumZSubvoxels
<< ",\"layout_max_z_q\":" << recipe.layoutMaximumZSubvoxels
<< ",\"primary_island_count\":" << recipe.primaryIslandCount
<< ",\"island_density_q16\":" << recipe.islandDensityQ16
<< ",\"spacing_q\":" << recipe.spacingSubvoxels
<< ",\"minimum_radius_q\":" << recipe.minimumRadiusSubvoxels
<< ",\"maximum_radius_q\":" << recipe.maximumRadiusSubvoxels
<< ",\"thickness_ratio_q16\":" << recipe.thicknessRatioQ16
<< ",\"vertical_jitter_q16\":" << recipe.verticalJitterQ16
<< ",\"top_flatten_q16\":" << recipe.topFlattenQ16
<< ",\"taper_end_min_q16\":" << recipe.taperEndMinimumQ16
<< ",\"taper_end_max_q16\":" << recipe.taperEndMaximumQ16;
if (recipe.geometryVersion >= 2)
out << ",\"geometry_version\":" << recipe.geometryVersion
<< ",\"crust_depth_bonus_q16\":" << recipe.crustDepthBonusQ16
<< ",\"root_depth_ratio_q16\":" << recipe.rootDepthRatioQ16;
if(recipe.geometryVersion>=4)
out << ",\"layout_chunk_width_q\":" << recipe.layoutChunkWidthSubvoxels;
if(recipe.geometryVersion>=5)
out << ",\"anchor_island_ordinal\":" << recipe.anchorIslandOrdinal
<< ",\"anchor_placement_radius_q\":" << recipe.anchorPlacementRadiusSubvoxels
<< ",\"anchor_root_radius_q\":" << recipe.anchorRootRadiusSubvoxels
<< ",\"anchor_island_minimum_radius_q\":" << recipe.anchorIslandMinimumRadiusSubvoxels
<< ",\"anchor_root_bottom_z_q\":" << recipe.anchorRootBottomZSubvoxels
<< ",\"anchor_mouth_offset_q\":" << recipe.anchorMouthOffsetSubvoxels;
out << ",\"blend_radius_q\":" << recipe.blendRadiusSubvoxels
<< ",\"contour_warp_q\":" << recipe.contourWarpSubvoxels
<< ",\"roughness_q\":" << recipe.roughnessSubvoxels
<< ",\"boundary_seal_q\":" << recipe.boundarySealSubvoxels
<< ",\"bridge_half_width_q\":" << recipe.bridgeHalfWidthSubvoxels
<< ",\"bridge_thickness_q\":" << recipe.bridgeThicknessSubvoxels << ',';
floating_island_detail::WriteConstraints(out, recipe.routeConstraints);
out << ",\"content_hash\":\"" << floating_island_detail::HashText(recipe.contentHash)
<< "\"},\"plan\":{\"content_hash\":\""
<< floating_island_detail::HashText(plan.contentHash) << "\",\"islands\":[";
for (std::size_t i = 0; i < plan.islands.size(); ++i)
{
if (i) out << ',';
const auto& island = plan.islands[i];
out << "{\"stable_id\":\"" << floating_island_detail::HashText(island.stableId)
<< "\",\"ordinal\":" << island.ordinal << ",\"center_q\":["
<< island.centerSubvoxels[0] << ',' << island.centerSubvoxels[1] << ',' << island.centerSubvoxels[2]
<< "],\"radius_q\":" << island.radiusSubvoxels
<< ",\"top_half_q\":" << island.topHalfSubvoxels
<< ",\"top_z_q\":" << island.topZSubvoxels
<< ",\"bottom_z_q\":" << island.bottomZSubvoxels
<< ",\"taper_end_q16\":" << island.taperEndQ16
<< ",\"bounds_min_q\":[" << island.influenceMinimumSubvoxels[0] << ','
<< island.influenceMinimumSubvoxels[1] << ',' << island.influenceMinimumSubvoxels[2]
<< "],\"bounds_max_q\":[" << island.influenceMaximumSubvoxels[0] << ','
<< island.influenceMaximumSubvoxels[1] << ',' << island.influenceMaximumSubvoxels[2] << "]}";
}
out << "],\"cluster_edges\":[";
for (std::size_t i = 0; i < plan.clusterEdges.size(); ++i)
{
if (i) out << ',';
const auto& edge = plan.clusterEdges[i];
out << "{\"stable_id\":\"" << floating_island_detail::HashText(edge.stableId)
<< "\",\"from_island_id\":\"" << floating_island_detail::HashText(edge.fromIslandId)
<< "\",\"to_island_id\":\"" << floating_island_detail::HashText(edge.toIslandId)
<< "\",\"start_q\":[" << edge.startSubvoxels[0] << ',' << edge.startSubvoxels[1] << ','
<< edge.startSubvoxels[2] << "],\"end_q\":[" << edge.endSubvoxels[0] << ','
<< edge.endSubvoxels[1] << ',' << edge.endSubvoxels[2]
<< "],\"half_width_q\":" << edge.halfWidthSubvoxels
<< ",\"thickness_q\":" << edge.thicknessSubvoxels
<< ",\"bounds_min_q\":[" << edge.influenceMinimumSubvoxels[0] << ','
<< edge.influenceMinimumSubvoxels[1] << ',' << edge.influenceMinimumSubvoxels[2]
<< "],\"bounds_max_q\":[" << edge.influenceMaximumSubvoxels[0] << ','
<< edge.influenceMaximumSubvoxels[1] << ',' << edge.influenceMaximumSubvoxels[2]
<< "],\"sharp_tag_ready\":" << (edge.sharpTagReady ? "true" : "false") << '}';
}
out << "],\"route\":[";
for (std::size_t i = 0; i < plan.route.size(); ++i)
{
if (i) out << ',';
const auto& point = plan.route[i];
out << "{\"feature_id\":\"" << floating_island_detail::HashText(point.stableFeatureId)
<< "\",\"position_q\":[" << point.positionSubvoxels[0] << ','
<< point.positionSubvoxels[1] << ',' << point.positionSubvoxels[2]
<< "],\"radius_q\":" << point.radiusSubvoxels << '}';
}
out << "]}}";
return out.str();
}
std::optional<FloatingIslandManifestV1> PlanFloatingIslandCluster(
const FloatingIslandRecipeV1& inputRecipe, std::string& error)
{
FloatingIslandManifestV1 manifest;
manifest.recipe = inputRecipe;
auto& recipe = manifest.recipe;
if (recipe.routeConstraints.version == 0)
{
recipe.routeConstraints.version = 1;
recipe.routeConstraints.capsuleRadiusSubvoxels = 348;
recipe.routeConstraints.capsuleHeightSubvoxels = 1802;
recipe.routeConstraints.maximumGradeQ16 = 17560;
recipe.routeConstraints.sweepSpacingSubvoxels = 128;
recipe.routeConstraints.supportProbeRadiusSubvoxels = 230;
recipe.routeConstraints.supportSearchDepthSubvoxels = 512;
recipe.routeConstraints.minimumClearanceSubvoxels = 128;
recipe.routeConstraints.minimumSupportProbes = 3;
}
if (!floating_island_detail::ValidateRecipe(recipe, error)) return std::nullopt;
auto& islands = manifest.plan.islands;
islands.reserve(static_cast<std::size_t>(recipe.primaryIslandCount) + 2);
const std::uint32_t count = recipe.primaryIslandCount;
const std::uint32_t middle = count / 2;
std::vector<std::int64_t> tops(count, recipe.layoutCenterTopZSubvoxels);
std::vector<std::int64_t> radii(count);
for (std::uint32_t i = 0; i < count; ++i)
{
const std::uint64_t site = 0x100000000ull + i;
const std::uint32_t radiusRoll = floating_island_detail::UnitQ16(recipe.seed, site, 0x524144495553ull);
radii[i] = recipe.minimumRadiusSubvoxels
+ (recipe.maximumRadiusSubvoxels - recipe.minimumRadiusSubvoxels) * radiusRoll / 65535;
}
std::vector<std::array<std::int64_t, 2>> primaryXY(count);
const std::int64_t rowJitter = recipe.spacingSubvoxels / 10;
for (std::uint32_t i = 0; i < count; ++i)
{
const std::uint64_t site = 0x100000000ull + i;
if (recipe.geometryVersion < 2)
{
const std::int64_t yOffset = i == middle ? 0
: floating_island_detail::SignedRange(recipe.seed, site, 0x594a4954544552ull, rowJitter);
primaryXY[i] = {recipe.layoutOriginXSubvoxels
- static_cast<std::int64_t>(middle) * recipe.spacingSubvoxels
+ static_cast<std::int64_t>(i) * recipe.spacingSubvoxels,
recipe.layoutOriginYSubvoxels + yOffset};
}
else
{
const std::int64_t along = (static_cast<std::int64_t>(i)
- static_cast<std::int64_t>(middle)) * recipe.spacingSubvoxels * 7 / 10;
const std::int64_t side = (i % 2 == 0 ? 1 : -1) * recipe.spacingSubvoxels / 3;
const std::int64_t jitter = floating_island_detail::SignedRange(
recipe.seed, site, 0x594a4954544552ull, rowJitter / 2);
primaryXY[i] = {recipe.layoutOriginXSubvoxels + along,
recipe.layoutOriginYSubvoxels + side + jitter};
}
}
const double maxGrade = floating_island_detail::Q16ToDouble(recipe.routeConstraints.maximumGradeQ16);
const double levelFactor = floating_island_detail::Q16ToDouble(recipe.verticalJitterQ16) * 1.5;
const auto riseLimit = [&](std::uint32_t a, std::uint32_t b)
{
const double centerDistance = recipe.geometryVersion < 2
? static_cast<double>(recipe.spacingSubvoxels)
: std::hypot(static_cast<double>(primaryXY[b][0] - primaryXY[a][0]),
static_cast<double>(primaryXY[b][1] - primaryXY[a][1]));
const double gap = centerDistance
- 0.78 * (static_cast<double>(radii[a]) + static_cast<double>(radii[b]));
return std::max(0.0, gap * maxGrade * levelFactor);
};
for (std::uint32_t i = middle + 1; i < count; ++i)
{
const auto site = 0x100000000ull + i;
tops[i] = tops[i - 1] + floating_island_detail::SignedRange(recipe.seed, site,
0x4c4556454cull, static_cast<std::int64_t>(std::ceil(riseLimit(i - 1, i))));
}
for (std::int32_t i = static_cast<std::int32_t>(middle) - 1; i >= 0; --i)
{
const auto site = 0x100000000ull + static_cast<std::uint32_t>(i);
tops[static_cast<std::size_t>(i)] = tops[static_cast<std::size_t>(i + 1)]
+ floating_island_detail::SignedRange(recipe.seed, site, 0x4c4556454cull,
static_cast<std::int64_t>(std::ceil(riseLimit(static_cast<std::uint32_t>(i),
static_cast<std::uint32_t>(i + 1)))));
}
if (recipe.geometryVersion >= 2)
{
// Step the outer route islands around the middle island so the connected path still reads as separate floating masses.
// Decaler les iles externes autour de l'ile centrale pour distinguer les masses tout en gardant la route continue.
constexpr std::int64_t VerticalStagger = 3 * floating_island_detail::Q;
for (std::uint32_t i = 0; i < count; ++i)
{
if (i < middle) tops[i] -= VerticalStagger;
else if (i > middle) tops[i] += VerticalStagger;
}
}
for (std::uint32_t i = 0; i < count; ++i)
{
const std::uint64_t site = 0x100000000ull + i;
auto island = floating_island_detail::MakeIsland(recipe, i,
primaryXY[i][0], primaryXY[i][1], tops[i], site);
island.radiusSubvoxels = radii[i];
if(recipe.geometryVersion>=5&&i==recipe.anchorIslandOrdinal)
island.radiusSubvoxels=std::max(island.radiusSubvoxels,
recipe.anchorIslandMinimumRadiusSubvoxels);
island.topHalfSubvoxels = std::max<std::int64_t>(1, island.radiusSubvoxels / 5);
island.centerSubvoxels[2] = island.topZSubvoxels - island.topHalfSubvoxels;
island.bottomZSubvoxels = floating_island_detail::IslandBottom(recipe,
island.topZSubvoxels, island.radiusSubvoxels);
floating_island_detail::SetIslandBounds(recipe, island);
islands.push_back(island);
}
auto& edges = manifest.plan.clusterEdges;
for (std::uint32_t i = 0; i + 1 < count; ++i)
edges.push_back(floating_island_detail::MakeConnector(recipe, islands[i], islands[i + 1],
0x200000000ull + i));
// Two optional side sites make density a real recipe parameter. The primary chain is never
// probabilistic, so a sparse roll cannot remove the guaranteed entrance-to-exit path.
// Deux sites lateraux dependent de la densite; la chaine principale reste toujours garantie.
for (std::uint32_t branch = 0; branch < 2; ++branch)
{
const std::uint64_t site = 0x300000000ull + branch;
if (floating_island_detail::UnitQ16(recipe.seed, site, 0x53495445524f4c4cull)
>= static_cast<std::uint32_t>(recipe.islandDensityQ16)) continue;
const std::uint32_t hostIndex = recipe.geometryVersion < 2
? (branch == 0 ? 0 : count - 1)
: (branch == 0 ? middle : std::min(middle + 1, count - 1));
const auto& host = islands[hostIndex];
const std::int64_t xOffset = recipe.geometryVersion < 2 ? 0
: (branch == 0 ? -1 : 1) * recipe.spacingSubvoxels * 9 / 20;
const std::int64_t yOffset = branch == 0 ? -recipe.spacingSubvoxels : recipe.spacingSubvoxels;
const std::int64_t gap = recipe.spacingSubvoxels - static_cast<std::int64_t>(
std::llround(0.78 * static_cast<double>(host.radiusSubvoxels + recipe.minimumRadiusSubvoxels)));
const std::int64_t rise = static_cast<std::int64_t>(std::ceil(std::max(0.0,
static_cast<double>(gap) * maxGrade * levelFactor)));
const std::int64_t sideRise = recipe.geometryVersion < 2 ? rise
: std::min<std::int64_t>(24 * floating_island_detail::Q,
(recipe.layoutMaximumZSubvoxels - recipe.layoutMinimumZSubvoxels)
* recipe.verticalJitterQ16 / (8 * floating_island_detail::Q16));
std::int64_t topZ = host.topZSubvoxels
+ floating_island_detail::SignedRange(recipe.seed, site, 0x4252414e43484c56ull, sideRise);
const std::uint32_t radiusRoll = floating_island_detail::UnitQ16(recipe.seed, site, 0x524144495553ull);
const std::int64_t branchRadius = recipe.minimumRadiusSubvoxels
+ (recipe.maximumRadiusSubvoxels - recipe.minimumRadiusSubvoxels) * radiusRoll / 65535;
if (recipe.geometryVersion >= 2)
{
const std::int64_t maximumTop = recipe.layoutMaximumZSubvoxels
- recipe.boundarySealSubvoxels - branchRadius / 5;
const std::int64_t minimumTop = recipe.layoutMinimumZSubvoxels
+ recipe.boundarySealSubvoxels + floating_island_detail::IslandBottom(recipe, 0, branchRadius) * -1;
topZ = std::clamp(topZ, minimumTop, maximumTop);
}
auto island = floating_island_detail::MakeIsland(recipe,
recipe.primaryIslandCount + branch,
host.centerSubvoxels[0] + xOffset, host.centerSubvoxels[1] + yOffset, topZ, site);
island.radiusSubvoxels = branchRadius;
island.topHalfSubvoxels = std::max<std::int64_t>(1, branchRadius / 5);
island.centerSubvoxels[2] = topZ - island.topHalfSubvoxels;
island.bottomZSubvoxels = floating_island_detail::IslandBottom(recipe, topZ, branchRadius);
floating_island_detail::SetIslandBounds(recipe, island);
const std::uint64_t islandId = island.stableId;
islands.push_back(island);
edges.push_back(floating_island_detail::MakeConnector(recipe, host, islands.back(),
0x400000000ull + branch));
(void)islandId;
}
FloatingIslandFieldV1 planningField(recipe, manifest.plan);
if (!planningField.isValid())
{ error = "planned floating-island features did not pass the Core field contract"; return std::nullopt; }
auto& route = manifest.plan.route;
const std::int64_t capsuleHalf = recipe.routeConstraints.capsuleHeightSubvoxels / 2;
const std::int64_t routeRadius = 4 * floating_island_detail::Q;
std::uint32_t routeOrdinal = 0;
for (std::uint32_t segment = 0; segment + 1 < count; ++segment)
{
const auto& a = islands[segment];
const auto& b = islands[segment + 1];
const double dx = static_cast<double>(b.centerSubvoxels[0] - a.centerSubvoxels[0]);
const double dy = static_cast<double>(b.centerSubvoxels[1] - a.centerSubvoxels[1]);
const std::uint32_t pieces = static_cast<std::uint32_t>(std::max(1.0,
std::ceil(std::hypot(dx, dy) / (4.0 * floating_island_detail::Q))));
for (std::uint32_t piece = segment == 0 ? 0 : 1; piece <= pieces; ++piece)
{
const double t = static_cast<double>(piece) / pieces;
const std::int64_t x = floating_island_detail::RoundQ(
static_cast<double>(a.centerSubvoxels[0]) + dx * t);
const std::int64_t y = floating_island_detail::RoundQ(
static_cast<double>(a.centerSubvoxels[1]) + dy * t);
std::int64_t routeX=x,routeY=y;
if(recipe.geometryVersion>=5&&segment+2==count&&piece==pieces)
{
const auto& anchor=islands[recipe.anchorIslandOrdinal];
const std::uint32_t side=static_cast<std::uint32_t>(
floating_island_detail::UnitQ16(recipe.seed,anchor.stableId,
0x414e43484f524d50ull)&1u);
const double routeAngle=std::atan2(dy,dx);
const double angle=routeAngle+(side?1.0:-1.0)
*floating_island_detail::Pi/2.0;
const double mouthX=static_cast<double>(anchor.centerSubvoxels[0])
+static_cast<double>(recipe.anchorMouthOffsetSubvoxels)*std::cos(angle);
const double mouthY=static_cast<double>(anchor.centerSubvoxels[1])
+static_cast<double>(recipe.anchorMouthOffsetSubvoxels)*std::sin(angle);
routeX=floating_island_detail::RoundQ(mouthX);
routeY=floating_island_detail::RoundQ(mouthY);
}
const auto surface = floating_island_detail::FindCapsuleFootingTopZ(
recipe, manifest.plan, routeX, routeY);
if (!surface)
{ error = "island planner could not find a full capsule-width support surface along its connector chain"; return std::nullopt; }
WorldAssemblyRoutePointV1 point;
point.stableFeatureId = floating_island_detail::StableId(recipe.seed,
0x500000000ull + routeOrdinal, 0x524f555445ull);
point.positionSubvoxels = {routeX, routeY, *surface + capsuleHalf
+ recipe.routeConstraints.minimumClearanceSubvoxels
+ (recipe.geometryVersion >= 2 ? floating_island_detail::Q / 8 : 0)};
point.radiusSubvoxels = routeRadius;
route.push_back(point);
++routeOrdinal;
}
}
// Find the lowest route profile that stays above every sampled footing surface while obeying
// the grade limit. This raises valleys instead of allowing a short terrain bump to create a
// collision or a grade spike in the serialized walk line.
// Profil minimal au-dessus des appuis : les bosses locales ne percent pas la capsule.
const double maximumGrade = floating_island_detail::Q16ToDouble(
recipe.routeConstraints.maximumGradeQ16) * 0.95;
std::vector<std::int64_t> baseHeights;
baseHeights.reserve(route.size());
for (const auto& point : route) baseHeights.push_back(point.positionSubvoxels[2]);
for (std::size_t i = 0; i < route.size(); ++i)
{
double profile = static_cast<double>(baseHeights[i]);
for (std::size_t j = 0; j < route.size(); ++j)
{
const double dx = static_cast<double>(route[i].positionSubvoxels[0]
- route[j].positionSubvoxels[0]);
const double dy = static_cast<double>(route[i].positionSubvoxels[1]
- route[j].positionSubvoxels[1]);
profile = std::max(profile, static_cast<double>(baseHeights[j])
- maximumGrade * std::hypot(dx, dy));
}
route[i].positionSubvoxels[2] = floating_island_detail::RoundQ(profile);
}
if (!manifest.finalize(error)) return std::nullopt;
const std::string encoded = manifest.ToJson();
const auto roundTrip = FloatingIslandManifestV1::FromJson(encoded, error);
if (!roundTrip || roundTrip->ToJson() != encoded || roundTrip->contentHash != manifest.contentHash)
{ if (error.empty()) error = "floating-island plan did not canonical-round-trip"; return std::nullopt; }
return manifest;
}
FloatingIslandFieldV1::FloatingIslandFieldV1(const FloatingIslandRecipeV1& recipe,
const FloatingIslandPlanV1& plan) noexcept : recipe_(&recipe), plan_(&plan)
{}
bool FloatingIslandFieldV1::isValid() const noexcept
{
if (!recipe_ || !plan_ || recipe_->schemaVersion != 1
|| recipe_->unitsPerVoxel != floating_island_detail::Q
|| recipe_->primaryIslandCount < 3 || plan_->islands.size() < recipe_->primaryIslandCount
|| plan_->islands.size() > 4096 || plan_->clusterEdges.size() > 4096
|| recipe_->minimumRadiusSubvoxels <= 0 || recipe_->maximumRadiusSubvoxels < recipe_->minimumRadiusSubvoxels
|| recipe_->blendRadiusSubvoxels < 0 || recipe_->contourWarpSubvoxels < 0) return false;
return std::all_of(plan_->islands.begin(), plan_->islands.end(), [](const auto& island)
{
return island.stableId != 0 && island.radiusSubvoxels > 0
&& island.topZSubvoxels > island.bottomZSubvoxels;
});
}
std::int64_t FloatingIslandFieldV1::densitySubvoxels(std::int64_t x,
std::int64_t y, std::int64_t z) const noexcept
{
// The selected evaluator validates once; do not scan the plan twice per scoped sample.
// L'evaluateur choisi valide une seule fois; ne pas reparcourir le plan a chaque echantillon.
if (const auto* list = ScopedWorldAssemblyFeatureQueriesV1::find(recipe_, plan_, x, y, z))
return densitySubvoxelsForTile(x, y, z, *list);
if (!isValid() || !floating_island_detail::ValidScalar(x)
|| !floating_island_detail::ValidScalar(y) || !floating_island_detail::ValidScalar(z))
return floating_island_detail::MaxDensity;
return floating_island_detail::FieldDensity(*recipe_, *plan_, x, y, z);
}
std::vector<WorldAssemblyFeatureBoundsV1> FloatingIslandFieldV1::solidSupportBounds() const
{
std::vector<WorldAssemblyFeatureBoundsV1> features;
features.reserve(plan_->islands.size() + plan_->clusterEdges.size());
for (const auto& island : plan_->islands)
features.push_back({island.stableId, island.influenceMinimumSubvoxels,
island.influenceMaximumSubvoxels});
for (const auto& edge : plan_->clusterEdges)
features.push_back({edge.stableId, edge.influenceMinimumSubvoxels,
edge.influenceMaximumSubvoxels});
return features;
}
FloatingIslandTileFeatureListV1 FloatingIslandFieldV1::buildFeatureTileList(
const std::array<std::int64_t, 3>& tileMinimumSubvoxels,
const std::array<std::int64_t, 3>& tileMaximumSubvoxels) const
{
FloatingIslandTileFeatureListV1 result;
// The measured four-island plan is cheaper to scan; larger plans amortize group certificates.
// Le plan mesure de quatre iles coute moins en parcours complet; les grands plans amortissent les bornes.
result.candidateEvaluationUseful=plan_->islands.size()>4;
result.minimumSubvoxels = tileMinimumSubvoxels;
result.maximumSubvoxels = tileMaximumSubvoxels;
if (!isValid()) return result;
const auto features = solidSupportBounds();
result.featureIndices = BuildWorldAssemblyFeatureTileListV1(features,
tileMinimumSubvoxels, tileMaximumSubvoxels);
const std::size_t islandCount = plan_->islands.size();
result.groupBounds.resize(islandCount);
for (std::size_t islandIndex = 0; islandIndex < islandCount; ++islandIndex)
{
result.groupBounds[islandIndex].minimumSubvoxels = plan_->islands[islandIndex].influenceMinimumSubvoxels;
result.groupBounds[islandIndex].maximumSubvoxels = plan_->islands[islandIndex].influenceMaximumSubvoxels;
}
const auto findSourceIsland = [&](const FloatingIslandConnectorV1& edge)
{
return std::find_if(plan_->islands.begin(), plan_->islands.end(),
[&edge](const auto& island) { return island.stableId == edge.fromIslandId; });
};
for (const auto& edge : plan_->clusterEdges)
{
const auto source = findSourceIsland(edge);
if (source == plan_->islands.end())
{ result.groupBounds.clear(); result.featureIndices.clear(); return result; }
auto& group = result.groupBounds[static_cast<std::size_t>(source - plan_->islands.begin())];
for (std::size_t axis = 0; axis < 3; ++axis)
{
group.minimumSubvoxels[axis] = std::min(group.minimumSubvoxels[axis],
edge.influenceMinimumSubvoxels[axis]);
group.maximumSubvoxels[axis] = std::max(group.maximumSubvoxels[axis],
edge.influenceMaximumSubvoxels[axis]);
}
}
result.groupSearchOrder.resize(islandCount);
std::iota(result.groupSearchOrder.begin(), result.groupSearchOrder.end(), 0u);
const auto tileDistanceLowerBound = [&](
const std::array<std::int64_t, 3>& featureMinimum,
const std::array<std::int64_t, 3>& featureMaximum)
{
std::int64_t maximumGap = 0;
for (std::size_t axis = 0; axis < 3; ++axis)
{
if (featureMinimum[axis] > tileMaximumSubvoxels[axis])
maximumGap = std::max(maximumGap,
featureMinimum[axis] - tileMaximumSubvoxels[axis]);
else if (featureMaximum[axis] < tileMinimumSubvoxels[axis])
maximumGap = std::max(maximumGap,
tileMinimumSubvoxels[axis] - featureMaximum[axis]);
}
return maximumGap;
};
result.groupDistanceLowerBoundsSubvoxels.resize(islandCount);
for (std::size_t islandIndex = 0; islandIndex < islandCount; ++islandIndex)
{
const auto& island = plan_->islands[islandIndex];
const std::int64_t islandGap = tileDistanceLowerBound(
island.influenceMinimumSubvoxels, island.influenceMaximumSubvoxels);
double islandLowerBound = static_cast<double>(islandGap)
- floating_island_detail::IslandBlendMargin(*recipe_) - recipe_->roughnessSubvoxels;
if (recipe_->geometryVersion >= 2)
{
// Root lobes use a scaled ellipsoid distance, so their positive values can be
// substantially smaller than distance to the island AABB. Supported root radii
// guarantee minAxis/maxAxis >= 0.22; include their smooth-min budget before pruning.
// Les racines utilisent une distance d'ellipsoide mise a l'echelle; borner leur pente.
const double radius = static_cast<double>(island.radiusSubvoxels);
const double rootBlend = std::clamp(radius * 0.12,
2.0 * floating_island_detail::Q, 5.0 * floating_island_detail::Q);
const double rootSmoothBudget = static_cast<double>(
floating_island_detail::RootCount(*recipe_, island)) * rootBlend * 0.25;
const double rootLowerBound = 0.22 * static_cast<double>(islandGap)
- recipe_->roughnessSubvoxels - rootSmoothBudget;
islandLowerBound = std::min(islandLowerBound, rootLowerBound);
}
double groupLowerBound = islandLowerBound;
for (const auto& edge : plan_->clusterEdges)
{
if (edge.fromIslandId != island.stableId) continue;
const std::int64_t edgeGap = tileDistanceLowerBound(
edge.influenceMinimumSubvoxels, edge.influenceMaximumSubvoxels);
const double edgeLowerBound = static_cast<double>(edgeGap) - recipe_->roughnessSubvoxels;
groupLowerBound = std::min(groupLowerBound, edgeLowerBound)
- static_cast<double>(recipe_->blendRadiusSubvoxels) * 0.25;
}
// Floor with one subvoxel of slack so quantization cannot make a lower bound optimistic.
// Arrondir vers le bas avec une marge pour ne jamais surestimer la borne.
result.groupDistanceLowerBoundsSubvoxels[islandIndex] =
static_cast<std::int64_t>(std::floor(groupLowerBound)) - 1;
}
std::sort(result.groupSearchOrder.begin(), result.groupSearchOrder.end(),
[&result](std::uint32_t a, std::uint32_t b)
{
const auto distanceA = result.groupDistanceLowerBoundsSubvoxels[a];
const auto distanceB = result.groupDistanceLowerBoundsSubvoxels[b];
return distanceA == distanceB ? a < b : distanceA < distanceB;
});
// Store distance in search order so query samples use one sequential early-out walk.
// Ranger les distances dans le meme ordre pour arreter le parcours au premier echec.
std::vector<std::int64_t> orderedDistances;
orderedDistances.reserve(islandCount);
for (const std::uint32_t islandIndex : result.groupSearchOrder)
orderedDistances.push_back(result.groupDistanceLowerBoundsSubvoxels[islandIndex]);
result.groupDistanceLowerBoundsSubvoxels = std::move(orderedDistances);
std::vector<std::uint8_t> selectedGroups(islandCount, 0);
for (const std::uint32_t featureIndex : result.featureIndices)
{
if (featureIndex < islandCount) selectedGroups[featureIndex] = 1;
else
{
const std::size_t edgeIndex = static_cast<std::size_t>(featureIndex) - islandCount;
if (edgeIndex >= plan_->clusterEdges.size())
{ result.groupBounds.clear(); result.featureIndices.clear(); return result; }
const auto source = findSourceIsland(plan_->clusterEdges[edgeIndex]);
if (source == plan_->islands.end())
{ result.groupBounds.clear(); result.featureIndices.clear(); return result; }
selectedGroups[static_cast<std::size_t>(source - plan_->islands.begin())] = 1;
}
}
// A connector is composed into its source island's group. Keep that source and every
// outgoing connector in the tile list so the ordered smooth-min chain remains exact.
// Un connecteur fait partie de son ile source : garder toute la chaine de fondu exacte.
for (std::size_t islandIndex = 0; islandIndex < islandCount; ++islandIndex)
{
if (!selectedGroups[islandIndex]) continue;
auto insertSorted = [&](std::uint32_t value)
{
const auto insertion = std::lower_bound(result.featureIndices.begin(),
result.featureIndices.end(), value);
if (insertion == result.featureIndices.end() || *insertion != value)
result.featureIndices.insert(insertion, value);
};
insertSorted(static_cast<std::uint32_t>(islandIndex));
for (std::size_t edgeIndex = 0; edgeIndex < plan_->clusterEdges.size(); ++edgeIndex)
if (plan_->clusterEdges[edgeIndex].fromIslandId == plan_->islands[islandIndex].stableId)
insertSorted(static_cast<std::uint32_t>(islandCount + edgeIndex));
}
return result;
}
std::int64_t FloatingIslandFieldV1::densitySubvoxelsForTile(std::int64_t x,
std::int64_t y, std::int64_t z,
const FloatingIslandTileFeatureListV1& featureList) const noexcept
{
if (!isValid() || !floating_island_detail::ValidScalar(x)
|| !floating_island_detail::ValidScalar(y) || !floating_island_detail::ValidScalar(z))
return floating_island_detail::MaxDensity;
std::uint32_t previous = 0;
bool first = true;
const std::size_t featureCount = plan_->islands.size() + plan_->clusterEdges.size();
if (featureList.groupBounds.size() != plan_->islands.size())
return floating_island_detail::FieldDensity(*recipe_, *plan_, x, y, z);
for (const std::uint32_t index : featureList.featureIndices)
{
if (index >= featureCount || (!first && index <= previous))
return floating_island_detail::FieldDensity(*recipe_, *plan_, x, y, z);
first = false;
previous = index;
}
return floating_island_detail::FieldDensityForTile(*recipe_, *plan_, x, y, z, featureList);
}
FieldInterval FloatingIslandFieldV1::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 result;
if (!isValid() || minX > maxX || minY > maxY || minZ > maxZ
|| !floating_island_detail::ValidScalar(minX) || !floating_island_detail::ValidScalar(minY)
|| !floating_island_detail::ValidScalar(minZ) || !floating_island_detail::ValidScalar(maxX)
|| !floating_island_detail::ValidScalar(maxY) || !floating_island_detail::ValidScalar(maxZ)) return result;
const std::array<std::int64_t, 3> minimum{minX, minY, minZ};
const std::array<std::int64_t, 3> maximum{maxX, maxY, maxZ};
for (const auto& island : plan_->islands)
if (floating_island_detail::Intersects(minimum, maximum,
island.influenceMinimumSubvoxels, island.influenceMaximumSubvoxels)) return result;
for (const auto& edge : plan_->clusterEdges)
if (floating_island_detail::Intersects(minimum, maximum,
edge.influenceMinimumSubvoxels, edge.influenceMaximumSubvoxels)) return result;
// Root is void. A box outside every lobe/taper/blend/connector influence bound proves air.
// La racine est le vide : une boite hors des influences bornees prouve l'air.
result.known = true;
result.minimumDensitySubvoxels = 0;
result.maximumDensitySubvoxels = floating_island_detail::MaxDensity;
return result;
}
FloatingIslandInfluenceBounds FloatingIslandFieldV1::influenceBounds() const noexcept
{
FloatingIslandInfluenceBounds result;
if (!isValid()) return result;
result.finite = true;
result.minimumSubvoxels.fill(std::numeric_limits<std::int64_t>::max());
result.maximumSubvoxels.fill(std::numeric_limits<std::int64_t>::min());
const auto include = [&](const auto& feature)
{
for (std::size_t axis = 0; axis < 3; ++axis)
{
result.minimumSubvoxels[axis] = std::min(result.minimumSubvoxels[axis], feature.influenceMinimumSubvoxels[axis]);
result.maximumSubvoxels[axis] = std::max(result.maximumSubvoxels[axis], feature.influenceMaximumSubvoxels[axis]);
}
};
for (const auto& island : plan_->islands) include(island);
for (const auto& edge : plan_->clusterEdges) include(edge);
return result;
}
FieldDetailEnvelope FloatingIslandFieldV1::detailEnvelope() const noexcept
{
FieldDetailEnvelope result;
if (!isValid()) return result;
result.maximumAbsoluteDisplacementSubvoxels = recipe_->contourWarpSubvoxels
+ recipe_->roughnessSubvoxels + floating_island_detail::IslandBlendMargin(*recipe_);
result.minimumFeatureSizeSubvoxels = std::numeric_limits<std::int64_t>::max();
for (const auto& island : plan_->islands)
result.minimumFeatureSizeSubvoxels = std::min(result.minimumFeatureSizeSubvoxels,
island.radiusSubvoxels * 2);
return result;
}
FieldBlockContract FloatingIslandFieldV1::contract() const noexcept
{
FieldBlockContract result;
result.readsChannels = ChannelBit(FieldChannel::island);
result.writesChannels = ChannelBit(FieldChannel::rock) | ChannelBit(FieldChannel::route)
| ChannelBit(FieldChannel::surfaceTags);
result.evaluatorVersion = 1;
return result;
}
FloatingIslandBoundsAuditResult AuditFloatingIslandBounds(const FloatingIslandFieldV1& field,
std::uint32_t boxCount, std::uint64_t seed)
{
FloatingIslandBoundsAuditResult result;
if (!field.isValid() || !field.recipe() || !field.plan()) return result;
const auto& recipe = *field.recipe();
const auto& plan = *field.plan();
const auto bounds = field.influenceBounds();
if (!bounds.finite) return result;
for (const auto& island : plan.islands) ++result.islandBoundsTested;
for (const auto& edge : plan.clusterEdges) ++result.connectorBoundsTested;
floating_island_detail::RandomStream random{seed};
const auto randomBetween = [&](std::int64_t low, std::int64_t high)
{
if (high <= low) return low;
return low + static_cast<std::int64_t>(random.next() % static_cast<std::uint64_t>(high - low + 1));
};
constexpr std::array<std::int32_t, 3> samples{0, 1, 2};
for (std::uint32_t boxIndex = 0; boxIndex < boxCount; ++boxIndex)
{
std::array<std::int64_t, 3> minimum{}, maximum{};
for (std::size_t axis = 0; axis < 3; ++axis)
{
const std::int64_t pad = 48 * floating_island_detail::Q;
const std::int64_t low = bounds.minimumSubvoxels[axis] - pad;
const std::int64_t high = bounds.maximumSubvoxels[axis] + pad;
const std::int64_t extent = std::max<std::int64_t>(1, randomBetween(1,
20 * floating_island_detail::Q));
const std::int64_t start = randomBetween(low, high);
minimum[axis] = start;
maximum[axis] = std::min(high + extent, start + extent);
}
const auto interval = field.interval(minimum[0], minimum[1], minimum[2],
maximum[0], maximum[1], maximum[2]);
++result.boxesTested;
if (!interval.known) ++result.unknownBounds;
for (const auto sx : samples)
for (const auto sy : samples)
for (const auto sz : samples)
{
const std::array<std::int64_t, 3> point{
minimum[0] + (maximum[0] - minimum[0]) * sx / 2,
minimum[1] + (maximum[1] - minimum[1]) * sy / 2,
minimum[2] + (maximum[2] - minimum[2]) * sz / 2};
const std::int64_t density = field.densitySubvoxels(point[0], point[1], point[2]);
++result.pointSamples;
if (interval.known && (density < interval.minimumDensitySubvoxels
|| density > interval.maximumDensitySubvoxels)) ++result.missedBounds;
if (density < 0)
{
bool enclosed = false;
for (const auto& island : plan.islands)
enclosed = enclosed || floating_island_detail::Contains(island.influenceMinimumSubvoxels,
island.influenceMaximumSubvoxels, point);
for (const auto& edge : plan.clusterEdges)
enclosed = enclosed || floating_island_detail::Contains(edge.influenceMinimumSubvoxels,
edge.influenceMaximumSubvoxels, point);
if (!enclosed) ++result.missedBounds;
}
}
}
// Probe the void beside each primary connector. These boxes should be certifiably air.
for (std::size_t i = 0; i < plan.clusterEdges.size(); ++i)
{
const auto& edge = plan.clusterEdges[i];
const double dx = static_cast<double>(edge.endSubvoxels[0] - edge.startSubvoxels[0]);
const double dy = static_cast<double>(edge.endSubvoxels[1] - edge.startSubvoxels[1]);
const double length = std::hypot(dx, dy);
if (!(length > 0.0)) continue;
const double offset = static_cast<double>(recipe.maximumRadiusSubvoxels
+ recipe.contourWarpSubvoxels + 4 * recipe.blendRadiusSubvoxels
+ 3 * floating_island_detail::Q);
const std::int64_t x = floating_island_detail::RoundQ(
(edge.startSubvoxels[0] + edge.endSubvoxels[0]) * 0.5 - dy / length * offset);
const std::int64_t y = floating_island_detail::RoundQ(
(edge.startSubvoxels[1] + edge.endSubvoxels[1]) * 0.5 + dx / length * offset);
const std::int64_t z = (edge.startSubvoxels[2] + edge.endSubvoxels[2]) / 2;
constexpr std::int64_t half = floating_island_detail::Q / 2;
const auto interval = field.interval(x - half, y - half, z - half,
x + half, y + half, z + half);
++result.boxesTested;
if (!interval.known) { ++result.unknownBounds; continue; }
if (interval.minimumDensitySubvoxels < 0) ++result.missedBounds;
else ++result.voidBoxesProvedAir;
for (const auto sx : samples)
for (const auto sy : samples)
for (const auto sz : samples)
{
const std::int64_t density = field.densitySubvoxels(
x - half + half * sx, y - half + half * sy, z - half + half * sz);
++result.pointSamples; ++result.voidPointSamples;
if (density < 0 || density < interval.minimumDensitySubvoxels
|| density > interval.maximumDensitySubvoxels) ++result.missedBounds;
}
}
// Negative control: deliberately omit the lobe-warp and blend reach from an island box.
// Controles negatifs : une fausse borne oublie le lobe et le fondu, puis doit etre rejetee.
for (const auto& island : plan.islands)
{
const std::int64_t badReach = island.radiusSubvoxels + recipe.roughnessSubvoxels;
const std::array<std::int64_t, 3> badMin{island.centerSubvoxels[0] - badReach,
island.centerSubvoxels[1] - badReach, island.bottomZSubvoxels - recipe.roughnessSubvoxels};
const std::array<std::int64_t, 3> badMax{island.centerSubvoxels[0] + badReach,
island.centerSubvoxels[1] + badReach, island.topZSubvoxels + recipe.roughnessSubvoxels};
const double radial = static_cast<double>(island.radiusSubvoxels)
+ static_cast<double>(recipe.contourWarpSubvoxels) * 0.72;
const double z = static_cast<double>(island.topZSubvoxels)
- static_cast<double>(island.topHalfSubvoxels) * 0.95;
for (int angleIndex = 0; angleIndex < 512 && !result.negativeControlDetected; ++angleIndex)
{
const double angle = floating_island_detail::Pi * 2.0 * angleIndex / 512.0;
const std::array<std::int64_t, 3> point{
floating_island_detail::RoundQ(island.centerSubvoxels[0] + radial * std::cos(angle)),
floating_island_detail::RoundQ(island.centerSubvoxels[1] + radial * std::sin(angle)),
floating_island_detail::RoundQ(z)};
if (floating_island_detail::Contains(badMin, badMax, point)) continue;
bool hiddenByAnotherFeatureBound = false;
for (const auto& other : plan.islands)
if (other.stableId != island.stableId
&& floating_island_detail::Contains(other.influenceMinimumSubvoxels,
other.influenceMaximumSubvoxels, point))
{ hiddenByAnotherFeatureBound = true; break; }
if (!hiddenByAnotherFeatureBound)
for (const auto& edge : plan.clusterEdges)
if (floating_island_detail::Contains(edge.influenceMinimumSubvoxels,
edge.influenceMaximumSubvoxels, point))
{ hiddenByAnotherFeatureBound = true; break; }
if (hiddenByAnotherFeatureBound) continue;
if (field.densitySubvoxels(point[0], point[1], point[2]) < 0)
{
const auto wrongInterval = floating_island_detail::Contains(badMin, badMax, point)
? FieldInterval{} : FieldInterval{true, 0, floating_island_detail::MaxDensity};
result.negativeControlDetected = wrongInterval.known
&& wrongInterval.minimumDensitySubvoxels >= 0;
}
}
}
result.passed = result.missedBounds == 0 && result.negativeControlDetected
&& result.voidBoxesProvedAir > 0;
return result;
}
FloatingIslandRouteAuditResult AuditFloatingIslandRoute(const FloatingIslandFieldV1& field,
std::int64_t routeOffsetSubvoxels)
{
FloatingIslandRouteAuditResult result;
if (!field.isValid() || !field.recipe() || !field.plan() || field.plan()->route.size() < 2) return result;
const auto& recipe = *field.recipe();
const auto& route = field.plan()->route;
const auto& constraints = recipe.routeConstraints;
const double maxGrade = floating_island_detail::Q16ToDouble(constraints.maximumGradeQ16);
const double radius = static_cast<double>(constraints.capsuleRadiusSubvoxels);
const double height = static_cast<double>(constraints.capsuleHeightSubvoxels);
const double halfHeight = height * 0.5;
const double cylinderHalf = std::max(0.0, halfHeight - radius);
const double spacing = static_cast<double>(constraints.sweepSpacingSubvoxels);
double minimumClearance = std::numeric_limits<double>::infinity();
const std::array<std::array<double, 2>, 17> disk = {{
{0.0, 0.0}, {0.6, 0.0}, {-0.6, 0.0}, {0.0, 0.6}, {0.0, -0.6},
{0.424264, 0.424264}, {-0.424264, 0.424264}, {0.424264, -0.424264}, {-0.424264, -0.424264},
{0.95, 0.0}, {-0.95, 0.0}, {0.0, 0.95}, {0.0, -0.95},
{0.671751, 0.671751}, {-0.671751, 0.671751}, {0.671751, -0.671751}, {-0.671751, -0.671751}}};
for (std::size_t segment = 0; segment + 1 < route.size(); ++segment)
{
++result.segmentsTested;
const auto& a = route[segment].positionSubvoxels;
const auto& b = route[segment + 1].positionSubvoxels;
const double dx = static_cast<double>(b[0] - a[0]);
const double dy = static_cast<double>(b[1] - a[1]);
const double dz = static_cast<double>(b[2] - a[2]);
const double horizontal = std::hypot(dx, dy);
if (!(horizontal > 0.0) || std::abs(dz) / horizontal > maxGrade)
{
if (result.firstGradeFailureSegment == ~std::uint64_t{0})
result.firstGradeFailureSegment = segment;
++result.gradeFailures;
}
const double length = std::sqrt(dx * dx + dy * dy + dz * dz);
const std::uint64_t centers = std::max<std::uint64_t>(1,
static_cast<std::uint64_t>(std::ceil(length / std::max(spacing, 1.0))));
for (std::uint64_t center = 0; center <= centers; ++center)
{
const double t = static_cast<double>(center) / centers;
const double cx = static_cast<double>(a[0]) + dx * t;
const double cy = static_cast<double>(a[1]) + dy * t;
const double cz = static_cast<double>(a[2]) + dz * t + routeOffsetSubvoxels;
++result.centerSamples;
const std::uint32_t verticalSteps = static_cast<std::uint32_t>(
std::max(1.0, std::ceil(height / std::max(spacing, 1.0))));
for (std::uint32_t vz = 0; vz <= verticalSteps; ++vz)
{
const double offsetZ = -halfHeight + height * vz / verticalSteps;
const double capDistance = std::max(0.0, std::abs(offsetZ) - cylinderHalf);
const double sectionRadius = std::sqrt(std::max(0.0, radius * radius - capDistance * capDistance));
for (const auto& sample : disk)
{
const double sx = cx + sample[0] * sectionRadius;
const double sy = cy + sample[1] * sectionRadius;
const std::int64_t density = field.densitySubvoxels(
floating_island_detail::RoundQ(sx), floating_island_detail::RoundQ(sy),
floating_island_detail::RoundQ(cz + offsetZ));
++result.capsuleFieldSamples;
minimumClearance = std::min(minimumClearance, static_cast<double>(density));
if (density < -1)
{
if (result.firstBlockedSegment == ~std::uint64_t{0})
{
result.firstBlockedSegment = segment;
result.firstBlockedPositionSubvoxels = {
floating_island_detail::RoundQ(sx), floating_island_detail::RoundQ(sy),
floating_island_detail::RoundQ(cz + offsetZ)};
}
++result.blockedCapsuleSamples;
}
}
}
const double foot = cz - halfHeight;
const double probeRadius = static_cast<double>(constraints.supportProbeRadiusSubvoxels);
const std::array<std::array<double, 2>, 5> probes{{
{0.0, 0.0}, {probeRadius, 0.0}, {-probeRadius, 0.0},
{0.0, probeRadius}, {0.0, -probeRadius}}};
std::uint32_t supported = 0;
for (const auto& probe : probes)
{
bool foundSupport = false;
for (double depth = static_cast<double>(constraints.minimumClearanceSubvoxels)
+ 0.5 * floating_island_detail::Q;
depth <= static_cast<double>(constraints.minimumClearanceSubvoxels
+ constraints.supportSearchDepthSubvoxels) + 1.0;
depth += 0.5 * floating_island_detail::Q)
{
const std::int64_t density = field.densitySubvoxels(
floating_island_detail::RoundQ(cx + probe[0]),
floating_island_detail::RoundQ(cy + probe[1]),
floating_island_detail::RoundQ(foot - depth));
++result.supportProbeSamples;
if (density < 0) { foundSupport = true; break; }
}
if (foundSupport) ++supported;
}
if (supported < constraints.minimumSupportProbes)
{
if (result.firstUnsupportedSegment == ~std::uint64_t{0})
{
result.firstUnsupportedSegment = segment;
result.firstUnsupportedCenterSubvoxels = {
floating_island_detail::RoundQ(cx), floating_island_detail::RoundQ(cy),
floating_island_detail::RoundQ(cz)};
}
++result.unsupportedCenterSamples;
}
}
}
result.minimumClearanceSubvoxels = std::isfinite(minimumClearance)
? floating_island_detail::RoundQ(minimumClearance) : 0;
result.passed = result.segmentsTested > 0 && result.centerSamples > 0
&& result.blockedCapsuleSamples == 0 && result.unsupportedCenterSamples == 0
&& result.gradeFailures == 0;
return result;
}
FloatingIslandThicknessAuditResult AuditFloatingIslandThickness(
const FloatingIslandPlanV1& plan, double minimumDepthToDiameterRatio)
{
FloatingIslandThicknessAuditResult result;
result.requiredDepthToDiameterRatio = minimumDepthToDiameterRatio;
if (plan.islands.empty() || !std::isfinite(minimumDepthToDiameterRatio)
|| minimumDepthToDiameterRatio <= 0.0) return result;
result.minimumDepthToDiameterRatio = std::numeric_limits<double>::infinity();
for (const auto& island : plan.islands)
{
++result.islandsTested;
if (island.radiusSubvoxels <= 0 || island.topZSubvoxels <= island.bottomZSubvoxels)
{
++result.islandsBelowMinimum;
result.minimumDepthToDiameterRatio = 0.0;
result.minimumIslandStableId = island.stableId;
continue;
}
const double ratio = static_cast<double>(island.topZSubvoxels - island.bottomZSubvoxels)
/ (2.0 * static_cast<double>(island.radiusSubvoxels));
if (ratio < result.minimumDepthToDiameterRatio)
{
result.minimumDepthToDiameterRatio = ratio;
result.minimumIslandStableId = island.stableId;
}
if (ratio + 1.0e-12 < minimumDepthToDiameterRatio) ++result.islandsBelowMinimum;
}
if (!std::isfinite(result.minimumDepthToDiameterRatio)) result.minimumDepthToDiameterRatio = 0.0;
result.passed = result.islandsTested == plan.islands.size()
&& result.islandsBelowMinimum == 0;
return result;
}
}