#pragma once #include "VoxelForgeCore/ThreadLocalStorage.h" #include "Json.h" #include "VoxelForgeCore/VoxelForgeCore.h" #include #include #include #include #include #include #include #include #include #include #include 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(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(seed) << 32) ^ site ^ salt); } inline std::uint32_t UnitQ16(std::uint32_t seed, std::uint64_t site, std::uint64_t salt) { return static_cast(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(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(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(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::max()) return false; output = static_cast(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& 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(value) / static_cast(Q16); } inline std::int64_t RoundQ(double value) { if (!std::isfinite(value)) return value < 0.0 ? -MaxDensity : MaxDensity; return static_cast(std::llround(std::clamp(value, -static_cast(MaxDensity), static_cast(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(value.capsuleRadiusSubvoxels), 8); HashWord(hash, static_cast(value.capsuleHeightSubvoxels), 8); HashWord(hash, static_cast(value.maximumGradeQ16), 8); HashWord(hash, static_cast(value.sweepSpacingSubvoxels), 8); HashWord(hash, static_cast(value.supportProbeRadiusSubvoxels), 8); HashWord(hash, static_cast(value.supportSearchDepthSubvoxels), 8); HashWord(hash, static_cast(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(recipe.layoutOriginXSubvoxels), 8); HashWord(hash, static_cast(recipe.layoutOriginYSubvoxels), 8); HashWord(hash, static_cast(recipe.layoutCenterTopZSubvoxels), 8); HashWord(hash, static_cast(recipe.layoutMinimumZSubvoxels), 8); HashWord(hash, static_cast(recipe.layoutMaximumZSubvoxels), 8); if(recipe.geometryVersion>=4) HashWord(hash,static_cast(recipe.layoutChunkWidthSubvoxels),8); HashWord(hash, recipe.primaryIslandCount, 4); HashWord(hash, static_cast(recipe.islandDensityQ16), 8); HashWord(hash, static_cast(recipe.spacingSubvoxels), 8); HashWord(hash, static_cast(recipe.minimumRadiusSubvoxels), 8); HashWord(hash, static_cast(recipe.maximumRadiusSubvoxels), 8); HashWord(hash, static_cast(recipe.thicknessRatioQ16), 8); HashWord(hash, static_cast(recipe.verticalJitterQ16), 8); HashWord(hash, static_cast(recipe.topFlattenQ16), 8); HashWord(hash, static_cast(recipe.taperEndMinimumQ16), 8); HashWord(hash, static_cast(recipe.taperEndMaximumQ16), 8); if (recipe.geometryVersion >= 2) { HashWord(hash, recipe.geometryVersion, 4); HashWord(hash, static_cast(recipe.crustDepthBonusQ16), 8); HashWord(hash, static_cast(recipe.rootDepthRatioQ16), 8); } if (recipe.geometryVersion >= 5) { HashWord(hash, recipe.anchorIslandOrdinal, 4); HashWord(hash, static_cast(recipe.anchorPlacementRadiusSubvoxels), 8); HashWord(hash, static_cast(recipe.anchorRootRadiusSubvoxels), 8); HashWord(hash, static_cast(recipe.anchorIslandMinimumRadiusSubvoxels), 8); HashWord(hash, static_cast(recipe.anchorRootBottomZSubvoxels), 8); HashWord(hash, static_cast(recipe.anchorMouthOffsetSubvoxels), 8); } HashWord(hash, static_cast(recipe.blendRadiusSubvoxels), 8); HashWord(hash, static_cast(recipe.contourWarpSubvoxels), 8); HashWord(hash, static_cast(recipe.roughnessSubvoxels), 8); HashWord(hash, static_cast(recipe.boundarySealSubvoxels), 8); HashWord(hash, static_cast(recipe.bridgeHalfWidthSubvoxels), 8); HashWord(hash, static_cast(recipe.bridgeThicknessSubvoxels), 8); if(recipe.terrainFollowingRoute)HashWord(hash,1,1); 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(value), 8); HashWord(hash, static_cast(island.radiusSubvoxels), 8); HashWord(hash, static_cast(island.topHalfSubvoxels), 8); HashWord(hash, static_cast(island.topZSubvoxels), 8); HashWord(hash, static_cast(island.bottomZSubvoxels), 8); HashWord(hash, static_cast(island.taperEndQ16), 8); for (auto value : island.influenceMinimumSubvoxels) HashWord(hash, static_cast(value), 8); for (auto value : island.influenceMaximumSubvoxels) HashWord(hash, static_cast(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(value), 8); for (auto value : edge.endSubvoxels) HashWord(hash, static_cast(value), 8); HashWord(hash, static_cast(edge.halfWidthSubvoxels), 8); HashWord(hash, static_cast(edge.thicknessSubvoxels), 8); for (auto value : edge.influenceMinimumSubvoxels) HashWord(hash, static_cast(value), 8); for (auto value : edge.influenceMaximumSubvoxels) HashWord(hash, static_cast(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(value), 8); HashWord(hash, static_cast(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 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(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(recipe.layoutChunkWidthSubvoxels)*recipe.coreVoxelSizeMicrons < 250000.0L*Q || static_cast(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& minimum, const std::array& maximum, const std::array& 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& minimumA, const std::array& maximumA, const std::array& minimumB, const std::array& 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(Q); const double seed = static_cast(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(recipe.contourWarpSubvoxels) * (0.50 * a + 0.30 * b + 0.20 * c); } inline double LobePhase(const FloatingIslandRecipeV1& recipe, const FloatingIslandAnchorV1& island) { return static_cast(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(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(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(Roll(recipe.seed, featureId, 0x524f5547ull) & 0xffffffffull) * (2.0 * Pi / 4294967296.0); const double xv = x / static_cast(Q); const double yv = y / static_cast(Q); const double zv = z / static_cast(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(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(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(island.radiusSubvoxels) + LobeOffset(recipe, island, dx, dy, static_cast(island.topZSubvoxels))); const double edge = std::clamp(radial / effectiveRadius, 0.0, 1.0); const double drop = (1.0 - Q16ToDouble(recipe.topFlattenQ16)) * static_cast(island.topHalfSubvoxels) * 2.0 * edge * edge; return static_cast(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; 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> Cache; auto& cache=Cache.Get(); const std::int64_t SampleX=static_cast(x); const std::int64_t SampleY=static_cast(y); std::uint64_t IndexKey=island.stableId ^(static_cast(SampleX)*0x9e3779b97f4a7c15ull) ^(static_cast(SampleY)*0xc2b2ae3d27d4eb4full) ^(static_cast(recipe.seed)<<23) ^recipe.contentHash^std::rotl(plan.contentHash,17) ^static_cast(reinterpret_cast(&recipe)) ^std::rotl(static_cast(reinterpret_cast(&plan)),31); IndexKey^=IndexKey>>33; IndexKey*=0xff51afd7ed558ccdull; IndexKey^=IndexKey>>33; const std::size_t Base=static_cast(IndexKey&(SetCount-1))*Ways; FIslandColumnCacheEntry* Replacement=&cache[Base+((IndexKey>>11)&(Ways-1))]; for(std::size_t Way=0;Way(island.centerSubvoxels[0]); Entry.DeltaY=y-static_cast(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(island.radiusSubvoxels) +LobeOffsetWithPhase(recipe,Entry.Angle,static_cast(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(island.topHalfSubvoxels)*2.0*Edge*Edge; Entry.TopSurface=static_cast(island.topZSubvoxels)-Drop; return Entry; }; FIslandColumnCacheEntry Entry{}; if(auto* shared=FSharedIslandColumnCache::threadActive()) { const VoxelForgeCoreShared::FColumnCacheKey key{ static_cast(reinterpret_cast(&recipe)), recipe.contentHash, static_cast(reinterpret_cast(&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(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 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> Cache; auto& cache=Cache.Get(); const std::uint64_t IndexKey=island.stableId^(island.stableId>>33) ^(static_cast(recipe.seed)<<17) ^(static_cast(static_cast(rootIndex))*0x9e3779b97f4a7c15ull); FRootLobeCacheEntry& Entry=cache[static_cast(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, 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(rootIndex); const std::uint64_t roll = Roll(recipe.seed, island.stableId, salt); const double radius = static_cast(island.radiusSubvoxels); const double rootDepth = radius * Q16ToDouble(recipe.rootDepthRatioQ16); const double crustBottom = static_cast(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(island.centerSubvoxels[0]) + (centralButtress ? 0.0 : pattern[static_cast(rootIndex)][0] * orbit); const double rootY = static_cast(island.centerSubvoxels[1]) + (centralButtress ? 0.0 : pattern[static_cast(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 FRootLobeParameters RootLobeParametersForBounds( const FloatingIslandRecipeV1& recipe,const FloatingIslandAnchorV1& island, std::int32_t rootIndex) { constexpr std::array,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(rootIndex); const std::uint64_t roll=Roll(recipe.seed,island.stableId,salt); const double radius=static_cast(island.radiusSubvoxels); const double rootDepth=radius*Q16ToDouble(recipe.rootDepthRatioQ16); const double crustBottom=static_cast(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(island.centerSubvoxels[0]) +(centralButtress?0.0:pattern[static_cast(rootIndex)][0]*orbit); const double rootY=static_cast(island.centerSubvoxels[1]) +(centralButtress?0.0:pattern[static_cast(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))); return {rootX,rootY,centerZ,rx*rx,ry*ry,verticalRadius*verticalRadius, std::min({rx,ry,verticalRadius})}; } 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(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(island.centerSubvoxels[0]); const double dy = y - static_cast(island.centerSubvoxels[1]); const double radial = std::hypot(dx, dy); const double topZ = static_cast(island.topZSubvoxels); const double bottomZ = static_cast(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(island.radiusSubvoxels) + LobeOffset(recipe, island, dx, dy, z)); const double envelope = effectiveRadius * taper; const double top = IslandTopSurface(recipe, island, static_cast(island.centerSubvoxels[0]) + dx, static_cast(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(island.topZSubvoxels); const double radius = static_cast(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(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(IslandCrustBottom(recipe, island)); const double rootBottom = static_cast(recipe.anchorRootBottomZSubvoxels); const double depth = std::max(1.0, rootTop - rootBottom); const double blend = std::clamp(static_cast(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(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(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(edge.startSubvoxels[0]); const double ay = static_cast(edge.startSubvoxels[1]); const double az = static_cast(edge.startSubvoxels[2]); const double dx = static_cast(edge.endSubvoxels[0]) - ax; const double dy = static_cast(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(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(edge.endSubvoxels[2]) - az) * t; const std::uint64_t westernEdgeId = StableId(recipe.seed, 0x200000000ull, 0x425249444745ull); const bool westernSupportConnector=edge.stableId==westernEdgeId; if(recipe.geometryVersion>=2&&WorldAssemblyEvaluatorVersion>=183 &&westernSupportConnector) { const double phase=static_cast(Roll(recipe.seed,edge.stableId,0x5749445448ull) &0xffffffffull)*(2.0*Pi/4294967296.0); const double localHalfWidth=std::max( static_cast(recipe.routeConstraints.capsuleRadiusSubvoxels+Q), static_cast(edge.halfWidthSubvoxels) +0.48*static_cast(Q)*std::sin(6.0*Pi*t+phase)); const double localThickness=static_cast(edge.thicknessSubvoxels); const double rounding=std::min({1.5*static_cast(Q), localHalfWidth*0.6,localThickness*0.45}); const double roundedX=side-(localHalfWidth-rounding); const double roundedY=std::abs(along-0.5*length)-(0.5*length-rounding); const double roundedZ=std::abs(z-(deck-0.5*localThickness)) -(0.5*localThickness-rounding); const double roundedOutside=std::sqrt( std::pow(std::max(roundedX,0.0),2) +std::pow(std::max(roundedY,0.0),2) +std::pow(std::max(roundedZ,0.0),2)); const double roundedInside=std::min(std::max(roundedX, std::max(roundedY,roundedZ)),0.0); if(topSurface)*topSurface=z-deck>=std::max(horizontal, deck-localThickness-z); return roundedOutside+roundedInside-rounding; } const double arch = recipe.geometryVersion >= 2 && WorldAssemblyEvaluatorVersion < 172 ? 2.0 * static_cast(edge.thicknessSubvoxels) * (4.0 * t * (1.0 - t)) : 0.0; const double upper = z - deck; const double lower = deck - static_cast(edge.thicknessSubvoxels) - arch - z; if (topSurface) *topSurface = upper >= std::max(horizontal, lower); return std::max({horizontal, upper, lower}); } struct FScalarRange { double minimum=0.0,maximum=0.0; }; struct FDensityRange { double minimum=0.0,maximum=0.0; }; inline double DistanceToRange(double minimum,double maximum,double value) { if(valuemaximum)return value-maximum; return 0.0; } inline double MaximumDistanceToRange(double minimum,double maximum,double value) { return std::max(std::abs(minimum-value),std::abs(maximum-value)); } inline FScalarRange AffineRange(double xMinimum,double xMaximum,double xOffset, double xScale,double yMinimum,double yMaximum,double yOffset,double yScale) { const double xa=(xMinimum-xOffset)*xScale,xb=(xMaximum-xOffset)*xScale; const double ya=(yMinimum-yOffset)*yScale,yb=(yMaximum-yOffset)*yScale; return {std::min(xa,xb)+std::min(ya,yb), std::max(xa,xb)+std::max(ya,yb)}; } inline FScalarRange AbsoluteRange(double minimum,double maximum) { const double minimumAbsolute=minimum<=0.0&&maximum>=0.0 ?0.0:std::min(std::abs(minimum),std::abs(maximum)); return {minimumAbsolute,std::max(std::abs(minimum),std::abs(maximum))}; } inline double RectangleSdf(double x,double y) { return std::hypot(std::max(x,0.0),std::max(y,0.0)) +std::min(std::max(x,y),0.0); } inline double RoundedBoxSdf(double x,double y,double z) { return std::sqrt(std::max(x,0.0)*std::max(x,0.0) +std::max(y,0.0)*std::max(y,0.0) +std::max(z,0.0)*std::max(z,0.0)) +std::min(std::max(x,std::max(y,z)),0.0); } inline FDensityRange RootLobeRange(const FloatingIslandRecipeV1& recipe, const FloatingIslandAnchorV1& island,std::int32_t rootIndex, const std::array& minimum,const std::array& maximum) { const FRootLobeParameters root=RootLobeParametersForBounds(recipe,island,rootIndex); const double dxMinimum=DistanceToRange(minimum[0],maximum[0],root.RootX); const double dyMinimum=DistanceToRange(minimum[1],maximum[1],root.RootY); const double dzMinimum=DistanceToRange(minimum[2],maximum[2],root.CenterZ); const double dxMaximum=MaximumDistanceToRange(minimum[0],maximum[0],root.RootX); const double dyMaximum=MaximumDistanceToRange(minimum[1],maximum[1],root.RootY); const double dzMaximum=MaximumDistanceToRange(minimum[2],maximum[2],root.CenterZ); const double qMinimum=dxMinimum*dxMinimum/root.RadiusXSquared +dyMinimum*dyMinimum/root.RadiusYSquared +dzMinimum*dzMinimum/root.VerticalRadiusSquared; const double qMaximum=dxMaximum*dxMaximum/root.RadiusXSquared +dyMaximum*dyMaximum/root.RadiusYSquared +dzMaximum*dzMaximum/root.VerticalRadiusSquared; return {(std::sqrt(std::max(qMinimum,0.0))-1.0)*root.MinimumRadius, (std::sqrt(std::max(qMaximum,0.0))-1.0)*root.MinimumRadius}; } inline FDensityRange IslandDensityRange(const FloatingIslandRecipeV1& recipe, const FloatingIslandAnchorV1& island,const std::array& minimum, const std::array& maximum) { const double radius=static_cast(island.radiusSubvoxels); const double contour=static_cast(recipe.contourWarpSubvoxels); const double radialMinimum=std::hypot( DistanceToRange(minimum[0],maximum[0], static_cast(island.centerSubvoxels[0])), DistanceToRange(minimum[1],maximum[1], static_cast(island.centerSubvoxels[1]))); const double radialMaximum=std::max({ std::hypot(minimum[0]-island.centerSubvoxels[0],minimum[1]-island.centerSubvoxels[1]), std::hypot(minimum[0]-island.centerSubvoxels[0],maximum[1]-island.centerSubvoxels[1]), std::hypot(maximum[0]-island.centerSubvoxels[0],minimum[1]-island.centerSubvoxels[1]), std::hypot(maximum[0]-island.centerSubvoxels[0],maximum[1]-island.centerSubvoxels[1])}); const double top=static_cast(island.topZSubvoxels); const double topDrop=std::max(0.0,(1.0-Q16ToDouble(recipe.topFlattenQ16)) *static_cast(island.topHalfSubvoxels)*2.0); const double topDistanceMinimum=minimum[2]-top; const double topDistanceMaximum=maximum[2]-(top-topDrop); const double sideDistanceMinimum=radialMinimum-std::max(0.0,radius+contour); const double sideDistanceMaximum=recipe.geometryVersion<2 ?radialMaximum:radialMaximum-std::max(0.0,radius-contour); double lowerMinimum=0.0,lowerMaximum=0.0; if(recipe.geometryVersion<2) { const double bottom=static_cast(island.bottomZSubvoxels); lowerMinimum=bottom-maximum[2]; lowerMaximum=bottom-minimum[2]; } else { const double crust=static_cast(IslandCrustBottom(recipe,island)); lowerMinimum=crust-maximum[2]; lowerMaximum=crust+radius*0.16-minimum[2]; } double structuralMinimum=std::max({topDistanceMinimum, sideDistanceMinimum,lowerMinimum}); double structuralMaximum=std::max({topDistanceMaximum, sideDistanceMaximum,lowerMaximum}); if(recipe.geometryVersion>=2) { const double rootBlend=std::clamp(radius*0.12,2.0*Q,5.0*Q); for(std::int32_t root=0;root=5&&recipe.geometryVersion<8 &&island.ordinal==recipe.anchorIslandOrdinal &&recipe.anchorRootBottomZSubvoxels(IslandCrustBottom(recipe,island)); const double rootBottom=static_cast(recipe.anchorRootBottomZSubvoxels); const double rootRadius=static_cast(recipe.anchorRootRadiusSubvoxels); const double anchorMinimum=std::max({radialMinimum-rootRadius, rootBottom-maximum[2],minimum[2]-rootTop}); const double anchorMaximum=std::max({radialMaximum-0.92*rootRadius, rootBottom-minimum[2],maximum[2]-rootTop}); const double anchorDetail=0.75*static_cast(recipe.roughnessSubvoxels); const double anchorBlend=std::clamp(rootRadius*0.12,3.0*Q,6.0*Q); structuralMinimum=std::min(structuralMinimum,anchorMinimum-anchorDetail) -anchorBlend*0.25; structuralMaximum=std::min(structuralMaximum,anchorMaximum+anchorDetail); } } const double finalDetail=static_cast(recipe.roughnessSubvoxels); return {structuralMinimum-finalDetail,structuralMaximum+finalDetail}; } inline FDensityRange ConnectorDensityRange(const FloatingIslandRecipeV1& recipe, const FloatingIslandConnectorV1& edge,const std::array& minimum, const std::array& maximum) { const double ax=static_cast(edge.startSubvoxels[0]); const double ay=static_cast(edge.startSubvoxels[1]); const double az=static_cast(edge.startSubvoxels[2]); const double dx=static_cast(edge.endSubvoxels[0])-ax; const double dy=static_cast(edge.endSubvoxels[1])-ay; const double dz=static_cast(edge.endSubvoxels[2])-az; const double length=std::hypot(dx,dy); if(!(length>0.0))return {static_cast(MaxDensity),static_cast(MaxDensity)}; const double ux=dx/length,uy=dy/length; const FScalarRange along=AffineRange(minimum[0],maximum[0],ax,ux, minimum[1],maximum[1],ay,uy); const FScalarRange signedSide=AffineRange(minimum[0],maximum[0],ax,-uy, minimum[1],maximum[1],ay,ux); const FScalarRange side=AbsoluteRange(signedSide.minimum,signedSide.maximum); const double qxMinimum=side.minimum-static_cast(edge.halfWidthSubvoxels); const double qxMaximum=side.maximum-static_cast(edge.halfWidthSubvoxels); const FScalarRange alongCenter=AbsoluteRange(along.minimum-0.5*length, along.maximum-0.5*length); const double qyMinimum=alongCenter.minimum-0.5*length; const double qyMaximum=alongCenter.maximum-0.5*length; const double horizontalMinimum=RectangleSdf(qxMinimum,qyMinimum); const double horizontalMaximum=RectangleSdf(qxMaximum,qyMaximum); const double tMinimum=std::clamp(along.minimum/length,0.0,1.0); const double tMaximum=std::clamp(along.maximum/length,0.0,1.0); const double deckAtMinimum=az+dz*tMinimum; const double deckAtMaximum=az+dz*tMaximum; const double deckMinimum=std::min(deckAtMinimum,deckAtMaximum); const double deckMaximum=std::max(deckAtMinimum,deckAtMaximum); const std::uint64_t westernEdgeId=StableId(recipe.seed,0x200000000ull, 0x425249444745ull); const bool westernSupportConnector=edge.stableId==westernEdgeId; if(recipe.geometryVersion>=2&&WorldAssemblyEvaluatorVersion>=183 &&westernSupportConnector) { const double minimumHalfWidth=static_cast(recipe.routeConstraints.capsuleRadiusSubvoxels+Q); const double baseHalfWidth=static_cast(edge.halfWidthSubvoxels); const double halfWidthMinimum=std::max(minimumHalfWidth,baseHalfWidth-0.48*Q); const double halfWidthMaximum=std::max(minimumHalfWidth,baseHalfWidth+0.48*Q); const double roundingMinimum=std::min({1.5*Q,halfWidthMinimum*0.6, static_cast(edge.thicknessSubvoxels)*0.45}); const double roundingMaximum=std::min({1.5*Q,halfWidthMaximum*0.6, static_cast(edge.thicknessSubvoxels)*0.45}); const double roundedXMinimum=side.minimum-halfWidthMaximum+roundingMinimum; const double roundedXMaximum=side.maximum-halfWidthMinimum+roundingMaximum; const double roundedYMinimum=alongCenter.minimum-0.5*length+roundingMinimum; const double roundedYMaximum=alongCenter.maximum-0.5*length+roundingMaximum; const double halfThickness=0.5*static_cast(edge.thicknessSubvoxels); const double centerMinimum=deckMinimum-halfThickness; const double centerMaximum=deckMaximum-halfThickness; const double differenceMinimum=minimum[2]-centerMaximum; const double differenceMaximum=maximum[2]-centerMinimum; const FScalarRange absoluteDifference=AbsoluteRange(differenceMinimum,differenceMaximum); const double roundedZMinimum=absoluteDifference.minimum-halfThickness+roundingMinimum; const double roundedZMaximum=absoluteDifference.maximum-halfThickness+roundingMaximum; return {RoundedBoxSdf(roundedXMinimum,roundedYMinimum,roundedZMinimum), RoundedBoxSdf(roundedXMaximum,roundedYMaximum,roundedZMaximum)}; } const double thickness=static_cast(edge.thicknessSubvoxels); const double maximumArch=recipe.geometryVersion>=2&&WorldAssemblyEvaluatorVersion<172 ?2.0*thickness:0.0; const double upperMinimum=minimum[2]-deckMaximum; const double upperMaximum=maximum[2]-deckMinimum; const double lowerMinimum=deckMinimum-thickness-maximumArch-maximum[2]; const double lowerMaximum=deckMaximum-thickness-minimum[2]; return {std::max({horizontalMinimum,upperMinimum,lowerMinimum}), std::max({horizontalMaximum,upperMaximum,lowerMaximum})}; } 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(recipe.blendRadiusSubvoxels); double result = static_cast(MaxDensity); for (const auto& island : plan.islands) { double group = IslandDensity(recipe, plan, island, static_cast(x), static_cast(y), static_cast(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(x), static_cast(y), static_cast(z), &topSurface, &alongFraction); if (recipe.geometryVersion >= 2) connector = ApplySurfaceDetail(recipe, edge.stableId, connector, static_cast(x), static_cast(y), static_cast(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(recipe.blendRadiusSubvoxels); double result = static_cast(MaxDensity); const auto evaluateLocalGroup = [&](std::size_t islandIndex) { const auto& island = plan.islands[islandIndex]; double group = IslandDensity(recipe, plan, island, static_cast(x), static_cast(y), static_cast(z)); for (const std::uint32_t edgeIndex : featureIndices) { if (edgeIndex < islandCount) continue; const std::size_t planIndex = static_cast(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(x), static_cast(y), static_cast(z), &topSurface, &alongFraction); if (recipe.geometryVersion >= 2) connector = ApplySurfaceDetail(recipe, edge.stableId, connector, static_cast(x), static_cast(y), static_cast(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(x), static_cast(y), static_cast(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(x), static_cast(y), static_cast(z), &topSurface, &alongFraction); if (recipe.geometryVersion >= 2) connector = ApplySurfaceDetail(recipe, edge.stableId, connector, static_cast(x), static_cast(y), static_cast(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(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(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 islandIds; std::unordered_set 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(island.topZSubvoxels - island.bottomZSubvoxels) / (2.0 * static_cast(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(anchorIsland->centerSubvoxels[0]), static_cast(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 edgeIds; std::unordered_map> 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 reached; std::vector 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 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(delta) * static_cast(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::llround(maxRise))); return centerZ + static_cast(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(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(to.centerSubvoxels[0] - from.centerSubvoxels[0]); const double dy = static_cast(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 capsule footprint supported while seating L37 connectors just below the native island tops. // Garder l'appui de la capsule tout en enfouissant les connecteurs L37 juste sous les plateaux. const double landingFraction = recipe.geometryVersion >= 2 ? 0.52 : 0.62; const double fromReach = static_cast(from.radiusSubvoxels) * landingFraction; const double toReach = static_cast(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 bool craterMouthConnector=WorldAssemblyEvaluatorVersion>=174 &&(from.ordinal==recipe.anchorIslandOrdinal ||to.ordinal==recipe.anchorIslandOrdinal); const bool westernSupportConnector=WorldAssemblyEvaluatorVersion>=178 &&((from.ordinal==0&&to.ordinal==1)||(from.ordinal==1&&to.ordinal==0)); const double routeDeckLift=recipe.geometryVersion>=2 ?(WorldAssemblyEvaluatorVersion>=178 ?(westernSupportConnector ?(WorldAssemblyEvaluatorVersion>=181?-1.0 :(WorldAssemblyEvaluatorVersion>=180?-1.25 :(WorldAssemblyEvaluatorVersion>=179?-1.5:-1.0))):-2.0) :(craterMouthConnector?-2.0 :(WorldAssemblyEvaluatorVersion>=172?-1.0: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 FindSurfaceZ(const FloatingIslandRecipeV1& recipe, const FloatingIslandPlanV1& plan, std::int64_t x, std::int64_t y) { const std::int64_t step = std::max(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 FindCapsuleFootingTopZ( const FloatingIslandRecipeV1& recipe, const FloatingIslandPlanV1& plan, std::int64_t x, std::int64_t y) { const double radius = static_cast(recipe.routeConstraints.capsuleRadiusSubvoxels); constexpr std::array, 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::min(); for (const auto& offset : footprint) { const std::int64_t sampleX = RoundQ(static_cast(x) + offset[0] * radius); const std::int64_t sampleY = RoundQ(static_cast(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::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;} } manifest.recipe.terrainFollowingRoute=recipeValue.Get("terrain_following_route").Boolean(false); 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(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; if(recipe.terrainFollowingRoute)out << ",\"terrain_following_route\":true"; 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 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(recipe.primaryIslandCount) + 2); const std::uint32_t count = recipe.primaryIslandCount; const std::uint32_t middle = count / 2; std::vector tops(count, recipe.layoutCenterTopZSubvoxels); std::vector 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> 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(middle) * recipe.spacingSubvoxels + static_cast(i) * recipe.spacingSubvoxels, recipe.layoutOriginYSubvoxels + yOffset}; } else { const std::int64_t along = (static_cast(i) - static_cast(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(recipe.spacingSubvoxels) : std::hypot(static_cast(primaryXY[b][0] - primaryXY[a][0]), static_cast(primaryXY[b][1] - primaryXY[a][1])); const double gap = centerDistance - 0.78 * (static_cast(radii[a]) + static_cast(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::ceil(riseLimit(i - 1, i)))); } for (std::int32_t i = static_cast(middle) - 1; i >= 0; --i) { const auto site = 0x100000000ull + static_cast(i); tops[static_cast(i)] = tops[static_cast(i + 1)] + floating_island_detail::SignedRange(recipe.seed, site, 0x4c4556454cull, static_cast(std::ceil(riseLimit(static_cast(i), static_cast(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(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(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::llround(0.78 * static_cast(host.radiusSubvoxels + recipe.minimumRadiusSubvoxels))); const std::int64_t rise = static_cast(std::ceil(std::max(0.0, static_cast(gap) * maxGrade * levelFactor))); const std::int64_t sideRise = recipe.geometryVersion < 2 ? rise : std::min(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(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(b.centerSubvoxels[0] - a.centerSubvoxels[0]); const double dy = static_cast(b.centerSubvoxels[1] - a.centerSubvoxels[1]); const std::uint32_t pieces = static_cast(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(piece) / pieces; const std::int64_t x = floating_island_detail::RoundQ( static_cast(a.centerSubvoxels[0]) + dx * t); const std::int64_t y = floating_island_detail::RoundQ( static_cast(a.centerSubvoxels[1]) + dy * t); std::int64_t routeX=x,routeY=y; if(WorldAssemblyEvaluatorVersion>=192&&recipe.terrainFollowingRoute &&recipe.geometryVersion>=5 &&segment+2==count&&pieces>1) { const auto& anchor=islands[recipe.anchorIslandOrdinal]; const std::uint32_t side=static_cast( floating_island_detail::UnitQ16(recipe.seed,anchor.stableId, 0x414e43484f524d50ull)&1u); const double length=std::hypot(dx,dy); const double bend=10.0*floating_island_detail::Q *std::pow(std::sin(floating_island_detail::Pi*t),2.0); const double sign=side?-1.0:1.0; if(length>0.0) { routeX=floating_island_detail::RoundQ( static_cast(routeX)+sign*(-dy/length)*bend); routeY=floating_island_detail::RoundQ( static_cast(routeY)+sign*(dx/length)*bend); } } if(recipe.geometryVersion>=5&&segment+2==count&&piece==pieces) { const auto& anchor=islands[recipe.anchorIslandOrdinal]; const std::uint32_t side=static_cast( 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(anchor.centerSubvoxels[0]) +static_cast(recipe.anchorMouthOffsetSubvoxels)*std::cos(angle); const double mouthY=static_cast(anchor.centerSubvoxels[1]) +static_cast(recipe.anchorMouthOffsetSubvoxels)*std::sin(angle); routeX=floating_island_detail::RoundQ(mouthX); routeY=floating_island_detail::RoundQ(mouthY); } const auto surface = WorldAssemblyEvaluatorVersion>=199 &&recipe.terrainFollowingRoute ?floating_island_detail::FindSurfaceZ(recipe,manifest.plan,routeX,routeY) :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 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(baseHeights[i]); for (std::size_t j = 0; j < route.size(); ++j) { const double dx = static_cast(route[i].positionSubvoxels[0] - route[j].positionSubvoxels[0]); const double dy = static_cast(route[i].positionSubvoxels[1] - route[j].positionSubvoxels[1]); profile = std::max(profile, static_cast(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 FloatingIslandFieldV1::solidSupportBounds() const { std::vector 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& tileMinimumSubvoxels, const std::array& 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(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& featureMinimum, const std::array& 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(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(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( floating_island_detail::RootCount(*recipe_, island)) * rootBlend * 0.25; const double rootLowerBound = 0.22 * static_cast(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(edgeGap) - recipe_->roughnessSubvoxels; groupLowerBound = std::min(groupLowerBound, edgeLowerBound) - static_cast(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::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 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 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(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(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(islandIndex)); for (std::size_t edgeIndex = 0; edgeIndex < plan_->clusterEdges.size(); ++edgeIndex) if (plan_->clusterEdges[edgeIndex].fromIslandId == plan_->islands[islandIndex].stableId) insertSorted(static_cast(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; using namespace floating_island_detail; if (plan_->islands.empty()) return {true,MaxDensity,MaxDensity}; const std::array minimum{static_cast(minX), static_cast(minY),static_cast(minZ)}; const std::array maximum{static_cast(maxX), static_cast(maxY),static_cast(maxZ)}; double fieldMinimum=static_cast(MaxDensity); double fieldMaximum=static_cast(MaxDensity); for(const auto& island:plan_->islands) { FDensityRange group=IslandDensityRange(*recipe_,island,minimum,maximum); for(const auto& edge:plan_->clusterEdges) { if(edge.fromIslandId!=island.stableId)continue; const FDensityRange connector=ConnectorDensityRange(*recipe_,edge,minimum,maximum); const double detail=recipe_->geometryVersion>=2 ?static_cast(recipe_->roughnessSubvoxels):0.0; group.minimum=std::min(group.minimum,connector.minimum-detail) -static_cast(recipe_->blendRadiusSubvoxels)*0.25; group.maximum=std::min(group.maximum,connector.maximum+detail); } fieldMinimum=std::min(fieldMinimum,group.minimum); fieldMaximum=std::min(fieldMaximum,group.maximum); } if(!std::isfinite(fieldMinimum)||!std::isfinite(fieldMaximum))return result; // These interval operations are shared by native and future generator classifiers: SDF // intersections take max, unions take min, and smooth unions subtract at most k/4. // Keep two subvoxels of slack for floating bounds followed by the field's integer rounding. // Ces bornes servent les generateurs actuels et futurs : intersections=max, unions=min. const double minimumRounded=std::clamp(std::floor(fieldMinimum)-2.0, -static_cast(MaxDensity),static_cast(MaxDensity)); const double maximumRounded=std::clamp(std::ceil(fieldMaximum)+2.0, -static_cast(MaxDensity),static_cast(MaxDensity)); if(minimumRounded>maximumRounded)return result; result.known=true; result.minimumDensitySubvoxels=static_cast(minimumRounded); result.maximumDensitySubvoxels=static_cast(maximumRounded); return result; } FloatingIslandInfluenceBounds FloatingIslandFieldV1::influenceBounds() const noexcept { FloatingIslandInfluenceBounds result; if (!isValid()) return result; result.finite = true; result.minimumSubvoxels.fill(std::numeric_limits::max()); result.maximumSubvoxels.fill(std::numeric_limits::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::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(random.next() % static_cast(high - low + 1)); }; constexpr std::array samples{0, 1, 2}; for (std::uint32_t boxIndex = 0; boxIndex < boxCount; ++boxIndex) { std::array 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(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 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(edge.endSubvoxels[0] - edge.startSubvoxels[0]); const double dy = static_cast(edge.endSubvoxels[1] - edge.startSubvoxels[1]); const double length = std::hypot(dx, dy); if (!(length > 0.0)) continue; const double offset = static_cast(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 badMin{island.centerSubvoxels[0] - badReach, island.centerSubvoxels[1] - badReach, island.bottomZSubvoxels - recipe.roughnessSubvoxels}; const std::array badMax{island.centerSubvoxels[0] + badReach, island.centerSubvoxels[1] + badReach, island.topZSubvoxels + recipe.roughnessSubvoxels}; const double radial = static_cast(island.radiusSubvoxels) + static_cast(recipe.contourWarpSubvoxels) * 0.72; const double z = static_cast(island.topZSubvoxels) - static_cast(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 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(constraints.capsuleRadiusSubvoxels); const double height = static_cast(constraints.capsuleHeightSubvoxels); const double halfHeight = height * 0.5; const double cylinderHalf = std::max(0.0, halfHeight - radius); const double spacing = static_cast(constraints.sweepSpacingSubvoxels); double minimumClearance = std::numeric_limits::infinity(); const std::array, 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(b[0] - a[0]); const double dy = static_cast(b[1] - a[1]); const double dz = static_cast(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(1, static_cast(std::ceil(length / std::max(spacing, 1.0)))); for (std::uint64_t center = 0; center <= centers; ++center) { const double t = static_cast(center) / centers; const double cx = static_cast(a[0]) + dx * t; const double cy = static_cast(a[1]) + dy * t; const double cz = static_cast(a[2]) + dz * t + routeOffsetSubvoxels; ++result.centerSamples; const std::uint32_t verticalSteps = static_cast( 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(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(constraints.supportProbeRadiusSubvoxels); const std::array, 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(constraints.minimumClearanceSubvoxels) + 0.5 * floating_island_detail::Q; depth <= static_cast(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::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(island.topZSubvoxels - island.bottomZSubvoxels) / (2.0 * static_cast(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; } }