wip(assembler): M3 round 2 -- islands get real mass: deep crust, separate tapered roots, arched spans

Pinch cause: SmoothStep taper drove the underside radius to zero at one point. Version 2: depth 0.75x
diameter (thin-pad control at 0.30 fails), 3-4 seeded roots, bounded contour warp + roughness, one
off-route side island, 3 m rock spans. Bounds/route/W1-W8-W16 pass. Open: legacy probe metadata says
25 cm but production legacy is 12.5 cm -- round 3 must fix the import scale contract.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-10 12:24:35 +02:00
1 parent 53a1e69af7
commit 4afea7c802
15 files changed
+1007 -99

No files matched your search

+14 -6
View File
@@ -153,16 +153,24 @@ radius range, top flattening, taper, bounded contour warp, roughness, blend, con
route constraints. `PlanFloatingIslandCluster` emits stable island and connector IDs, a connected
cluster graph, conservative influence AABBs, and a centerline route. At least the primary chain is
always planned; `islandDensityQ16` controls up to two optional side sites. Connectors carry a sharp-tag
ready bit for a later hybrid-mesher adapter.
ready bit for a later hybrid-mesher adapter. The schema accepts geometry version 1 and 2. Manifests
without a geometry version retain version-1 evaluation and canonical hashes.
`FloatingIslandFieldV1::densitySubvoxels` evaluates the canonical island and connector field.
`interval` returns unknown for boxes overlapping any influence AABB and a certified all-air range
outside them. AABBs include contour displacement, roughness, taper height and smoothing reach.
outside them. Geometry version 2 builds a flat walkable crust plus deterministic lobed rock roots,
keeps bounded oblique surface detail inside its certified envelope, and arches the connector
undersides. AABBs include root depth, contour displacement, detail, arch sag and smoothing reach.
`AuditFloatingIslandBounds` checks sampled boxes and void beside connectors and includes a deliberately
shrunken island-bound negative control. `AuditFloatingIslandRoute` samples the player capsule and
support along the serialized route. The current field block is not yet composed into
`WorldAssemblyManifest` or the Unreal adapter; M3 round 1 proves the standalone Core representation.
The frozen owner evaluator is used as a visual and timing oracle, not as a hash target.
shrunken island-bound negative control. `AuditFloatingIslandThickness` reports each island's
`(topZ - bottomZ) / (2 * radius)` and checks a caller-supplied minimum; `AuditFloatingIslandRoute`
samples the player capsule and support along the serialized route. M3 round 2 passed at a minimum
depth-to-diameter ratio of 0.75 against a 0.70 threshold, while a 0.30 thin-pad control failed.
The family is still separate from `WorldAssemblyManifest` and the Unreal adapter. M3 rounds 1–2 prove
the standalone Core representation, bounded geometry, route, determinism and matched legacy timing.
The frozen owner evaluator is a visual and timing oracle, not a hash target. Unreal import and cooked
collision are planned for M3 round 3.
## Queries and tiles
@@ -301,29 +301,36 @@ namespace VoxelForgeCore
struct FloatingIslandRecipeV1
{
std::uint32_t schemaVersion = 1;
// Profile 1 preserves the original shallow pads. Profile 2 adds a thick crust,
// bounded lobed roots and arched spans without changing the feature-family schema.
std::uint32_t geometryVersion = 2;
std::uint32_t unitsPerVoxel = static_cast<std::uint32_t>(WorldAssemblySubvoxelsPerVoxel);
std::uint32_t seed = 0;
std::int64_t layoutOriginXSubvoxels = 0;
std::int64_t layoutOriginYSubvoxels = 0;
std::int64_t layoutCenterTopZSubvoxels = -128 * WorldAssemblySubvoxelsPerVoxel;
std::int64_t layoutMinimumZSubvoxels = -256 * WorldAssemblySubvoxelsPerVoxel;
std::int64_t layoutCenterTopZSubvoxels = -64 * WorldAssemblySubvoxelsPerVoxel;
std::int64_t layoutMinimumZSubvoxels = -128 * WorldAssemblySubvoxelsPerVoxel;
std::int64_t layoutMaximumZSubvoxels = 0;
std::uint32_t primaryIslandCount = 5;
std::uint32_t primaryIslandCount = 3;
std::int64_t islandDensityQ16 = 1ll << 15;
std::int64_t spacingSubvoxels = 112 * WorldAssemblySubvoxelsPerVoxel;
std::int64_t minimumRadiusSubvoxels = 40 * WorldAssemblySubvoxelsPerVoxel;
std::int64_t maximumRadiusSubvoxels = 48 * WorldAssemblySubvoxelsPerVoxel;
// Legacy owner lengths use 12.5 cm voxels; Core uses 25 cm, so halve the owner lengths.
// Les longueurs owner utilisent des voxels de 12,5 cm ; Core utilise 25 cm, donc les diviser par deux.
std::int64_t spacingSubvoxels = 56 * WorldAssemblySubvoxelsPerVoxel;
std::int64_t minimumRadiusSubvoxels = 12 * WorldAssemblySubvoxelsPerVoxel;
std::int64_t maximumRadiusSubvoxels = 24 * WorldAssemblySubvoxelsPerVoxel;
std::int64_t thicknessRatioQ16 = 39322; // 0.60
std::int64_t verticalJitterQ16 = 26214; // 0.40
std::int64_t topFlattenQ16 = 51118; // 0.78
std::int64_t verticalJitterQ16 = 39322; // 0.60
std::int64_t topFlattenQ16 = 39322; // 0.60
std::int64_t taperEndMinimumQ16 = 29491; // 0.45
std::int64_t taperEndMaximumQ16 = 45875; // 0.70
std::int64_t blendRadiusSubvoxels = 4 * WorldAssemblySubvoxelsPerVoxel;
std::int64_t contourWarpSubvoxels = 15 * WorldAssemblySubvoxelsPerVoxel;
std::int64_t roughnessSubvoxels = WorldAssemblySubvoxelsPerVoxel / 2;
std::int64_t boundarySealSubvoxels = 4 * WorldAssemblySubvoxelsPerVoxel;
std::int64_t bridgeHalfWidthSubvoxels = 4 * WorldAssemblySubvoxelsPerVoxel;
std::int64_t bridgeThicknessSubvoxels = 3 * WorldAssemblySubvoxelsPerVoxel;
std::int64_t crustDepthBonusQ16 = 9830; // 0.15 radius, profile 2 only
std::int64_t rootDepthRatioQ16 = 49152; // 0.75 radius, profile 2 only
std::int64_t blendRadiusSubvoxels = 3 * WorldAssemblySubvoxelsPerVoxel;
std::int64_t contourWarpSubvoxels = 6 * WorldAssemblySubvoxelsPerVoxel + 77; // 6.3
std::int64_t roughnessSubvoxels = 3 * WorldAssemblySubvoxelsPerVoxel / 2;
std::int64_t boundarySealSubvoxels = 2 * WorldAssemblySubvoxelsPerVoxel;
std::int64_t bridgeHalfWidthSubvoxels = 6 * WorldAssemblySubvoxelsPerVoxel;
std::int64_t bridgeThicknessSubvoxels = 4 * WorldAssemblySubvoxelsPerVoxel;
WorldAssemblyRouteConstraintsV1 routeConstraints;
std::uint64_t contentHash = 0;
};
@@ -409,6 +416,16 @@ namespace VoxelForgeCore
bool passed = false;
};
struct FloatingIslandThicknessAuditResult
{
std::uint64_t islandsTested = 0;
std::uint64_t islandsBelowMinimum = 0;
std::uint64_t minimumIslandStableId = 0;
double minimumDepthToDiameterRatio = 0.0;
double requiredDepthToDiameterRatio = 0.70;
bool passed = false;
};
struct FloatingIslandInfluenceBounds
{
bool finite = false;
@@ -451,6 +468,8 @@ namespace VoxelForgeCore
std::uint64_t seed = 0x49534c414e444231ull);
VFCORE_API FloatingIslandRouteAuditResult AuditFloatingIslandRoute(
const FloatingIslandFieldV1& field, std::int64_t routeOffsetSubvoxels = 0);
VFCORE_API FloatingIslandThicknessAuditResult AuditFloatingIslandThickness(
const FloatingIslandPlanV1& plan, double minimumDepthToDiameterRatio = 0.70);
struct HeightfieldPointSample
{
+298 -48
View File
@@ -174,6 +174,12 @@ namespace floating_island_detail
HashWord(hash, static_cast<std::uint64_t>(recipe.topFlattenQ16), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.taperEndMinimumQ16), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.taperEndMaximumQ16), 8);
if (recipe.geometryVersion >= 2)
{
HashWord(hash, recipe.geometryVersion, 4);
HashWord(hash, static_cast<std::uint64_t>(recipe.crustDepthBonusQ16), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.rootDepthRatioQ16), 8);
}
HashWord(hash, static_cast<std::uint64_t>(recipe.blendRadiusSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.contourWarpSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.roughnessSubvoxels), 8);
@@ -233,7 +239,7 @@ namespace floating_island_detail
inline bool ValidateRecipe(const FloatingIslandRecipeV1& recipe, std::string& error)
{
const std::array<std::int64_t, 23> values = {
const std::array<std::int64_t, 25> values = {
recipe.layoutOriginXSubvoxels, recipe.layoutOriginYSubvoxels,
recipe.layoutCenterTopZSubvoxels, recipe.layoutMinimumZSubvoxels,
recipe.layoutMaximumZSubvoxels, recipe.islandDensityQ16,
@@ -241,22 +247,28 @@ namespace floating_island_detail
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,
recipe.routeConstraints.capsuleRadiusSubvoxels,
recipe.routeConstraints.capsuleHeightSubvoxels,
recipe.routeConstraints.maximumGradeQ16};
if (recipe.schemaVersion != 1 || recipe.unitsPerVoxel != Q
if (recipe.schemaVersion != 1 || (recipe.geometryVersion != 1 && recipe.geometryVersion != 2)
|| (recipe.geometryVersion == 1
&& (recipe.crustDepthBonusQ16 != 0 || recipe.rootDepthRatioQ16 != 0))
|| recipe.unitsPerVoxel != Q
|| recipe.primaryIslandCount < 3 || recipe.primaryIslandCount > 9
|| 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 < 64 * Q || recipe.spacingSubvoxels > 256 * Q
|| recipe.minimumRadiusSubvoxels < 24 * Q
|| recipe.spacingSubvoxels < minimumSpacing || recipe.spacingSubvoxels > 256 * Q
|| recipe.minimumRadiusSubvoxels < minimumRadius
|| recipe.maximumRadiusSubvoxels < recipe.minimumRadiusSubvoxels
|| recipe.maximumRadiusSubvoxels > 64 * Q
|| recipe.thicknessRatioQ16 < 29491 || recipe.thicknessRatioQ16 > 49152
@@ -264,6 +276,9 @@ namespace floating_island_detail
|| 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
@@ -325,18 +340,30 @@ namespace floating_island_detail
}
inline double Roughness(const FloatingIslandRecipeV1& recipe,
const FloatingIslandAnchorV1& island,
double x, double y, double z)
std::uint64_t featureId, double x, double y, double z)
{
const double phase = static_cast<double>(Roll(recipe.seed, island.stableId, 0x524f5547ull)
const double phase = static_cast<double>(Roll(recipe.seed, featureId, 0x524f5547ull)
& 0xffffffffull) * (2.0 * Pi / 4294967296.0);
const double xv = x / static_cast<double>(Q);
const double yv = y / static_cast<double>(Q);
const double zv = z / static_cast<double>(Q);
if (recipe.geometryVersion < 2)
{
const double oldNoise = 0.52 * std::sin(xv * 0.071 + yv * 0.053 + zv * 0.089 + phase)
+ 0.31 * std::sin(xv * 0.127 - yv * 0.097 + zv * 0.061 - phase * 0.73)
+ 0.17 * std::cos(xv * 0.043 + yv * 0.139 - zv * 0.113 + phase * 1.39);
return static_cast<double>(recipe.roughnessSubvoxels) * oldNoise;
}
// Four oblique bands span about 7 m down to 0.9 m at 25 cm per voxel.
// Their absolute weights sum to one, keeping displacement inside the 3-voxel envelope.
const double noise = 0.52 * std::sin(xv * 0.071 + yv * 0.053 + zv * 0.089 + phase)
+ 0.31 * std::sin(xv * 0.127 - yv * 0.097 + zv * 0.061 - phase * 0.73)
+ 0.17 * std::cos(xv * 0.043 + yv * 0.139 - zv * 0.113 + phase * 1.39);
return static_cast<double>(recipe.roughnessSubvoxels) * noise;
const double fine = 0.46 * std::sin(xv * 0.22 + yv * 0.31 - zv * 0.17 + phase * 0.41)
+ 0.32 * std::cos(xv * 0.38 - yv * 0.21 + zv * 0.29 - phase * 0.67)
+ 0.22 * std::sin(xv * 0.74 + yv * 0.53 + zv * 0.61 + phase * 1.17);
return static_cast<double>(recipe.roughnessSubvoxels)
* std::clamp(0.42 * noise + 0.58 * fine, -1.0, 1.0);
}
inline double IslandTopSurface(const FloatingIslandRecipeV1& recipe,
@@ -355,31 +382,138 @@ namespace floating_island_detail
return static_cast<double>(island.topZSubvoxels) - drop;
}
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)
{
return 3 + static_cast<std::int32_t>(Roll(recipe.seed, island.stableId,
0x524f4f54434e54ull) & 1ull);
}
inline double UnitHash(std::uint64_t hash)
{ return static_cast<double>(hash & 0xffffull) / 65535.0; }
inline double RootLobeDensity(const FloatingIslandRecipeV1& recipe,
const FloatingIslandAnchorV1& island,
std::int32_t rootIndex, double x, double y, double z)
{
constexpr std::array<std::array<double, 2>, 4> pattern{{
{{-0.92, -0.12}}, {{0.28, 0.98}}, {{0.83, -0.42}}, {{-0.16, -0.94}}}};
const std::uint64_t salt = 0x524f4f540000ull + static_cast<std::uint64_t>(rootIndex);
const std::uint64_t roll = Roll(recipe.seed, island.stableId, salt);
const double radius = static_cast<double>(island.radiusSubvoxels);
const double rootDepth = radius * Q16ToDouble(recipe.rootDepthRatioQ16);
const double crustBottom = static_cast<double>(IslandCrustBottom(recipe, island));
const double orbit = radius * (0.17 + 0.07 * UnitHash(roll));
const double rootX = static_cast<double>(island.centerSubvoxels[0])
+ pattern[static_cast<std::size_t>(rootIndex)][0] * orbit;
const double rootY = static_cast<double>(island.centerSubvoxels[1])
+ pattern[static_cast<std::size_t>(rootIndex)][1] * orbit;
const double shortRootVariation = UnitHash(Roll(recipe.seed, island.stableId, salt ^ 0x4c454eull));
const double lengthRatio = rootIndex == 0 ? 1.0
: Q16ToDouble(island.taperEndQ16) * (0.82 + 0.18 * shortRootVariation);
const double lowerDepth = rootDepth * lengthRatio;
const double embedDepth = rootDepth * (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 * (0.15 + 0.055 * UnitHash(Roll(recipe.seed, island.stableId, salt ^ 0x5258ull)));
const double ry = radius * (0.15 + 0.065 * UnitHash(Roll(recipe.seed, island.stableId, salt ^ 0x5259ull)));
const double q = (x - rootX) * (x - rootX) / (rx * rx)
+ (y - rootY) * (y - rootY) / (ry * ry)
+ (z - centerZ) * (z - centerZ) / (verticalRadius * verticalRadius);
return (std::sqrt(std::max(q, 0.0)) - 1.0) * 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<double>(recipe.roughnessSubvoxels) * detailScale;
const double band = 2.0 * amplitude;
if (!(amplitude > 0.0) || std::abs(structural) >= band) return structural;
const double fade = 1.0 - SmoothStep(std::abs(structural) / band);
return structural + Roughness(recipe, featureId, x, y, z) * detailScale * fade;
}
inline double IslandDensity(const FloatingIslandRecipeV1& recipe,
const FloatingIslandAnchorV1& island,
double x, double y, double z)
{
if (recipe.geometryVersion < 2)
{
const double dx = x - static_cast<double>(island.centerSubvoxels[0]);
const double dy = y - static_cast<double>(island.centerSubvoxels[1]);
const double radial = std::hypot(dx, dy);
const double topZ = static_cast<double>(island.topZSubvoxels);
const double bottomZ = static_cast<double>(island.bottomZSubvoxels);
const double verticalSpan = std::max(topZ - bottomZ, 1.0);
const double height = std::clamp((z - bottomZ) / verticalSpan, 0.0, 1.0);
const double taperEnd = Q16ToDouble(island.taperEndQ16);
const double taper = SmoothStep(std::clamp(height / std::max(taperEnd, 0.01), 0.0, 1.0));
const double effectiveRadius = std::max(0.0,
static_cast<double>(island.radiusSubvoxels) + LobeOffset(recipe, island, dx, dy, z));
const double envelope = effectiveRadius * taper;
const double top = IslandTopSurface(recipe, island,
static_cast<double>(island.centerSubvoxels[0]) + dx,
static_cast<double>(island.centerSubvoxels[1]) + dy);
return ApplySurfaceDetail(recipe, island.stableId,
std::max({z - top, radial - envelope, bottomZ - z}), x, y, z);
}
const double dx = x - static_cast<double>(island.centerSubvoxels[0]);
const double dy = y - static_cast<double>(island.centerSubvoxels[1]);
const double radial = std::hypot(dx, dy);
const double topZ = static_cast<double>(island.topZSubvoxels);
const double bottomZ = static_cast<double>(island.bottomZSubvoxels);
const double verticalSpan = std::max(topZ - bottomZ, 1.0);
const double height = std::clamp((z - bottomZ) / verticalSpan, 0.0, 1.0);
const double taperEnd = Q16ToDouble(island.taperEndQ16);
const double taper = SmoothStep(std::clamp(height / std::max(taperEnd, 0.01), 0.0, 1.0));
const double radius = static_cast<double>(island.radiusSubvoxels);
const double effectiveRadius = std::max(0.0,
static_cast<double>(island.radiusSubvoxels) + LobeOffset(recipe, island, dx, dy, z));
const double envelope = effectiveRadius * taper;
radius + LobeOffset(recipe, island, dx, dy, z));
const double edge = std::clamp(radial / std::max(effectiveRadius, 1.0), 0.0, 1.0);
const double lowerSurface = static_cast<double>(IslandCrustBottom(recipe, island))
+ radius * 0.16 * edge * edge;
const double top = IslandTopSurface(recipe, island,
static_cast<double>(island.centerSubvoxels[0]) + dx,
static_cast<double>(island.centerSubvoxels[1]) + dy);
const double sdf = std::max({z - top, radial - envelope, bottomZ - z});
return sdf + Roughness(recipe, island, x, y, z);
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);
const double topDetailScale = topDistance >= std::max(sideDistance, undersideDistance)
|| z >= static_cast<double>(IslandCrustBottom(recipe, island)) + radius * 0.45
? 0.25 : 1.0;
return ApplySurfaceDetail(recipe, island.stableId, sdf, x, y, z, topDetailScale);
}
inline double ConnectorDensity(const FloatingIslandConnectorV1& edge,
double x, double y, double z)
inline double ConnectorDensity(const FloatingIslandRecipeV1& recipe,
const FloatingIslandConnectorV1& edge,
double x, double y, double z,
bool* topSurface = nullptr,
double* alongFraction = nullptr)
{
const double ax = static_cast<double>(edge.startSubvoxels[0]);
const double ay = static_cast<double>(edge.startSubvoxels[1]);
@@ -397,9 +531,15 @@ namespace floating_island_detail
const double outside = std::hypot(std::max(qx, 0.0), std::max(qy, 0.0));
const double horizontal = outside + std::min(std::max(qx, qy), 0.0);
const double t = std::clamp(along / length, 0.0, 1.0);
if (alongFraction) *alongFraction = t;
const double deck = az + (static_cast<double>(edge.endSubvoxels[2]) - az) * t;
return std::max({horizontal, z - deck,
deck - static_cast<double>(edge.thicknessSubvoxels) - z});
const double arch = recipe.geometryVersion >= 2
? 2.0 * static_cast<double>(edge.thicknessSubvoxels) * (4.0 * t * (1.0 - t))
: 0.0;
const double upper = z - deck;
const double lower = deck - static_cast<double>(edge.thicknessSubvoxels) - arch - z;
if (topSurface) *topSurface = upper >= std::max(horizontal, lower);
return std::max({horizontal, upper, lower});
}
inline std::int64_t FieldDensity(const FloatingIslandRecipeV1& recipe,
@@ -416,8 +556,16 @@ namespace floating_island_detail
for (const auto& edge : plan.clusterEdges)
{
if (edge.fromIslandId != island.stableId) continue;
group = SmoothMin(group, ConnectorDensity(edge,
static_cast<double>(x), static_cast<double>(y), static_cast<double>(z)), blend);
bool topSurface = false;
double alongFraction = 0.0;
double connector = ConnectorDensity(recipe, edge,
static_cast<double>(x), static_cast<double>(y), static_cast<double>(z),
&topSurface, &alongFraction);
if (recipe.geometryVersion >= 2)
connector = ApplySurfaceDetail(recipe, edge.stableId, connector,
static_cast<double>(x), static_cast<double>(y), static_cast<double>(z),
topSurface || alongFraction < 0.12 || alongFraction > 0.88 ? 0.25 : 1.0);
group = SmoothMin(group, connector, blend);
}
result = std::min(result, group);
}
@@ -439,11 +587,14 @@ namespace floating_island_detail
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,
@@ -483,6 +634,10 @@ namespace floating_island_detail
|| island.taperEndQ16 > recipe.taperEndMaximumQ16
|| island.centerSubvoxels[2] != island.topZSubvoxels - island.topHalfSubvoxels)
{ error = "floating-island anchor has an invalid or unstable identity or profile"; return false; }
if (recipe.geometryVersion >= 2
&& static_cast<double>(island.topZSubvoxels - island.bottomZSubvoxels)
/ (2.0 * static_cast<double>(island.radiusSubvoxels)) < 0.70)
{ error = "floating-island crust/root depth is below its minimum width ratio"; return false; }
for (std::size_t axis = 0; axis < 3; ++axis)
{
if (!ValidScalar(island.centerSubvoxels[axis])
@@ -527,6 +682,7 @@ namespace floating_island_detail
|| 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);
@@ -593,8 +749,7 @@ namespace floating_island_detail
* radiusRoll / 65535;
island.topHalfSubvoxels = std::max<std::int64_t>(1, island.radiusSubvoxels / 5);
island.topZSubvoxels = topZ;
island.bottomZSubvoxels = topZ
- island.radiusSubvoxels * recipe.thicknessRatioQ16 / Q16;
island.bottomZSubvoxels = IslandBottom(recipe, topZ, island.radiusSubvoxels);
island.centerSubvoxels = {x, y, topZ - island.topHalfSubvoxels};
island.taperEndQ16 = recipe.taperEndMinimumQ16
+ (recipe.taperEndMaximumQ16 - recipe.taperEndMinimumQ16)
@@ -619,14 +774,16 @@ namespace floating_island_detail
// 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.
const double fromReach = static_cast<double>(from.radiusSubvoxels) * 0.62;
const double toReach = static_cast<double>(to.radiusSubvoxels) * 0.62;
const double landingFraction = recipe.geometryVersion >= 2 ? 0.38 : 0.62;
const double fromReach = static_cast<double>(from.radiusSubvoxels) * landingFraction;
const double toReach = static_cast<double>(to.radiusSubvoxels) * landingFraction;
const double sx = from.centerSubvoxels[0] + ux * fromReach;
const double sy = from.centerSubvoxels[1] + uy * fromReach;
const double ex = to.centerSubvoxels[0] - ux * toReach;
const double ey = to.centerSubvoxels[1] - uy * toReach;
const double sz = IslandTopSurface(recipe, from, sx, sy);
const double ez = IslandTopSurface(recipe, to, ex, ey);
const double routeDeckLift = recipe.geometryVersion >= 2 ? 1.0 * Q : 0.0;
const double sz = IslandTopSurface(recipe, from, sx, sy) + routeDeckLift;
const double ez = IslandTopSurface(recipe, to, ex, ey) + routeDeckLift;
edge.startSubvoxels = {RoundQ(sx), RoundQ(sy), RoundQ(sz)};
edge.endSubvoxels = {RoundQ(ex), RoundQ(ey), RoundQ(ez)};
edge.halfWidthSubvoxels = recipe.bridgeHalfWidthSubvoxels;
@@ -734,6 +891,23 @@ std::optional<FloatingIslandManifestV1> FloatingIslandManifestV1::FromJson(
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 (!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)
@@ -867,8 +1041,12 @@ std::string FloatingIslandManifestV1::ToJson() const
<< ",\"vertical_jitter_q16\":" << recipe.verticalJitterQ16
<< ",\"top_flatten_q16\":" << recipe.topFlattenQ16
<< ",\"taper_end_min_q16\":" << recipe.taperEndMinimumQ16
<< ",\"taper_end_max_q16\":" << recipe.taperEndMaximumQ16
<< ",\"blend_radius_q\":" << recipe.blendRadiusSubvoxels
<< ",\"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;
out << ",\"blend_radius_q\":" << recipe.blendRadiusSubvoxels
<< ",\"contour_warp_q\":" << recipe.contourWarpSubvoxels
<< ",\"roughness_q\":" << recipe.roughnessSubvoxels
<< ",\"boundary_seal_q\":" << recipe.boundarySealSubvoxels
@@ -961,11 +1139,40 @@ std::optional<FloatingIslandManifestV1> PlanFloatingIslandCluster(
radii[i] = recipe.minimumRadiusSubvoxels
+ (recipe.maximumRadiusSubvoxels - recipe.minimumRadiusSubvoxels) * radiusRoll / 65535;
}
std::vector<std::array<std::int64_t, 2>> primaryXY(count);
const std::int64_t rowJitter = recipe.spacingSubvoxels / 10;
for (std::uint32_t i = 0; i < count; ++i)
{
const std::uint64_t site = 0x100000000ull + i;
if (recipe.geometryVersion < 2)
{
const std::int64_t yOffset = i == middle ? 0
: floating_island_detail::SignedRange(recipe.seed, site, 0x594a4954544552ull, rowJitter);
primaryXY[i] = {recipe.layoutOriginXSubvoxels
- static_cast<std::int64_t>(middle) * recipe.spacingSubvoxels
+ static_cast<std::int64_t>(i) * recipe.spacingSubvoxels,
recipe.layoutOriginYSubvoxels + yOffset};
}
else
{
const std::int64_t along = (static_cast<std::int64_t>(i)
- static_cast<std::int64_t>(middle)) * recipe.spacingSubvoxels * 7 / 10;
const std::int64_t side = (i % 2 == 0 ? 1 : -1) * recipe.spacingSubvoxels / 3;
const std::int64_t jitter = floating_island_detail::SignedRange(
recipe.seed, site, 0x594a4954544552ull, rowJitter / 2);
primaryXY[i] = {recipe.layoutOriginXSubvoxels + along,
recipe.layoutOriginYSubvoxels + side + jitter};
}
}
const double maxGrade = floating_island_detail::Q16ToDouble(recipe.routeConstraints.maximumGradeQ16);
const double levelFactor = floating_island_detail::Q16ToDouble(recipe.verticalJitterQ16) * 1.5;
const auto riseLimit = [&](std::uint32_t a, std::uint32_t b)
{
const double gap = static_cast<double>(recipe.spacingSubvoxels)
const double centerDistance = recipe.geometryVersion < 2
? static_cast<double>(recipe.spacingSubvoxels)
: std::hypot(static_cast<double>(primaryXY[b][0] - primaryXY[a][0]),
static_cast<double>(primaryXY[b][1] - primaryXY[a][1]));
const double gap = centerDistance
- 0.78 * (static_cast<double>(radii[a]) + static_cast<double>(radii[b]));
return std::max(0.0, gap * maxGrade * levelFactor);
};
@@ -983,22 +1190,16 @@ std::optional<FloatingIslandManifestV1> PlanFloatingIslandCluster(
static_cast<std::int64_t>(std::ceil(riseLimit(static_cast<std::uint32_t>(i),
static_cast<std::uint32_t>(i + 1)))));
}
const std::int64_t rowJitter = recipe.spacingSubvoxels / 10;
const std::int64_t startX = recipe.layoutOriginXSubvoxels
- static_cast<std::int64_t>(middle) * recipe.spacingSubvoxels;
for (std::uint32_t i = 0; i < count; ++i)
{
const std::uint64_t site = 0x100000000ull + i;
const std::int64_t yOffset = i == middle ? 0
: floating_island_detail::SignedRange(recipe.seed, site, 0x594a4954544552ull, rowJitter);
auto island = floating_island_detail::MakeIsland(recipe, i,
startX + static_cast<std::int64_t>(i) * recipe.spacingSubvoxels,
recipe.layoutOriginYSubvoxels + yOffset, tops[i], site);
primaryXY[i][0], primaryXY[i][1], tops[i], site);
island.radiusSubvoxels = radii[i];
island.topHalfSubvoxels = std::max<std::int64_t>(1, island.radiusSubvoxels / 5);
island.centerSubvoxels[2] = island.topZSubvoxels - island.topHalfSubvoxels;
island.bottomZSubvoxels = island.topZSubvoxels
- island.radiusSubvoxels * recipe.thicknessRatioQ16 / floating_island_detail::Q16;
island.bottomZSubvoxels = floating_island_detail::IslandBottom(recipe,
island.topZSubvoxels, island.radiusSubvoxels);
floating_island_detail::SetIslandBounds(recipe, island);
islands.push_back(island);
}
@@ -1016,26 +1217,41 @@ std::optional<FloatingIslandManifestV1> PlanFloatingIslandCluster(
const std::uint64_t site = 0x300000000ull + branch;
if (floating_island_detail::UnitQ16(recipe.seed, site, 0x53495445524f4c4cull)
>= static_cast<std::uint32_t>(recipe.islandDensityQ16)) continue;
const std::uint32_t hostIndex = branch == 0 ? 0 : count - 1;
const std::uint32_t hostIndex = recipe.geometryVersion < 2
? (branch == 0 ? 0 : count - 1)
: (branch == 0 ? middle : std::min(middle + 1, count - 1));
const auto& host = islands[hostIndex];
const std::int64_t xOffset = recipe.geometryVersion < 2 ? 0
: (branch == 0 ? -1 : 1) * recipe.spacingSubvoxels * 9 / 20;
const std::int64_t yOffset = branch == 0 ? -recipe.spacingSubvoxels : recipe.spacingSubvoxels;
const std::int64_t gap = recipe.spacingSubvoxels - static_cast<std::int64_t>(
std::llround(0.78 * static_cast<double>(host.radiusSubvoxels + recipe.minimumRadiusSubvoxels)));
const std::int64_t rise = static_cast<std::int64_t>(std::ceil(std::max(0.0,
static_cast<double>(gap) * maxGrade * levelFactor)));
const std::int64_t topZ = host.topZSubvoxels
+ floating_island_detail::SignedRange(recipe.seed, site, 0x4252414e43484c56ull, rise);
const std::int64_t sideRise = recipe.geometryVersion < 2 ? rise
: std::min<std::int64_t>(24 * floating_island_detail::Q,
(recipe.layoutMaximumZSubvoxels - recipe.layoutMinimumZSubvoxels)
* recipe.verticalJitterQ16 / (8 * floating_island_detail::Q16));
std::int64_t topZ = host.topZSubvoxels
+ floating_island_detail::SignedRange(recipe.seed, site, 0x4252414e43484c56ull, sideRise);
const std::uint32_t radiusRoll = floating_island_detail::UnitQ16(recipe.seed, site, 0x524144495553ull);
const std::int64_t branchRadius = recipe.minimumRadiusSubvoxels
+ (recipe.maximumRadiusSubvoxels - recipe.minimumRadiusSubvoxels) * radiusRoll / 65535;
if (recipe.geometryVersion >= 2)
{
const std::int64_t maximumTop = recipe.layoutMaximumZSubvoxels
- recipe.boundarySealSubvoxels - branchRadius / 5;
const std::int64_t minimumTop = recipe.layoutMinimumZSubvoxels
+ recipe.boundarySealSubvoxels + floating_island_detail::IslandBottom(recipe, 0, branchRadius) * -1;
topZ = std::clamp(topZ, minimumTop, maximumTop);
}
auto island = floating_island_detail::MakeIsland(recipe,
recipe.primaryIslandCount + branch,
host.centerSubvoxels[0], host.centerSubvoxels[1] + yOffset, topZ, site);
host.centerSubvoxels[0] + xOffset, host.centerSubvoxels[1] + yOffset, topZ, site);
island.radiusSubvoxels = branchRadius;
island.topHalfSubvoxels = std::max<std::int64_t>(1, branchRadius / 5);
island.centerSubvoxels[2] = topZ - island.topHalfSubvoxels;
island.bottomZSubvoxels = topZ - branchRadius * recipe.thicknessRatioQ16
/ floating_island_detail::Q16;
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);
@@ -1074,7 +1290,8 @@ std::optional<FloatingIslandManifestV1> PlanFloatingIslandCluster(
point.stableFeatureId = floating_island_detail::StableId(recipe.seed,
0x500000000ull + routeOrdinal, 0x524f555445ull);
point.positionSubvoxels = {x, y, *surface + capsuleHalf
+ recipe.routeConstraints.minimumClearanceSubvoxels};
+ recipe.routeConstraints.minimumClearanceSubvoxels
+ (recipe.geometryVersion >= 2 ? floating_island_detail::Q / 8 : 0)};
point.radiusSubvoxels = routeRadius;
route.push_back(point);
++routeOrdinal;
@@ -1471,4 +1688,37 @@ FloatingIslandRouteAuditResult AuditFloatingIslandRoute(const FloatingIslandFiel
return result;
}
FloatingIslandThicknessAuditResult AuditFloatingIslandThickness(
const FloatingIslandPlanV1& plan, double minimumDepthToDiameterRatio)
{
FloatingIslandThicknessAuditResult result;
result.requiredDepthToDiameterRatio = minimumDepthToDiameterRatio;
if (plan.islands.empty() || !std::isfinite(minimumDepthToDiameterRatio)
|| minimumDepthToDiameterRatio <= 0.0) return result;
result.minimumDepthToDiameterRatio = std::numeric_limits<double>::infinity();
for (const auto& island : plan.islands)
{
++result.islandsTested;
if (island.radiusSubvoxels <= 0 || island.topZSubvoxels <= island.bottomZSubvoxels)
{
++result.islandsBelowMinimum;
result.minimumDepthToDiameterRatio = 0.0;
result.minimumIslandStableId = island.stableId;
continue;
}
const double ratio = static_cast<double>(island.topZSubvoxels - island.bottomZSubvoxels)
/ (2.0 * static_cast<double>(island.radiusSubvoxels));
if (ratio < result.minimumDepthToDiameterRatio)
{
result.minimumDepthToDiameterRatio = ratio;
result.minimumIslandStableId = island.stableId;
}
if (ratio + 1.0e-12 < minimumDepthToDiameterRatio) ++result.islandsBelowMinimum;
}
if (!std::isfinite(result.minimumDepthToDiameterRatio)) result.minimumDepthToDiameterRatio = 0.0;
result.passed = result.islandsTested == plan.islands.size()
&& result.islandsBelowMinimum == 0;
return result;
}
}