wip(assembler): round S1 -- one Core voxel-size source; per-tile feature lists (islands)

AVoxelWorld::CoreVoxelSizeCm (default 25, 32 voxels/tile) flows through plan, field,
mesher, content, water, atmosphere, edits and collision. Staged-owner BuildHost passes at
20 cm (SurfaceWorld + islands, W1/W8/W16, anchors, BP edits, collision) with a fixed-25cm
negative control; default-size hashes match round 4; Core off stays legacy 12.5 cm.
Reusable BuildWorldAssemblyFeatureTileListV1; wired into FloatingIslands only so far.
256-island stress: tile p95 112.5 -> 2.0 ms, 0 mismatches, dropped-feature control fails.
Content placement measured (no populated cells): island ground search 7.25 ms/cell vs
legacy 0.29 ms -- open. Outside-review evaluation in Round5/REPORT.md.

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-18 18:48:38 +02:00
1 parent 111cf3d262
commit d6e69420e8
25 files changed
+2136 -250

No files matched your search

+284 -4
View File
@@ -9,6 +9,7 @@
#include <cmath>
#include <cstdint>
#include <limits>
#include <numeric>
#include <sstream>
#include <string_view>
#include <unordered_map>
@@ -258,14 +259,20 @@ namespace floating_island_detail
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 != 2)
|| (recipe.geometryVersion == 1
&& (recipe.crustDepthBonusQ16 != 0 || recipe.rootDepthRatioQ16 != 0))
|| recipe.unitsPerVoxel != Q
|| recipe.ownerVoxelSizeMicrons != 125000
|| recipe.coreVoxelSizeMicrons != 250000
|| recipe.ownerToCoreScaleNumerator != 1
|| recipe.ownerToCoreScaleDenominator != 2
|| recipe.coreVoxelSizeMicrons < 125000 || recipe.coreVoxelSizeMicrons > 250000
|| recipe.ownerToCoreScaleNumerator != ExpectedNumerator
|| recipe.ownerToCoreScaleDenominator != ExpectedDenominator
|| recipe.primaryIslandCount < 3 || recipe.primaryIslandCount > 9
|| recipe.layoutMaximumZSubvoxels <= recipe.layoutMinimumZSubvoxels
|| recipe.layoutMaximumZSubvoxels - recipe.layoutMinimumZSubvoxels > 2048 * Q
@@ -742,6 +749,103 @@ namespace floating_island_detail
return std::clamp(RoundQ(result), -MaxDensity, MaxDensity);
}
inline std::int64_t FieldDensityForTile(const FloatingIslandRecipeV1& recipe,
const FloatingIslandPlanV1& plan, std::int64_t x, std::int64_t y, std::int64_t z,
const FloatingIslandTileFeatureListV1& featureList)
{
if (plan.islands.empty()) return MaxDensity;
if (featureList.groupBounds.size() != plan.islands.size())
return FieldDensity(recipe, plan, x, y, z);
const auto& featureIndices = featureList.featureIndices;
const std::size_t islandCount = plan.islands.size();
const double blend = static_cast<double>(recipe.blendRadiusSubvoxels);
double result = static_cast<double>(MaxDensity);
const auto evaluateLocalGroup = [&](std::size_t islandIndex)
{
const auto& island = plan.islands[islandIndex];
double group = IslandDensity(recipe, island,
static_cast<double>(x), static_cast<double>(y), static_cast<double>(z));
for (const std::uint32_t edgeIndex : featureIndices)
{
if (edgeIndex < islandCount) continue;
const std::size_t planIndex = static_cast<std::size_t>(edgeIndex) - islandCount;
if (planIndex >= plan.clusterEdges.size()) continue;
const auto& edge = plan.clusterEdges[planIndex];
if (edge.fromIslandId != island.stableId) continue;
bool topSurface = false;
double alongFraction = 0.0;
double connector = ConnectorDensity(recipe, edge,
static_cast<double>(x), static_cast<double>(y), static_cast<double>(z),
&topSurface, &alongFraction);
if (recipe.geometryVersion >= 2)
connector = ApplySurfaceDetail(recipe, edge.stableId, connector,
static_cast<double>(x), static_cast<double>(y), static_cast<double>(z),
topSurface || alongFraction < 0.12 || alongFraction > 0.88 ? 0.25 : 1.0);
group = SmoothMin(group, connector, blend);
}
return group;
};
const auto evaluateDistantGroup = [&](std::size_t islandIndex)
{
const auto& island = plan.islands[islandIndex];
double group = IslandDensity(recipe, island,
static_cast<double>(x), static_cast<double>(y), static_cast<double>(z));
for (const auto& edge : plan.clusterEdges)
{
if (edge.fromIslandId != island.stableId) continue;
bool topSurface = false;
double alongFraction = 0.0;
double connector = ConnectorDensity(recipe, edge,
static_cast<double>(x), static_cast<double>(y), static_cast<double>(z),
&topSurface, &alongFraction);
if (recipe.geometryVersion >= 2)
connector = ApplySurfaceDetail(recipe, edge.stableId, connector,
static_cast<double>(x), static_cast<double>(y), static_cast<double>(z),
topSurface || alongFraction < 0.12 || alongFraction > 0.88 ? 0.25 : 1.0);
group = SmoothMin(group, connector, blend);
}
return group;
};
// Each local group keeps its complete ordered connector chain in the tile list.
// Les connecteurs d'un groupe local restent complets et dans leur ordre canonique.
for (const std::uint32_t index : featureIndices)
{
if (index >= islandCount) break;
result = std::min(result, evaluateLocalGroup(index));
}
// Air-side density magnitudes are part of the canonical field too. The per-tile list
// handles nearby groups; conservative group-box lower bounds prune distant exact SDFs.
// Les magnitudes d'air restent canoniques; les bornes de groupes eliminent les SDF lointains.
if (result >= 0.0)
{
if ((featureList.groupSearchOrder.size() != islandCount
|| featureList.groupDistanceLowerBoundsSubvoxels.size() != islandCount)
&& featureList.groupSearchComplete)
return FieldDensity(recipe, plan, x, y, z);
const std::size_t searchCount = std::min(featureList.groupSearchOrder.size(),
featureList.groupDistanceLowerBoundsSubvoxels.size());
for (std::size_t orderIndex = 0; orderIndex < searchCount; ++orderIndex)
{
const std::size_t islandIndex = featureList.groupSearchOrder[orderIndex];
if (islandIndex >= islandCount) return FieldDensity(recipe, plan, x, y, z);
if (std::binary_search(featureIndices.begin(), featureIndices.end(),
static_cast<std::uint32_t>(islandIndex))) continue;
const auto& bounds = featureList.groupBounds[islandIndex];
for (std::size_t axis = 0; axis < 3; ++axis)
if (bounds.minimumSubvoxels[axis] > bounds.maximumSubvoxels[axis])
return FieldDensity(recipe, plan, x, y, z);
if (static_cast<double>(featureList.groupDistanceLowerBoundsSubvoxels[orderIndex]) > result)
break;
result = std::min(result, evaluateDistantGroup(islandIndex));
}
}
return std::clamp(RoundQ(result), -MaxDensity, MaxDensity);
}
inline void SetIslandBounds(const FloatingIslandRecipeV1& recipe,
FloatingIslandAnchorV1& island)
{
@@ -1528,7 +1632,7 @@ 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() > recipe_->primaryIslandCount + 2
|| 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)
@@ -1547,6 +1651,182 @@ std::int64_t FloatingIslandFieldV1::densitySubvoxels(std::int64_t x,
return floating_island_detail::FieldDensity(*recipe_, *plan_, x, y, z);
}
FloatingIslandTileFeatureListV1 FloatingIslandFieldV1::buildFeatureTileList(
const std::array<std::int64_t, 3>& tileMinimumSubvoxels,
const std::array<std::int64_t, 3>& tileMaximumSubvoxels) const
{
FloatingIslandTileFeatureListV1 result;
if (!isValid()) return result;
std::vector<WorldAssemblyFeatureBoundsV1> features;
features.reserve(plan_->islands.size() + plan_->clusterEdges.size());
for (const auto& island : plan_->islands)
features.push_back({island.stableId, island.influenceMinimumSubvoxels,
island.influenceMaximumSubvoxels});
for (const auto& edge : plan_->clusterEdges)
features.push_back({edge.stableId, edge.influenceMinimumSubvoxels,
edge.influenceMaximumSubvoxels});
result.featureIndices = BuildWorldAssemblyFeatureTileListV1(features,
tileMinimumSubvoxels, tileMaximumSubvoxels);
const std::size_t islandCount = plan_->islands.size();
result.groupBounds.resize(islandCount);
for (std::size_t islandIndex = 0; islandIndex < islandCount; ++islandIndex)
{
result.groupBounds[islandIndex].minimumSubvoxels = plan_->islands[islandIndex].influenceMinimumSubvoxels;
result.groupBounds[islandIndex].maximumSubvoxels = plan_->islands[islandIndex].influenceMaximumSubvoxels;
}
const auto findSourceIsland = [&](const FloatingIslandConnectorV1& edge)
{
return std::find_if(plan_->islands.begin(), plan_->islands.end(),
[&edge](const auto& island) { return island.stableId == edge.fromIslandId; });
};
for (const auto& edge : plan_->clusterEdges)
{
const auto source = findSourceIsland(edge);
if (source == plan_->islands.end())
{ result.groupBounds.clear(); result.featureIndices.clear(); return result; }
auto& group = result.groupBounds[static_cast<std::size_t>(source - plan_->islands.begin())];
for (std::size_t axis = 0; axis < 3; ++axis)
{
group.minimumSubvoxels[axis] = std::min(group.minimumSubvoxels[axis],
edge.influenceMinimumSubvoxels[axis]);
group.maximumSubvoxels[axis] = std::max(group.maximumSubvoxels[axis],
edge.influenceMaximumSubvoxels[axis]);
}
}
result.groupSearchOrder.resize(islandCount);
std::iota(result.groupSearchOrder.begin(), result.groupSearchOrder.end(), 0u);
const auto tileDistanceLowerBound = [&](
const std::array<std::int64_t, 3>& featureMinimum,
const std::array<std::int64_t, 3>& featureMaximum)
{
std::int64_t maximumGap = 0;
for (std::size_t axis = 0; axis < 3; ++axis)
{
if (featureMinimum[axis] > tileMaximumSubvoxels[axis])
maximumGap = std::max(maximumGap,
featureMinimum[axis] - tileMaximumSubvoxels[axis]);
else if (featureMaximum[axis] < tileMinimumSubvoxels[axis])
maximumGap = std::max(maximumGap,
tileMinimumSubvoxels[axis] - featureMaximum[axis]);
}
return maximumGap;
};
result.groupDistanceLowerBoundsSubvoxels.resize(islandCount);
for (std::size_t islandIndex = 0; islandIndex < islandCount; ++islandIndex)
{
const auto& island = plan_->islands[islandIndex];
const std::int64_t islandGap = tileDistanceLowerBound(
island.influenceMinimumSubvoxels, island.influenceMaximumSubvoxels);
double islandLowerBound = static_cast<double>(islandGap)
- floating_island_detail::IslandBlendMargin(*recipe_) - recipe_->roughnessSubvoxels;
if (recipe_->geometryVersion >= 2)
{
// Root lobes use a scaled ellipsoid distance, so their positive values can be
// substantially smaller than distance to the island AABB. Supported root radii
// guarantee minAxis/maxAxis >= 0.22; include their smooth-min budget before pruning.
// Les racines utilisent une distance d'ellipsoide mise a l'echelle; borner leur pente.
const double radius = static_cast<double>(island.radiusSubvoxels);
const double rootBlend = std::clamp(radius * 0.12,
2.0 * floating_island_detail::Q, 5.0 * floating_island_detail::Q);
const double rootSmoothBudget = static_cast<double>(
floating_island_detail::RootCount(*recipe_, island)) * rootBlend * 0.25;
const double rootLowerBound = 0.22 * static_cast<double>(islandGap)
- recipe_->roughnessSubvoxels - rootSmoothBudget;
islandLowerBound = std::min(islandLowerBound, rootLowerBound);
}
double groupLowerBound = islandLowerBound;
for (const auto& edge : plan_->clusterEdges)
{
if (edge.fromIslandId != island.stableId) continue;
const std::int64_t edgeGap = tileDistanceLowerBound(
edge.influenceMinimumSubvoxels, edge.influenceMaximumSubvoxels);
const double edgeLowerBound = static_cast<double>(edgeGap) - recipe_->roughnessSubvoxels;
groupLowerBound = std::min(groupLowerBound, edgeLowerBound)
- static_cast<double>(recipe_->blendRadiusSubvoxels) * 0.25;
}
// Floor with one subvoxel of slack so quantization cannot make a lower bound optimistic.
// Arrondir vers le bas avec une marge pour ne jamais surestimer la borne.
result.groupDistanceLowerBoundsSubvoxels[islandIndex] =
static_cast<std::int64_t>(std::floor(groupLowerBound)) - 1;
}
std::sort(result.groupSearchOrder.begin(), result.groupSearchOrder.end(),
[&result](std::uint32_t a, std::uint32_t b)
{
const auto distanceA = result.groupDistanceLowerBoundsSubvoxels[a];
const auto distanceB = result.groupDistanceLowerBoundsSubvoxels[b];
return distanceA == distanceB ? a < b : distanceA < distanceB;
});
// Store distance in search order so query samples use one sequential early-out walk.
// Ranger les distances dans le meme ordre pour arreter le parcours au premier echec.
std::vector<std::int64_t> orderedDistances;
orderedDistances.reserve(islandCount);
for (const std::uint32_t islandIndex : result.groupSearchOrder)
orderedDistances.push_back(result.groupDistanceLowerBoundsSubvoxels[islandIndex]);
result.groupDistanceLowerBoundsSubvoxels = std::move(orderedDistances);
std::vector<std::uint8_t> selectedGroups(islandCount, 0);
for (const std::uint32_t featureIndex : result.featureIndices)
{
if (featureIndex < islandCount) selectedGroups[featureIndex] = 1;
else
{
const std::size_t edgeIndex = static_cast<std::size_t>(featureIndex) - islandCount;
if (edgeIndex >= plan_->clusterEdges.size())
{ result.groupBounds.clear(); result.featureIndices.clear(); return result; }
const auto source = findSourceIsland(plan_->clusterEdges[edgeIndex]);
if (source == plan_->islands.end())
{ result.groupBounds.clear(); result.featureIndices.clear(); return result; }
selectedGroups[static_cast<std::size_t>(source - plan_->islands.begin())] = 1;
}
}
// A connector is composed into its source island's group. Keep that source and every
// outgoing connector in the tile list so the ordered smooth-min chain remains exact.
// Un connecteur fait partie de son ile source : garder toute la chaine de fondu exacte.
for (std::size_t islandIndex = 0; islandIndex < islandCount; ++islandIndex)
{
if (!selectedGroups[islandIndex]) continue;
auto insertSorted = [&](std::uint32_t value)
{
const auto insertion = std::lower_bound(result.featureIndices.begin(),
result.featureIndices.end(), value);
if (insertion == result.featureIndices.end() || *insertion != value)
result.featureIndices.insert(insertion, value);
};
insertSorted(static_cast<std::uint32_t>(islandIndex));
for (std::size_t edgeIndex = 0; edgeIndex < plan_->clusterEdges.size(); ++edgeIndex)
if (plan_->clusterEdges[edgeIndex].fromIslandId == plan_->islands[islandIndex].stableId)
insertSorted(static_cast<std::uint32_t>(islandCount + edgeIndex));
}
return result;
}
std::int64_t FloatingIslandFieldV1::densitySubvoxelsForTile(std::int64_t x,
std::int64_t y, std::int64_t z,
const FloatingIslandTileFeatureListV1& featureList) const noexcept
{
if (!isValid() || !floating_island_detail::ValidScalar(x)
|| !floating_island_detail::ValidScalar(y) || !floating_island_detail::ValidScalar(z))
return floating_island_detail::MaxDensity;
std::uint32_t previous = 0;
bool first = true;
const std::size_t featureCount = plan_->islands.size() + plan_->clusterEdges.size();
if (featureList.groupBounds.size() != plan_->islands.size())
return floating_island_detail::FieldDensity(*recipe_, *plan_, x, y, z);
for (const std::uint32_t index : featureList.featureIndices)
{
if (index >= featureCount || (!first && index <= previous))
return floating_island_detail::FieldDensity(*recipe_, *plan_, x, y, z);
first = false;
previous = index;
}
return floating_island_detail::FieldDensityForTile(*recipe_, *plan_, x, y, z, featureList);
}
FieldInterval FloatingIslandFieldV1::interval(std::int64_t minX, std::int64_t minY,
std::int64_t minZ, std::int64_t maxX, std::int64_t maxY, std::int64_t maxZ) const noexcept
{