wip(assembler): round S2 (Sol) -- shared per-tile feature queries for every Core family; island placement 30x faster

Island-only worker context replaced by a shared worker-local candidate domain
(WorldAssemblyFeatureTileListV1 / ScopedWorldAssemblyFeatureQueriesV1) serving
SurfaceWorld, FloatingIslands and far-shell queries; a per-domain cost hint keeps the
full scan on small plans. Content placement prepares the same queries and skips proven
air: owner island cell p95 7.07 -> 0.24 ms; populated fixture 572 exact placements,
3.27 s -> 29 ms. Full/candidate hashes equal with dropped-feature controls on both
families; S1 default W1/W8/W16 hashes reproduced; Core-off and M2 fixtures pass.
Default contract for future families in ARCHITECTURE 8.23. Run on gpt-6.1-sol medium.

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 19:50:45 +02:00
1 parent d6e69420e8
commit 6ddb834b1c
19 files changed
+1849 -132

No files matched your search

@@ -56,6 +56,18 @@ namespace VoxelForgeCore
const std::array<std::int64_t, 3>& tileMinimumSubvoxels,
const std::array<std::int64_t, 3>& tileMaximumSubvoxels);
struct WorldAssemblyFeatureColumnRangeV1
{
bool known = false;
bool empty = false;
std::int64_t minimumZSubvoxels = 0, maximumZSubvoxels = 0;
};
// Only families whose base is air may use this solid-support union to shorten a march.
// Reserve aux familles a fond d'air : union conservative du support solide de la colonne.
VFCORE_API WorldAssemblyFeatureColumnRangeV1 BuildWorldAssemblyFeatureColumnRangeV1(
std::span<const WorldAssemblyFeatureBoundsV1> solidSupportBounds,
std::int64_t xSubvoxels, std::int64_t ySubvoxels);
struct VFCORE_API TileKeyHash
{
std::size_t operator()(const TileKey& key) const noexcept;
@@ -114,6 +126,8 @@ namespace VoxelForgeCore
double classificationMilliseconds = 0.0;
double columnComputeMilliseconds = 0.0;
double meshingMilliseconds = 0.0;
double featureListMilliseconds = 0.0;
double farShellMilliseconds = 0.0;
bool tileWasClassified = false;
bool meshingSkipped = false;
bool featureRefinedResolution = false;
@@ -386,25 +400,60 @@ namespace VoxelForgeCore
std::uint64_t contentHash = 0;
};
struct FloatingIslandFeatureGroupBoundsV1
struct WorldAssemblyFeatureGroupBoundsV1
{
std::array<std::int64_t, 3> minimumSubvoxels{};
std::array<std::int64_t, 3> maximumSubvoxels{};
};
struct FloatingIslandTileFeatureListV1
struct WorldAssemblyFeatureTileListV1
{
// Flat stable-order IDs for island and connector features that touch this tile.
// Identifiants ordonnes des features qui touchent ce carreau.
// Stable recipe-order indices for features that touch this query domain.
// Indices dans l'ordre de recette des features touchant ce domaine.
std::vector<std::uint32_t> featureIndices;
// Cheap conservative group bounds preserve exact positive SDF distances while distant
// feature evaluators are skipped. Bornes legeres pour garder les distances SDF exactes.
std::vector<FloatingIslandFeatureGroupBoundsV1> groupBounds;
// Optional ordered-union group certificates preserve exact positive density values.
// Certificats optionnels de groupes d'union pour conserver les valeurs positives exactes.
std::vector<WorldAssemblyFeatureGroupBoundsV1> groupBounds;
// Groups are ordered by a tile-wide conservative distance lower bound. Samples stop at
// the first group that cannot beat their current density. Ordre par distance minimale.
std::vector<std::uint32_t> groupSearchOrder;
std::vector<std::int64_t> groupDistanceLowerBoundsSubvoxels;
bool groupSearchComplete = true;
// Performance hint only: the shared engine may prefer the exact full scan on small plans.
// Indication de cout seulement : le moteur peut preferer le parcours complet exact.
bool candidateEvaluationUseful = true;
// Exact-domain guard: outside queries always fall back to the canonical full scan.
// Hors du domaine prepare, revenir au parcours complet canonique.
std::array<std::int64_t, 3> minimumSubvoxels{};
std::array<std::int64_t, 3> maximumSubvoxels{};
};
using FloatingIslandTileFeatureListV1 = WorldAssemblyFeatureTileListV1;
using FloatingIslandFeatureGroupBoundsV1 = WorldAssemblyFeatureGroupBoundsV1;
struct WorldAssemblyFeatureQueryV1
{
const void* recipe = nullptr;
const void* plan = nullptr;
WorldAssemblyFeatureTileListV1 features;
};
// One worker-local, nestable scope for meshing, shells and placement in every family.
// Une portee locale au worker, emboitable, pour toutes les familles et tous les consommateurs.
class VFCORE_API ScopedWorldAssemblyFeatureQueriesV1
{
public:
explicit ScopedWorldAssemblyFeatureQueriesV1(std::span<const WorldAssemblyFeatureQueryV1> queries,
bool forceCandidatesForValidation = false) noexcept;
~ScopedWorldAssemblyFeatureQueriesV1();
ScopedWorldAssemblyFeatureQueriesV1(const ScopedWorldAssemblyFeatureQueriesV1&) = delete;
ScopedWorldAssemblyFeatureQueriesV1& operator=(const ScopedWorldAssemblyFeatureQueriesV1&) = delete;
static const WorldAssemblyFeatureTileListV1* find(const void* recipe, const void* plan,
std::int64_t x, std::int64_t y, std::int64_t z, bool columnOnly = false) noexcept;
private:
std::span<const WorldAssemblyFeatureQueryV1> queries_;
bool forceCandidates_ = false;
const ScopedWorldAssemblyFeatureQueriesV1* previous_ = nullptr;
};
struct VFCORE_API FloatingIslandManifestV1
@@ -510,6 +559,7 @@ namespace VoxelForgeCore
FloatingIslandTileFeatureListV1 buildFeatureTileList(
const std::array<std::int64_t, 3>& tileMinimumSubvoxels,
const std::array<std::int64_t, 3>& tileMaximumSubvoxels) const;
std::vector<WorldAssemblyFeatureBoundsV1> solidSupportBounds() const;
std::int64_t densitySubvoxelsForTile(std::int64_t xSubvoxels,
std::int64_t ySubvoxels, std::int64_t zSubvoxels,
const FloatingIslandTileFeatureListV1& featureList) const noexcept;
@@ -601,6 +651,9 @@ namespace VoxelForgeCore
bool isValid() const noexcept;
HeightfieldPointSample point(std::int64_t xSubvoxels, std::int64_t ySubvoxels,
std::int64_t zSubvoxels) const noexcept;
WorldAssemblyFeatureTileListV1 buildFeatureTileList(
const std::array<std::int64_t, 3>& tileMinimumSubvoxels,
const std::array<std::int64_t, 3>& tileMaximumSubvoxels) const;
HeightfieldBoxClass classifyLatticeBox(std::int64_t minimumXSubvoxels,
std::int64_t minimumYSubvoxels, std::int64_t minimumZSubvoxels,
std::int64_t maximumXSubvoxels, std::int64_t maximumYSubvoxels,
@@ -928,6 +981,9 @@ namespace VoxelForgeCore
bool skipUv = false;
bool skipColors = false;
bool skipMaterialGroups = false;
// Validation control only; never participates in recipe/world identity.
// Controle de validation seulement, sans effet sur l'identite du monde.
bool useAssemblyFeatureQueries = true;
};
class VFCORE_API World
+21 -5
View File
@@ -1645,18 +1645,18 @@ bool FloatingIslandFieldV1::isValid() const noexcept
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);
}
FloatingIslandTileFeatureListV1 FloatingIslandFieldV1::buildFeatureTileList(
const std::array<std::int64_t, 3>& tileMinimumSubvoxels,
const std::array<std::int64_t, 3>& tileMaximumSubvoxels) const
std::vector<WorldAssemblyFeatureBoundsV1> FloatingIslandFieldV1::solidSupportBounds() 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)
@@ -1666,6 +1666,22 @@ FloatingIslandTileFeatureListV1 FloatingIslandFieldV1::buildFeatureTileList(
features.push_back({edge.stableId, edge.influenceMinimumSubvoxels,
edge.influenceMaximumSubvoxels});
return features;
}
FloatingIslandTileFeatureListV1 FloatingIslandFieldV1::buildFeatureTileList(
const std::array<std::int64_t, 3>& tileMinimumSubvoxels,
const std::array<std::int64_t, 3>& tileMaximumSubvoxels) const
{
FloatingIslandTileFeatureListV1 result;
// The measured four-island plan is cheaper to scan; larger plans amortize group certificates.
// Le plan mesure de quatre iles coute moins en parcours complet; les grands plans amortissent les bornes.
result.candidateEvaluationUseful=plan_->islands.size()>4;
result.minimumSubvoxels = tileMinimumSubvoxels;
result.maximumSubvoxels = tileMaximumSubvoxels;
if (!isValid()) return result;
const auto features = solidSupportBounds();
result.featureIndices = BuildWorldAssemblyFeatureTileListV1(features,
tileMinimumSubvoxels, tileMaximumSubvoxels);
const std::size_t islandCount = plan_->islands.size();
+1 -50
View File
@@ -32,43 +32,6 @@
class FStandaloneShapeWorld
{
public:
struct FNativeIslandTileFeatureContext
{
const VoxelForgeCore::FloatingIslandPlanV1* Plan = nullptr;
const VoxelForgeCore::FloatingIslandTileFeatureListV1* FeatureList = nullptr;
};
class FScopedNativeIslandTileFeatureContext
{
public:
FScopedNativeIslandTileFeatureContext(const VoxelForgeCore::FloatingIslandPlanV1* plan,
const VoxelForgeCore::FloatingIslandTileFeatureListV1* featureList)
{
Context.Plan = plan;
Context.FeatureList = featureList;
Previous = SetNativeIslandTileFeatureContext(plan && featureList ? &Context : nullptr);
}
~FScopedNativeIslandTileFeatureContext()
{ RestoreNativeIslandTileFeatureContext(Previous); }
FScopedNativeIslandTileFeatureContext(const FScopedNativeIslandTileFeatureContext&) = delete;
FScopedNativeIslandTileFeatureContext& operator=(const FScopedNativeIslandTileFeatureContext&) = delete;
private:
FNativeIslandTileFeatureContext Context{};
const FNativeIslandTileFeatureContext* Previous = nullptr;
};
static const FNativeIslandTileFeatureContext* SetNativeIslandTileFeatureContext(
const FNativeIslandTileFeatureContext* context)
{
auto& active = ActiveNativeIslandTileFeatureContext();
const auto* previous = active;
active = context;
return previous;
}
static void RestoreNativeIslandTileFeatureContext(const FNativeIslandTileFeatureContext* context)
{ ActiveNativeIslandTileFeatureContext() = context; }
static const FNativeIslandTileFeatureContext* GetNativeIslandTileFeatureContext()
{ return ActiveNativeIslandTileFeatureContext(); }
double GetCoreVoxelSizeCm() const
{
return Owner == nullptr || Owner->CoreVoxelSizeMicrons == 0
@@ -1140,12 +1103,6 @@ public:
}
private:
static const FNativeIslandTileFeatureContext*& ActiveNativeIslandTileFeatureContext()
{
static thread_local const FNativeIslandTileFeatureContext* Context = nullptr;
return Context;
}
static uint64 PackLatticeKey(int32 X,int32 Y,int32 Z)
{
constexpr uint64 Mask=0x1FFFFFull;
@@ -1961,13 +1918,7 @@ private:
const VoxelForgeCore::FloatingIslandFieldV1 Field(
Plan->manifest.recipe,Plan->manifest.plan);
if(!Field.isValid())return false;
std::int64_t Density=0;
const auto* TileContext=GetNativeIslandTileFeatureContext();
if(TileContext&&TileContext->Plan==&Plan->manifest.plan&&TileContext->FeatureList)
Density=Field.densitySubvoxelsForTile(ToFixed(X),ToFixed(Y),ToFixed(Z),
*TileContext->FeatureList);
else
Density=Field.densitySubvoxels(ToFixed(X),ToFixed(Y),ToFixed(Z));
std::int64_t Density=Field.densitySubvoxels(ToFixed(X),ToFixed(Y),ToFixed(Z));
const std::int64_t Bottom=Plan->manifest.recipe.layoutMinimumZSubvoxels;
const std::int64_t Top=Plan->manifest.recipe.layoutMaximumZSubvoxels;
const std::int64_t Seal=Plan->manifest.recipe.boundarySealSubvoxels;
+32 -49
View File
@@ -1154,68 +1154,51 @@ namespace VoxelForgeCore
}
}
}
const WorldAssemblyFloatingIslandPlanV1* IslandTilePlan=nullptr;
FloatingIslandTileFeatureListV1 IslandTileFeatureList;
if(bHasWorldAssembly)
// Shared query domain includes the normal halo; all overlapping strate families participate.
// Domaine partage avec marge de normales; inclure toutes les familles des strates traversees.
const std::int64_t queryScale=WorldAssemblySubvoxelsPerVoxel;
const std::array<std::int64_t,3> queryMinimum={
(static_cast<std::int64_t>(origin.X)-step)*queryScale,
(static_cast<std::int64_t>(origin.Y)-step)*queryScale,
(static_cast<std::int64_t>(origin.Z)-step)*queryScale};
const std::array<std::int64_t,3> queryMaximum={
(static_cast<std::int64_t>(origin.X)+extent+step)*queryScale,
(static_cast<std::int64_t>(origin.Y)+extent+step)*queryScale,
(static_cast<std::int64_t>(origin.Z)+extent+step)*queryScale};
const auto FeatureListStart=std::chrono::steady_clock::now();
std::vector<WorldAssemblyFeatureQueryV1> featureQueries;
if(bHasWorldAssembly&&options.useAssemblyFeatureQueries)
{
for(const auto& plan:impl_->worldAssembly->plan.strates)
{
const Heightfield2D field(impl_->worldAssembly->recipe,plan);
featureQueries.push_back({field.recipe(),field.plan(),
field.buildFeatureTileList(queryMinimum,queryMaximum)});
}
for(const auto& entry:impl_->worldAssembly->floatingIslands)
{
const auto& scale=entry.manifest.recipe;
const std::int64_t ownerToCoreNumerator=scale.ownerToCoreScaleNumerator;
const std::int64_t ownerToCoreDenominator=scale.ownerToCoreScaleDenominator;
if(ownerToCoreNumerator<=0||ownerToCoreDenominator<=0)continue;
const std::int64_t coreVoxelsPerOwnerChunk=static_cast<std::int64_t>(CHUNK_SIZE)
*ownerToCoreNumerator/ownerToCoreDenominator;
if(coreVoxelsPerOwnerChunk<=0)continue;
const std::int64_t stratumBottom=static_cast<std::int64_t>(entry.bottomChunkZ)
*coreVoxelsPerOwnerChunk;
const std::int64_t stratumTop=(static_cast<std::int64_t>(entry.topChunkZ)+1)
*coreVoxelsPerOwnerChunk;
if(static_cast<std::int64_t>(origin.Z)>stratumTop
||static_cast<std::int64_t>(origin.Z)+extent<stratumBottom)continue;
IslandTilePlan=&entry;
break;
}
if(IslandTilePlan)
{
const FloatingIslandFieldV1 field(IslandTilePlan->manifest.recipe,
IslandTilePlan->manifest.plan);
const std::int64_t scale=WorldAssemblySubvoxelsPerVoxel;
// Include the mesher's one-sample gradient halo on each face.
// Inclure la marge d'un echantillon du maillage sur chaque face.
const std::int64_t sampleHalo=static_cast<std::int64_t>(step)*scale;
const std::array<std::int64_t,3> tileMinimum={
static_cast<std::int64_t>(origin.X)*scale-sampleHalo,
static_cast<std::int64_t>(origin.Y)*scale-sampleHalo,
static_cast<std::int64_t>(origin.Z)*scale-sampleHalo};
const std::array<std::int64_t,3> tileMaximum={
(static_cast<std::int64_t>(origin.X)+extent)*scale+sampleHalo,
(static_cast<std::int64_t>(origin.Y)+extent)*scale+sampleHalo,
(static_cast<std::int64_t>(origin.Z)+extent)*scale+sampleHalo};
IslandTileFeatureList=field.buildFeatureTileList(tileMinimum,tileMaximum);
const FloatingIslandFieldV1 field(entry.manifest.recipe,entry.manifest.plan);
featureQueries.push_back({field.recipe(),field.plan(),
field.buildFeatureTileList(queryMinimum,queryMaximum)});
}
}
FVoxelMeshData mesh;
if(IslandTilePlan)
{
FStandaloneShapeWorld::FScopedNativeIslandTileFeatureContext tileFeatureContext(
&IslandTilePlan->manifest.plan,&IslandTileFeatureList);
mesh=mesher.GenerateMesh(origin,step,cells,nullptr,
MeshBandMinZVoxel,MeshBandMaxZVoxel,&sampleCount,nullptr,nullptr,
(options.hashDensity||options.captureDensitySamples)?&densityValues:nullptr);
}
else
mesh=mesher.GenerateMesh(origin,step,cells,nullptr,
MeshBandMinZVoxel,MeshBandMaxZVoxel,&sampleCount,nullptr,nullptr,
(options.hashDensity||options.captureDensitySamples)?&densityValues:nullptr);
out.diagnostics.featureListMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-FeatureListStart).count();
const ScopedWorldAssemblyFeatureQueriesV1 featureScope(featureQueries);
FVoxelMeshData mesh=mesher.GenerateMesh(origin,step,cells,nullptr,
MeshBandMinZVoxel,MeshBandMaxZVoxel,&sampleCount,nullptr,nullptr,
(options.hashDensity||options.captureDensitySamples)?&densityValues:nullptr);
const double MeshingMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-MeshingStart).count();
const auto FarShellStart=std::chrono::steady_clock::now();
const FStandaloneShapeWorld& farShape=tileShape.HasFarWorld()?tileShape:*impl_->shape;
if(farShape.HasFarWorld())
{
AppendFarStratumShells(world,farShape,nativeKey,step,mesh);
AppendFarConnectorShells(farShape,nativeKey,step,world.CoarseTileCells,mesh);
}
out.diagnostics.farShellMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-FarShellStart).count();
if(impl_->worldAssembly&&key.lod>=8)
{
const auto CapStats=RefineLod8NativeCap(world,*impl_->worldAssembly,nativeKey,mesh);
+125 -2
View File
@@ -15,6 +15,45 @@
namespace VoxelForgeCore
{
namespace
{
thread_local const ScopedWorldAssemblyFeatureQueriesV1* ActiveAssemblyQueries = nullptr;
}
ScopedWorldAssemblyFeatureQueriesV1::ScopedWorldAssemblyFeatureQueriesV1(
std::span<const WorldAssemblyFeatureQueryV1> queries, bool forceCandidatesForValidation) noexcept
: queries_(queries), forceCandidates_(forceCandidatesForValidation), previous_(ActiveAssemblyQueries)
{
// Resolve cost hints once per domain, never rediscover them in the sample loop.
// Resoudre les indications de cout une fois par domaine, hors de la boucle d'echantillons.
ActiveAssemblyQueries = nullptr;
for (const auto& query : queries_)
if (forceCandidates_ || query.features.candidateEvaluationUseful)
{ ActiveAssemblyQueries = this; break; }
}
ScopedWorldAssemblyFeatureQueriesV1::~ScopedWorldAssemblyFeatureQueriesV1()
{ ActiveAssemblyQueries = previous_; }
const WorldAssemblyFeatureTileListV1* ScopedWorldAssemblyFeatureQueriesV1::find(
const void* recipe, const void* plan, std::int64_t x, std::int64_t y,
std::int64_t z, bool columnOnly) noexcept
{
const std::array<std::int64_t, 3> point{x,y,z};
if (ActiveAssemblyQueries)
for (const auto& query : ActiveAssemblyQueries->queries_)
{
if (query.recipe != recipe || query.plan != plan
|| (!ActiveAssemblyQueries->forceCandidates_ && !query.features.candidateEvaluationUseful)) continue;
bool inside = true;
for (std::size_t axis=0; axis<(columnOnly ? 2u : 3u); ++axis)
inside = inside && point[axis]>=query.features.minimumSubvoxels[axis]
&& point[axis]<=query.features.maximumSubvoxels[axis];
if (inside) return &query.features;
}
return nullptr;
}
std::vector<std::uint32_t> BuildWorldAssemblyFeatureTileListV1(
std::span<const WorldAssemblyFeatureBoundsV1> features,
const std::array<std::int64_t, 3>& tileMinimumSubvoxels,
@@ -52,8 +91,54 @@ std::vector<std::uint32_t> BuildWorldAssemblyFeatureTileListV1(
return result;
}
WorldAssemblyFeatureColumnRangeV1 BuildWorldAssemblyFeatureColumnRangeV1(
std::span<const WorldAssemblyFeatureBoundsV1> bounds, std::int64_t x, std::int64_t y)
{
WorldAssemblyFeatureColumnRangeV1 result;
result.known=true;
result.empty=true;
for(const auto& bound:bounds)
{
for(std::size_t axis=0;axis<3;++axis)
if(bound.minimumSubvoxels[axis]>bound.maximumSubvoxels[axis]) return {};
if(x<bound.minimumSubvoxels[0]||x>bound.maximumSubvoxels[0]
||y<bound.minimumSubvoxels[1]||y>bound.maximumSubvoxels[1])continue;
if(result.empty)
{
result.minimumZSubvoxels=bound.minimumSubvoxels[2];
result.maximumZSubvoxels=bound.maximumSubvoxels[2];
result.empty=false;
}
else
{
result.minimumZSubvoxels=std::min(result.minimumZSubvoxels,bound.minimumSubvoxels[2]);
result.maximumZSubvoxels=std::max(result.maximumZSubvoxels,bound.maximumSubvoxels[2]);
}
}
return result;
}
namespace world_assembly_detail
{
// Preserve recipe order without allocating or looking up metadata in the point loop.
// Garder l'ordre de recette sans allocation ni recherche de metadonnees par point.
struct FeatureRange
{
const std::vector<WorldAssemblyFeatureV1>& features;
const WorldAssemblyFeatureTileListV1* list;
struct Iterator
{
const FeatureRange* range;
std::size_t index;
const WorldAssemblyFeatureV1& operator*() const
{ return range->features[range->list ? range->list->featureIndices[index] : index]; }
Iterator& operator++() { ++index; return *this; }
bool operator!=(const Iterator& other) const { return index!=other.index; }
};
Iterator begin() const { return {this,0}; }
Iterator end() const { return {this,list ? list->featureIndices.size() : features.size()}; }
};
constexpr std::int64_t Q16 = 1ll << 16;
constexpr std::int64_t MinimumCliffShelfThicknessSubvoxels = 2 * WorldAssemblySubvoxelsPerVoxel;
constexpr std::int64_t MaxSerializedMagnitude = 1000000000000ll;
@@ -1294,7 +1379,8 @@ namespace world_assembly_detail
:CapHeightV2(recipe,plan,neighbor,x,y);
std::int64_t cap=primaryCap+((neighborCap-primaryCap)
*(static_cast<std::int64_t>(Q16)-selection.primaryWeightQ16))/Q16;
for(const auto& feature:plan.features)
const auto* list=ScopedWorldAssemblyFeatureQueriesV1::find(&recipe,&plan,x,y,0,true);
for(const auto& feature:FeatureRange{plan.features,list})
{
if(feature.type==1)
{
@@ -1730,6 +1816,42 @@ bool Heightfield2D::isValid() const noexcept
});
}
WorldAssemblyFeatureTileListV1 Heightfield2D::buildFeatureTileList(
const std::array<std::int64_t,3>& minimum,
const std::array<std::int64_t,3>& maximum) const
{
WorldAssemblyFeatureTileListV1 result;
// One shelf in the staged owner plan has no useful scan to shorten. Keep its full evaluator.
// Une seule corniche dans le plan auteur ne justifie pas la selection par echantillon.
result.candidateEvaluationUseful=plan_->features.size()>1;
result.minimumSubvoxels=minimum;
result.maximumSubvoxels=maximum;
std::vector<WorldAssemblyFeatureBoundsV1> bounds;
bounds.reserve(plan_->features.size());
for(const auto& feature:plan_->features)
{
WorldAssemblyFeatureBoundsV1 bound{feature.stableId,
feature.minimumSubvoxels,feature.maximumSubvoxels};
// Column writers affect every Z; finite volumes have an exact no-op outside their box.
// Les modificateurs de colonne agissent sur tout Z; les volumes sont neutres hors boite.
if(feature.type==1||feature.type==3)
{
bound.minimumSubvoxels[2]=std::numeric_limits<std::int64_t>::min();
bound.maximumSubvoxels[2]=std::numeric_limits<std::int64_t>::max();
}
else if(feature.type!=2&&feature.type!=4)
{
// Unknown future operations must opt into a proof before they can be culled.
// Une operation inconnue reste active tant que sa preuve manque.
bound.minimumSubvoxels.fill(std::numeric_limits<std::int64_t>::min());
bound.maximumSubvoxels.fill(std::numeric_limits<std::int64_t>::max());
}
bounds.push_back(bound);
}
result.featureIndices=BuildWorldAssemblyFeatureTileListV1(bounds,minimum,maximum);
return result;
}
HeightfieldPointSample Heightfield2D::point(std::int64_t x, std::int64_t y,
std::int64_t z) const noexcept
{
@@ -1744,7 +1866,8 @@ HeightfieldPointSample Heightfield2D::point(std::int64_t x, std::int64_t y,
out.capHeightSubvoxels-z);
if(recipe_->evaluatorVersion>=2)
{
for(const auto& feature:plan_->features)
const auto* list=ScopedWorldAssemblyFeatureQueriesV1::find(recipe_,plan_,x,y,z);
for(const auto& feature:FeatureRange{plan_->features,list})
{
if(feature.type==2)
{