feat: Phase 1 step 3 — wire the operator stack into GetDensityAt behind an opt-in
One branch on the density path, as OPSTACK-PLAN section 4 specified, and both systems coexist. - UVoxelStrateDefinition::bUseOperatorStack: the A/B switch section 2.6's acceptance bar needs. Flip it, regenerate, judge on a screenshot. - UVoxelStrateManager::UsesOperatorStackForChunk(): the ported-archetype list, written down in exactly one place. An unported archetype ignores the flag and falls back to the switch, so ticking the box anywhere is harmless today and only Maze changes behaviour. - GetDensityAt: CP_OpStack / CP_UseOpStack are resolved inside the SAME refetch block as the params, so the existing chunk + LayoutVersion key already covers them and there is no new invalidation logic to get wrong. Hot-path cost is one bool test per voxel; the stack is built per chunk, the same cadence as the param refetch. ApplyDisturbances and the diff layer stay outside the stack and run once for both paths, so the tail of the pipeline is unchanged. If UsesOperatorStackForChunk ever returns true for an archetype with no builder, the code clears the flag and falls back to the switch rather than generating an empty stack. An unported world is recoverable; a wrong one is not. UNVERIFIED: not compiled. Likely spots: the `else switch` form, FVoxelOpStack as a thread_local (move-only, reset by move-assigning a temporary), and the new include. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
34f06dfea7
commit
256a262140
5 files changed
+127
-1
No files matched your search
@@ -14,6 +14,7 @@
|
||||
#include "VoxelBiomeDefinition.h"
|
||||
#include "VoxelNoise.h" // T2.a: float, SIMD-batched gradient-noise core
|
||||
#include "VoxelDensityPrimitives.h" // spine / seal / passage — shared with the operator stack
|
||||
#include "VoxelDensityOpStack.h" // OPSTACK Phase 1: the opt-in per-strate operator stack
|
||||
|
||||
//=============================================================================
|
||||
// SURFACE COLUMN CACHE (T1.a) — kill the per-Z heightfield redundancy
|
||||
@@ -493,6 +494,11 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
|
||||
// The key MUST include the layout version, not just the chunk coord. Symptom without it:
|
||||
// "I tweaked the strate asset, regenerated, and one patch kept the old shape."
|
||||
thread_local uint32 CP_Version = 0xFFFFFFFFu;
|
||||
// OPSTACK Phase 1 — la pile d'opérateurs, construite dans le MÊME bloc de refetch que les
|
||||
// params (donc même clé chunk+version, aucune logique d'invalidation en plus). Vide tant que
|
||||
// la strate n'a pas coché `bUseOperatorStack` ET que son archétype n'est pas porté.
|
||||
thread_local FVoxelOpStack CP_OpStack;
|
||||
thread_local bool CP_UseOpStack = false;
|
||||
|
||||
const uint32 LayoutVersion = StrateManager->GetLayoutVersion();
|
||||
if (ChunkCoord != CP_Chunk || LayoutVersion != CP_Version)
|
||||
@@ -523,9 +529,38 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
|
||||
CP_Tunnel = StrateManager->GetGenerationParams(ChunkCoord); break;
|
||||
}
|
||||
CP_Dist = StrateManager->GetDisturbanceParamsForChunk(ChunkCoord);
|
||||
|
||||
// ── OPSTACK Phase 1 : (re)construire la pile si cette strate l'a demandée. ──
|
||||
// Une seule branche ajoutée au chemin densité, et elle est FROIDE : la construction est
|
||||
// par chunk (comme le refetch de params juste au-dessus), jamais par voxel.
|
||||
CP_UseOpStack = StrateManager->UsesOperatorStackForChunk(ChunkCoord);
|
||||
if (CP_UseOpStack)
|
||||
{
|
||||
CP_OpStack = FVoxelOpStack(); // move-assign : libère l'ancienne pile
|
||||
switch (CP_GenType)
|
||||
{
|
||||
case ECaveGeneratorType::Maze:
|
||||
VoxelDensityOps::BuildMazeStack(CP_OpStack, CP_Maze, Seed,
|
||||
OriginSpineRadius, StrateManager);
|
||||
break;
|
||||
default:
|
||||
// UsesOperatorStackForChunk ne rend true que pour les archétypes portés, donc
|
||||
// on ne devrait jamais arriver ici. Si ça arrive, retomber sur le `switch`
|
||||
// plutôt que générer du vide — un monde faux est pire qu'un monde non porté.
|
||||
CP_UseOpStack = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (CP_GenType)
|
||||
// Le seul point d'entrée de la pile dans le chemin de production. Elle rend la convention
|
||||
// MC (négatif = solide) comme les fonctions d'archétype, donc les disturbances et la couche
|
||||
// de diff qui suivent ne voient aucune différence.
|
||||
if (CP_UseOpStack)
|
||||
{
|
||||
Result = CP_OpStack.EvalMC(WorldX, WorldY, WorldZ);
|
||||
}
|
||||
else switch (CP_GenType)
|
||||
{
|
||||
case ECaveGeneratorType::FlatPlain:
|
||||
case ECaveGeneratorType::CrystalChamber:
|
||||
|
||||
Reference in new issue
Block a user