docs(comments): drop history narration, keep the why
Rewrite comments that told the story of past rounds instead of the current code: dated "fixed on / tried on / reverted on" notes, "this used to...", "stale paragraph removed", "Phase 1/2" progress labels, and the "ClassifyTile still does not use ClassifyBox" claim (it does now, via VF_ClassifyBoxWithWarpRetry). Every invariant, unit, sign convention, trap and measurement that justified the design is kept, rephrased in the present tense and in its original language (French or English). Also fix comments that named symbols removed in the dead-code commit (EvaluateSDF wrapper, FTunnelCoreWorldEvaluation support-floor fields, BlendChunks -> TransitionBlendChunks) and the stale function name in the VF_RollStrateParamsDetailed determinism checkf message. Comment and message text only; no code token changed. Tests untouched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
This commit is contained in:
21 files changed
+201
-244
No files matched your search
@@ -43,11 +43,11 @@ void UVoxelAtmosphereManager::UpdateForPlayer(const FVector& PlayerWorldPos)
|
|||||||
if (!O) return;
|
if (!O) return;
|
||||||
|
|
||||||
// Le champ de densite est en espace ACTEUR : les requetes de strate et de biome veulent des
|
// Le champ de densite est en espace ACTEUR : les requetes de strate et de biome veulent des
|
||||||
// coordonnees LOCALES. Le placement d acteurs, lui, veut du MONDE. Les deux cohabitent ici, ce
|
// coordonnees LOCALES. Le placement d acteurs, lui, veut du MONDE. Les deux cohabitent ici : ne
|
||||||
// qui est exactement pourquoi ce fichier supposait "acteur a l origine" avant le 2026-08-17.
|
// pas supposer "acteur a l origine".
|
||||||
// The density field is in ACTOR space: strate and biome queries want LOCAL coordinates, while
|
// The density field is in ACTOR space: strate and biome queries want LOCAL coordinates, while
|
||||||
// actor placement wants WORLD. Both live in this function - which is exactly why it used to
|
// actor placement wants WORLD. Both live in this function - do not assume "actor at origin /
|
||||||
// assume "actor at origin / identity".
|
// identity".
|
||||||
const FTransform Xf = O->GetActorTransform();
|
const FTransform Xf = O->GetActorTransform();
|
||||||
const FVector LocalPos = Xf.InverseTransformPosition(PlayerWorldPos);
|
const FVector LocalPos = Xf.InverseTransformPosition(PlayerWorldPos);
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace
|
|||||||
// Density coordinates and cached cull radii are float-valued. FVector is double-valued in
|
// Density coordinates and cached cull radii are float-valued. FVector is double-valued in
|
||||||
// UE5, so using FVector::DistSquared here needlessly widens every candidate reject. Keep the
|
// UE5, so using FVector::DistSquared here needlessly widens every candidate reject. Keep the
|
||||||
// cull in the same build-fixed float domain as the density query; the exact field output is
|
// cull in the same build-fixed float domain as the density query; the exact field output is
|
||||||
// intentionally no longer required to match the pre-optimization double round-trip.
|
// intentionally not required to match a double-precision round-trip.
|
||||||
FORCEINLINE float VF_FloatDistSquared(
|
FORCEINLINE float VF_FloatDistSquared(
|
||||||
float X, float Y, float Z, const FVector& Center)
|
float X, float Y, float Z, const FVector& Center)
|
||||||
{
|
{
|
||||||
@@ -1356,7 +1356,7 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Never turn a malformed authored bound into a giant allocation. A failed build is
|
// Never turn a malformed authored bound into a giant allocation. A failed build is
|
||||||
// deliberately indistinguishable from the old path to the density evaluator.
|
// deliberately indistinguishable from the unindexed linear scan to the density evaluator.
|
||||||
constexpr int64 MaxBuckets = 1024 * 1024;
|
constexpr int64 MaxBuckets = 1024 * 1024;
|
||||||
constexpr int64 MaxReferences = 4 * 1024 * 1024;
|
constexpr int64 MaxReferences = 4 * 1024 * 1024;
|
||||||
TArray<int32> ItemMinX;
|
TArray<int32> ItemMinX;
|
||||||
@@ -3626,11 +3626,11 @@ namespace
|
|||||||
// Once the lower and upper ends bracket the room, the authored room/floor field is
|
// Once the lower and upper ends bracket the room, the authored room/floor field is
|
||||||
// monotone through this narrow vertical landing band for the finite parameter envelope
|
// monotone through this narrow vertical landing band for the finite parameter envelope
|
||||||
// used by the generator. Find the one-voxel bucket with a logarithmic search, then run
|
// used by the generator. Find the one-voxel bucket with a logarithmic search, then run
|
||||||
// the exact legacy bisection in that bucket. The latter is kept byte-for-byte in its
|
// the exact bisection in that bucket. The latter is kept byte-for-byte in its arithmetic
|
||||||
// arithmetic and iteration count so the endpoint remains identical to the old scan.
|
// and iteration count so the endpoint is identical to the one-voxel walk below.
|
||||||
// If the bracket is not monotone (or the local bucket check cannot reproduce the crossing),
|
// If the bracket is not monotone (or the local bucket check cannot reproduce the crossing),
|
||||||
// fall back to the original one-voxel walk below; malformed/unsupported inputs therefore
|
// fall back to that one-voxel walk; malformed/unsupported inputs therefore keep its
|
||||||
// retain the old behavior.
|
// result.
|
||||||
auto BisectBracket = [&](float BracketLow, float BracketHigh,
|
auto BisectBracket = [&](float BracketLow, float BracketHigh,
|
||||||
float BracketLowValue) -> bool
|
float BracketLowValue) -> bool
|
||||||
{
|
{
|
||||||
@@ -7120,14 +7120,14 @@ void VoxelCaveMorphology::BuildChunkCache(
|
|||||||
// (Frontier rooms with no closer candidate in reach fall back to plain NN — a far
|
// (Frontier rooms with no closer candidate in reach fall back to plain NN — a far
|
||||||
// cluster stays internally chained even when it can't bridge to the origin side.)
|
// cluster stays internally chained even when it can't bridge to the origin side.)
|
||||||
//
|
//
|
||||||
// bTunnelsFlowTowardOrigin = false (legacy): plain nearest-neighbor pairing. NOTE:
|
// bTunnelsFlowTowardOrigin = false (legacy): plain nearest-neighbor pairing. NOTE: an
|
||||||
// despite what this comment used to claim, an NN-graph is a FOREST of small clusters,
|
// NN-graph is a FOREST of small clusters, not a connected tree — isolated cave pockets
|
||||||
// not a connected tree — isolated cave pockets are expected in this mode.
|
// are expected in this mode.
|
||||||
//
|
//
|
||||||
// Selection metric (not the reach filter) penalizes vertical separation via
|
// Selection metric (not the reach filter) penalizes vertical separation via
|
||||||
// TunnelHorizontalBias, so the GUARANTEED links also prefer walkable slopes —
|
// TunnelHorizontalBias, so the GUARANTEED links also prefer walkable slopes —
|
||||||
// previously only the random TunnelDensity extras were biased, which is why
|
// biasing only the random TunnelDensity extras lets backbone tunnels come out
|
||||||
// backbone tunnels could come out absurdly steep.
|
// absurdly steep.
|
||||||
TArray<int32, TInlineAllocator<64>> NearestNeighbor;
|
TArray<int32, TInlineAllocator<64>> NearestNeighbor;
|
||||||
NearestNeighbor.SetNumUninitialized(NumRooms);
|
NearestNeighbor.SetNumUninitialized(NumRooms);
|
||||||
|
|
||||||
@@ -8266,7 +8266,7 @@ void VoxelCaveMorphology::BuildChunkCache(
|
|||||||
MaxExtent += FloorReliefEnvelope + BlendEnvelope * 3.0f;
|
MaxExtent += FloorReliefEnvelope + BlendEnvelope * 3.0f;
|
||||||
|
|
||||||
// --- PRE-BAKED SHAPE ---
|
// --- PRE-BAKED SHAPE ---
|
||||||
// Same hash roll + thresholds + capsule trig the evaluator used to redo PER VOXEL;
|
// Same hash roll + thresholds + capsule trig the evaluator would otherwise redo PER VOXEL;
|
||||||
// done once here → EvaluateSDFCached just switches on ShapeType. Bit-identical output.
|
// done once here → EvaluateSDFCached just switches on ShapeType. Bit-identical output.
|
||||||
{
|
{
|
||||||
const uint32 ShapeHash = VoxelHash::Mix(CR.Hash ^ 0xDEADBEEFu);
|
const uint32 ShapeHash = VoxelHash::Mix(CR.Hash ^ 0xDEADBEEFu);
|
||||||
@@ -8758,7 +8758,7 @@ float VoxelCaveMorphology::EvaluateSDFCached(
|
|||||||
// A room owns every point inside its authored body. The short mouth ownership fade below
|
// A room owns every point inside its authored body. The short mouth ownership fade below
|
||||||
// handles the fillet outside the wall; this broader test is the hard A > B rule for the room
|
// handles the fillet outside the wall; this broader test is the hard A > B rule for the room
|
||||||
// interior. The exact raw shape sign was captured during the room SDF pass above, so this
|
// interior. The exact raw shape sign was captured during the room SDF pass above, so this
|
||||||
// point no longer performs a second spatial-index traversal.
|
// point does not perform a second spatial-index traversal.
|
||||||
auto EvaluateTunnel = [&](int32 TunnelIdx)
|
auto EvaluateTunnel = [&](int32 TunnelIdx)
|
||||||
{
|
{
|
||||||
const FCachedTunnel& Tunnel = TunnelData[TunnelIdx];
|
const FCachedTunnel& Tunnel = TunnelData[TunnelIdx];
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace VoxelClipmapDesiredTiles
|
|||||||
int32 ClipRadius = 3;
|
int32 ClipRadius = 3;
|
||||||
int32 MaxClipLevel = 4;
|
int32 MaxClipLevel = 4;
|
||||||
int32 RenderDistanceChunks = 0;
|
int32 RenderDistanceChunks = 0;
|
||||||
// Far sheet ring selection. AVoxelWorld always passes false (the runtime no longer meshes
|
// Far sheet ring selection. AVoxelWorld always passes false (the runtime does not mesh
|
||||||
// sheets); it stays here because the clipmap automation tests still exercise it.
|
// sheets); it stays here because the clipmap automation tests still exercise it.
|
||||||
bool bFarSheetRing = true;
|
bool bFarSheetRing = true;
|
||||||
int32 FarSheetSpanLevels = 2;
|
int32 FarSheetSpanLevels = 2;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// VoxelDensityOpStack.cpp
|
// VoxelDensityOpStack.cpp
|
||||||
// Les opérateurs concrets de la Phase 1 : la décomposition de Maze + le post-traitement structurel.
|
// Les opérateurs concrets : les décompositions d'archétypes + le post-traitement structurel.
|
||||||
// The concrete Phase 1 operators: the Maze decomposition + the structural post-process.
|
// The concrete operators: the archetype decompositions + the structural post-process.
|
||||||
//
|
//
|
||||||
// Ces opérateurs alimentent le jeu derrière l'opt-in décrit dans l'en-tête de VoxelDensityOpStack.h.
|
// Ces opérateurs alimentent le jeu derrière l'opt-in décrit dans l'en-tête de VoxelDensityOpStack.h.
|
||||||
//
|
//
|
||||||
@@ -92,23 +92,20 @@ namespace
|
|||||||
* gradients.) Se tromper ici coûte une boîte de recherche un peu plus large, jamais un
|
* gradients.) Se tromper ici coûte une boîte de recherche un peu plus large, jamais un
|
||||||
* verdict faux : plus large ⇒ SUR-ensemble de primitives ⇒ `Identity` plus rare.
|
* verdict faux : plus large ⇒ SUR-ensemble de primitives ⇒ `Identity` plus rare.
|
||||||
*
|
*
|
||||||
* ⚠️⚠️ **CORRIGÉ DE 2.0 À 1.5 LE 2026-07-28, ET CETTE CONSTANTE ÉTAIT LE TERME DOMINANT DE
|
* ⚠️⚠️ **CETTE CONSTANTE EST LE TERME DOMINANT DE TOUTE LA FONCTION.** À lire avant d'y
|
||||||
* TOUTE LA FONCTION PENDANT TROIS BUILDS.** À lire avant d'y retoucher.
|
* retoucher.
|
||||||
*
|
*
|
||||||
* La dilatation vaut `CaveWarpStrength · VOXEL_NOISE_SCALE · CETTE BORNE`. Avec les défauts
|
* La dilatation vaut `CaveWarpStrength · VOXEL_NOISE_SCALE · CETTE BORNE`. Avec les défauts
|
||||||
* (`CaveWarpStrength = 8`, `SCALE = 1.25`) elle valait **20 voxels** — appliquée des deux
|
* (`CaveWarpStrength = 8`, `SCALE = 1.25`), une borne de 2.0 vaut **20 voxels** — appliquée
|
||||||
* côtés de chaque axe d'une tuile de **10 voxels**, soit une boîte de requête de 50 voxels,
|
* des deux côtés de chaque axe d'une tuile de **10 voxels**, soit une boîte de requête de
|
||||||
* **125× le volume de la tuile**. Trois passes de resserrement (le ver, les colonnes,
|
* 50 voxels, **125× le volume de la tuile** : `BoxHalfDiag` est alors dominé par cette
|
||||||
* l'échantillonneur, la disjonction des tunnels) ont été faites AUTOUR de ce terme sans que
|
* dilatation et non par la géométrie, et tout resserrement ailleurs ne gagne presque rien.
|
||||||
* personne ne le mesure. Le test des tunnels, annoncé « un ordre de grandeur plus serré », ne
|
|
||||||
* gagnait en pratique que 25 % — exactement parce que `BoxHalfDiag` était dominé par cette
|
|
||||||
* dilatation et non par la géométrie.
|
|
||||||
*
|
*
|
||||||
* ⚠️ ET LE RESTE DU PLUGIN N'A JAMAIS ÉTÉ AUSSI PRUDENT : `BuildChunkCache` est appelée avec
|
* ⚠️ LE RESTE DU PLUGIN EST MOINS PRUDENT : `BuildChunkCache` est appelée avec
|
||||||
* `Expansion = CaveWarpStrength + 2` (ici comme dans `GetDensityWithParams`), ce qui suppose
|
* `Expansion = CaveWarpStrength + 2` (ici comme dans `GetDensityWithParams`), ce qui suppose
|
||||||
* `|Perlin3D| · SCALE ≤ CaveWarpStrength`, donc `|Perlin3D| ≤ 0.8`. Le code qui tourne en
|
* `|Perlin3D| · SCALE ≤ CaveWarpStrength`, donc `|Perlin3D| ≤ 0.8`. Le code de production
|
||||||
* production depuis toujours parie déjà là-dessus. Prendre 2.0 était 2,5× plus conservateur
|
* parie déjà là-dessus. 2.0 serait 2,5× plus conservateur que l'hypothèse dont dépend déjà la
|
||||||
* que l'hypothèse dont dépend déjà la correction du cache.
|
* correction du cache.
|
||||||
*
|
*
|
||||||
* LA BORNE 1.5, DÉMONTRÉE (et non observée) :
|
* LA BORNE 1.5, DÉMONTRÉE (et non observée) :
|
||||||
* 1. `GradDot` rend `±u ± v` où `u` et `v` sont deux composantes **distinctes** de l'offset
|
* 1. `GradDot` rend `±u ± v` où `u` et `v` sont deux composantes **distinctes** de l'offset
|
||||||
@@ -122,11 +119,10 @@ namespace
|
|||||||
* est la borne qui se démontre sans analyse de cas sur les hash. `√3/2 ≈ 0.87`, la borne
|
* est la borne qui se démontre sans analyse de cas sur les hash. `√3/2 ≈ 0.87`, la borne
|
||||||
* classique de Perlin 3D, dépend du jeu de gradients : on ne s'appuie pas dessus.)
|
* classique de Perlin 3D, dépend du jeu de gradients : on ne s'appuie pas dessus.)
|
||||||
*
|
*
|
||||||
* Was 2.0, and that constant was the dominant term of this whole function for three builds:
|
* This bound is the dominant term of the whole function: 2.0 would inflate a 10-voxel tile
|
||||||
* it inflated a 10-voxel tile into a 50-voxel query box (125x the volume), which is why the
|
* into a 50-voxel query box (125x the volume). The rest of the plugin already assumes
|
||||||
* "order of magnitude tighter" tunnel test only won 25%. The rest of the plugin has always
|
* |Perlin3D| <= 0.8 (BuildChunkCache's Expansion = CaveWarpStrength + 2). 1.5 is PROVED
|
||||||
* assumed |Perlin3D| <= 0.8 (BuildChunkCache's Expansion = CaveWarpStrength + 2). 1.5 is
|
* above from GradDot's two-distinct-axes form and the per-axis weighted bound of 0.5.
|
||||||
* PROVED above from GradDot's two-distinct-axes form and the per-axis weighted bound of 0.5.
|
|
||||||
*/
|
*/
|
||||||
static constexpr float VF_PerlinAbsBound = 1.5f;
|
static constexpr float VF_PerlinAbsBound = 1.5f;
|
||||||
|
|
||||||
@@ -356,7 +352,7 @@ namespace
|
|||||||
// ⚠️ CE QUE ÇA COÛTE, dit franchement : l'original teste UNE fois et saute les douze ; la pile
|
// ⚠️ CE QUE ÇA COÛTE, dit franchement : l'original teste UNE fois et saute les douze ; la pile
|
||||||
// teste douze fois. Douze comparaisons flottantes parfaitement prédites par voxel de roc profond
|
// teste douze fois. Douze comparaisons flottantes parfaitement prédites par voxel de roc profond
|
||||||
// — mesurable, mais c'est exactement le genre de chose que `AUDIT §C10` dit de MESURER avant
|
// — mesurable, mais c'est exactement le genre de chose que `AUDIT §C10` dit de MESURER avant
|
||||||
// d'optimiser. Noté dans OPSTACK-PROGRESS comme poste de perf, pas « corrigé » à l'aveugle.
|
// d'optimiser. C'est un poste de perf connu, pas à « corriger » à l'aveugle.
|
||||||
//
|
//
|
||||||
// STAGE B5 DECISION: repeated early-out in each op, NOT a scoping container — the stack is a flat
|
// STAGE B5 DECISION: repeated early-out in each op, NOT a scoping container — the stack is a flat
|
||||||
// list that ClassifyBox folds op by op, and an op that only exists inside a container is not
|
// list that ClassifyBox folds op by op, and an op that only exists inside a container is not
|
||||||
@@ -874,25 +870,22 @@ namespace
|
|||||||
bool IsAdditive() const override { return false; }
|
bool IsAdditive() const override { return false; }
|
||||||
void PrepareChunk(const FVoxelOpContext&) override {}
|
void PrepareChunk(const FVoxelOpContext&) override {}
|
||||||
|
|
||||||
// ⚠️⚠️ CORRIGÉ 2026-07-27 : c'était `true`, ET C'ÉTAIT FAUX.
|
// ⚠️⚠️ `false`, ET NE PAS LE PASSER À `true`.
|
||||||
//
|
//
|
||||||
// Le contrat de `IsXYPure` est « **`Eval`** ne dépend pas de Z » — pas « les surfaces ne
|
// Le contrat de `IsXYPure` est « **`Eval`** ne dépend pas de Z » — pas « les surfaces ne
|
||||||
// dépendent pas de Z ». Or `Eval` calcule `min(Z - sol, plafond - Z)` : il dépend de Z de
|
// dépendent pas de Z ». Or `Eval` calcule `min(Z - sol, plafond - Z)` : il dépend de Z de
|
||||||
// la façon la plus directe qui soit. §3.1 a rendu les SURFACES pures en XY ; la DENSITÉ,
|
// la façon la plus directe qui soit. §3.1 a rendu les SURFACES pures en XY ; la DENSITÉ,
|
||||||
// elle, ne l'a jamais été et ne peut pas l'être — c'est une distance à une surface.
|
// elle, ne l'a jamais été et ne peut pas l'être — c'est une distance à une surface.
|
||||||
//
|
//
|
||||||
// Latent seulement parce que personne ne lit encore ce drapeau. Le jour où le cache de
|
// Le jour où un cache de colonnes générique lit ce drapeau, un `true` ici ferait partager
|
||||||
// colonnes T1.a devient générique (l'étape suivante), un `true` ici ferait partager UNE
|
// UNE valeur de densité sur TOUTE la pile verticale de chunks — un monde silencieusement
|
||||||
// valeur de densité sur TOUTE la pile verticale de chunks — un monde silencieusement faux,
|
// faux, que `ValidateDeterminism` ne verrait pas parce qu'il échantillonne le long d'un
|
||||||
// que `ValidateDeterminism` ne verrait pas parce qu'il échantillonne le long d'un bord X.
|
// bord X. C'est exactement le piège que l'avertissement de `VoxelDensityOp.h` décrit.
|
||||||
// C'est exactement le piège que l'avertissement de `VoxelDensityOp.h` décrit, et je suis
|
|
||||||
// tombé dedans en écrivant l'opérateur qui le cite.
|
|
||||||
//
|
//
|
||||||
// ⚠️ FIXED: this said `true` and was WRONG. The contract is "**Eval** does not depend on Z",
|
// ⚠️ Must stay `false`. The contract is "**Eval** does not depend on Z", and Eval computes
|
||||||
// and Eval computes min(Z - floor, ceil - Z). §3.1 made the SURFACES XY-pure; the DENSITY
|
// min(Z - floor, ceil - Z). §3.1 made the SURFACES XY-pure; the DENSITY never was and
|
||||||
// never was and cannot be — it is a distance to a surface. Latent only because nothing reads
|
// cannot be — it is a distance to a surface. A generic column cache reading `true` here
|
||||||
// the flag yet; a generic T1.a column cache would have shared one density down the whole
|
// would share one density down the whole vertical chunk stack.
|
||||||
// vertical chunk stack.
|
|
||||||
//
|
//
|
||||||
// C'est précisément cette distinction qui justifie l'espace-hauteur (`VoxelHeightOp.h`) :
|
// C'est précisément cette distinction qui justifie l'espace-hauteur (`VoxelHeightOp.h`) :
|
||||||
// ce qui est pur en XY, ce sont les HAUTEURS, et elles y sont dans un type qui n'a pas de Z.
|
// ce qui est pur en XY, ce sont les HAUTEURS, et elles y sont dans un type qui n'a pas de Z.
|
||||||
@@ -956,8 +949,8 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// The surface coordinates stay in float; the old FVector round-trip only preserved
|
// The surface coordinates stay in float; an FVector (double) round-trip is not part of
|
||||||
// legacy rounding and is no longer part of the output contract.
|
// the output contract.
|
||||||
float SurfaceFloor(float WorldX, float WorldY) const
|
float SurfaceFloor(float WorldX, float WorldY) const
|
||||||
{
|
{
|
||||||
if (FloorRoughness <= 0.0f) { return FloorZ; }
|
if (FloorRoughness <= 0.0f) { return FloorZ; }
|
||||||
@@ -1307,29 +1300,29 @@ namespace
|
|||||||
bool IsXYPure() const override { return false; } // voir le bloc ci-dessus
|
bool IsXYPure() const override { return false; } // voir le bloc ci-dessus
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ⚠️ C'EST ICI QUE SE JOUE LA PERF DE CET ARCHÉTYPE — corrigé 2026-07-27 après mesure.
|
* ⚠️ C'EST ICI QUE SE JOUE LA PERF DE CET ARCHÉTYPE.
|
||||||
*
|
*
|
||||||
* Le mémo de colonne était clé sur `InstanceId`, qui change à CHAQUE reconstruction de pile,
|
* Ne PAS clé ce mémo sur `InstanceId` : il change à CHAQUE reconstruction de pile,
|
||||||
* c'est-à-dire à chaque chunk. Résultat : une strate haute de 4 chunks recalculait ses
|
* c'est-à-dire à chaque chunk, et une strate haute de 4 chunks recalculerait ses colonnes
|
||||||
* colonnes **4 fois**, resamples du cliff compris. Le chemin d'origine ne fait pas ça —
|
* **4 fois**, resamples du cliff compris. `GSurfColCache` est clé sur
|
||||||
* `GSurfColCache` est clé sur `(boîte XY, StrateKey, Seed, LayoutVersion)` **SANS ChunkZ**,
|
* `(boîte XY, StrateKey, Seed, LayoutVersion)` **SANS ChunkZ**, délibérément, « shared down
|
||||||
* délibérément, « shared down the whole vertical strate stack ». Cette pile reprend la
|
* the whole vertical strate stack ». Cette pile reprend la même identité de
|
||||||
* même identité de strate/layout/seed, en ajoutant l'empreinte obligatoire des params pour
|
* strate/layout/seed, en ajoutant l'empreinte obligatoire des params pour protéger ses
|
||||||
* protéger ses sorties propres ; son mémo est maintenant un LRU spatial de six boîtes.
|
* sorties propres ; son mémo est un LRU spatial de six boîtes.
|
||||||
*
|
*
|
||||||
* Donc la clé garde l'identité partagée : ce qui rend deux colonnes interchangeables, c'est
|
* Donc la clé garde l'identité partagée : ce qui rend deux colonnes interchangeables, c'est
|
||||||
* la STRATE, le seed, la version de layout et les params, pas le chunk. Le mémo étant
|
* la STRATE, le seed, la version de layout et les params, pas le chunk. Le mémo étant
|
||||||
* `thread_local`, il SURVIT à la reconstruction de la pile — seule la clé l'invalidait.
|
* `thread_local`, il SURVIT à la reconstruction de la pile — seule la clé l'invalide.
|
||||||
*
|
*
|
||||||
* POURQUOI C'EST SÛR : les hauteurs sont XY-pures par construction (c'est tout l'objet de
|
* POURQUOI C'EST SÛR : les hauteurs sont XY-pures par construction (c'est tout l'objet de
|
||||||
* `VoxelHeightOp.h`, où le type n'a pas de Z), et le champ de biomes est documenté
|
* `VoxelHeightOp.h`, où le type n'a pas de Z), et le champ de biomes est documenté
|
||||||
* XY-pur — « ZERO Z dependence: the climate/Voronoi fields are pure-XY ». C'est exactement
|
* XY-pur — « ZERO Z dependence: the climate/Voronoi fields are pure-XY ». C'est exactement
|
||||||
* la justification sur laquelle `GSurfColCache` repose déjà.
|
* la justification sur laquelle `GSurfColCache` repose déjà.
|
||||||
*
|
*
|
||||||
* The memo was keyed on InstanceId, which changes every chunk, so a 4-chunk strate recomputed
|
* Do not key the memo on InstanceId: it changes every chunk, so a 4-chunk strate would
|
||||||
* every column 4x. GSurfColCache deliberately omits ChunkZ and shares down the whole vertical
|
* recompute every column 4x. GSurfColCache deliberately omits ChunkZ and shares down the whole
|
||||||
* stack; this now shares the same strate/layout/seed identity and adds the required params
|
* vertical stack; this shares the same strate/layout/seed identity and adds the required
|
||||||
* fingerprint for its own outputs. The six-box LRU keeps independent XY regions alive. Safe
|
* params fingerprint for its own outputs. The six-box LRU keeps independent XY regions alive. Safe
|
||||||
* because heights are XY-pure by type and the biome field is documented Z-independent.
|
* because heights are XY-pure by type and the biome field is documented Z-independent.
|
||||||
*/
|
*/
|
||||||
void PrepareChunk(const FVoxelOpContext& Ctx) override
|
void PrepareChunk(const FVoxelOpContext& Ctx) override
|
||||||
@@ -1973,8 +1966,7 @@ namespace
|
|||||||
*
|
*
|
||||||
* (Le `ClassifyTile` actuel n'a pas cette marge — il exclut simplement ces z du test de
|
* (Le `ClassifyTile` actuel n'a pas cette marge — il exclut simplement ces z du test de
|
||||||
* colonne. La fenêtre est infime et demande que l'archétype produise de l'air pile à ce z,
|
* colonne. La fenêtre est infime et demande que l'archétype produise de l'air pile à ce z,
|
||||||
* mais elle est réelle ; notée plutôt que corrigée en douce, puisque le chemin d'aujourd'hui
|
* mais elle est réelle.)
|
||||||
* n'est pas touché par cette Phase 1.)
|
|
||||||
*/
|
*/
|
||||||
EVoxelTileClass ClassifyBox(const FBox& VoxelBox, const FVoxelOpContext&) const override
|
EVoxelTileClass ClassifyBox(const FBox& VoxelBox, const FVoxelOpContext&) const override
|
||||||
{
|
{
|
||||||
@@ -2879,19 +2871,15 @@ namespace
|
|||||||
//
|
//
|
||||||
// ⚠️ ÉCART ASSUMÉ AVEC `OPSTACK-DECOMPOSITION §7`, qui décrivait un `FRAME IslandWarp` enveloppant
|
// ⚠️ ÉCART ASSUMÉ AVEC `OPSTACK-DECOMPOSITION §7`, qui décrivait un `FRAME IslandWarp` enveloppant
|
||||||
// la source. Le warp reste À L'INTÉRIEUR de l'opérateur, et c'est délibéré : `§7` compte trois
|
// la source. Le warp reste À L'INTÉRIEUR de l'opérateur, et c'est délibéré : `§7` compte trois
|
||||||
// usages de frames (îles, caves de TunnelNetwork, tunnels), mais **deux d'entre eux ne sont pas
|
// usages de frames (îles, caves de TunnelNetwork, tunnels), et aucun n'en a besoin. Voir
|
||||||
// encore portés**. Inventer l'infrastructure de frame pour son unique utilisateur actuel, c'est
|
// `BuildTunnelNetworkStack` : `CaveWarp` n'enveloppe qu'UN opérateur (donc c'est une variable
|
||||||
// la concevoir contre un seul exemple — précisément ce que ce refactor a évité jusqu'ici en
|
// locale, pas un frame) et `VerticalScale` est une fonction pure d'un scalaire. **Zéro frame
|
||||||
// n'abstrayant qu'à la deuxième occurrence (cf. `IVoxelBiomeField`, né d'un besoin réel).
|
// sur trois candidats.** Ne pas rouvrir : le warp reste local ICI pour la même raison qu'il
|
||||||
// ✅ RÉPONDU (2026-08-16) : TunnelNetwork EST porté, et le deuxième usage réel a dissous la
|
// est resté local là-bas. On n'abstrait qu'à la deuxième occurrence réelle (cf.
|
||||||
// question au lieu de la trancher. Voir `BuildTunnelNetworkStack` : `CaveWarp` n'enveloppe
|
// `IVoxelBiomeField`, né d'un besoin réel).
|
||||||
// qu'UN opérateur (donc c'est une variable locale, pas un frame) et `VerticalScale` est une
|
|
||||||
// fonction pure d'un scalaire. **Zéro frame sur trois candidats.** Ne pas rouvrir : le warp
|
|
||||||
// reste local ICI pour la même raison qu'il est resté local là-bas.
|
|
||||||
//
|
//
|
||||||
// The warp stays INSIDE the op against §7's FRAME suggestion. ✅ ANSWERED 2026-08-16: this said
|
// The warp stays INSIDE the op against §7's FRAME suggestion. TunnelNetwork is ported, and the
|
||||||
// "revisit when TunnelNetwork brings the second real use" — TunnelNetwork is ported, and the
|
// second real use dissolved the question rather than settling it. See BuildTunnelNetworkStack:
|
||||||
// second use dissolved the question rather than settling it. See BuildTunnelNetworkStack:
|
|
||||||
// CaveWarp wraps exactly ONE operator (a local variable, not a frame) and VerticalScale is a
|
// CaveWarp wraps exactly ONE operator (a local variable, not a frame) and VerticalScale is a
|
||||||
// pure function of a scalar. ZERO frames out of three candidates. Do not reopen.
|
// pure function of a scalar. ZERO frames out of three candidates. Do not reopen.
|
||||||
class FIslandBlobSource final : public IVoxelDensityOp
|
class FIslandBlobSource final : public IVoxelDensityOp
|
||||||
@@ -3111,8 +3099,8 @@ namespace
|
|||||||
* ⚠️ LA CLÉ INCLUT `BoundarySealThickness`, QUE L'ORIGINAL OMET. `SpreadZ` s'en sert
|
* ⚠️ LA CLÉ INCLUT `BoundarySealThickness`, QUE L'ORIGINAL OMET. `SpreadZ` s'en sert
|
||||||
* (`H·0.5 − max(TopHalf, UnderDepth) − Seal`), donc dans `GetFloatingIslandDensity` une
|
* (`H·0.5 − max(TopHalf, UnderDepth) − Seal`), donc dans `GetFloatingIslandDensity` une
|
||||||
* édition à chaud qui ne change QUE l'épaisseur de seal sert des îles périmées. Même famille
|
* édition à chaud qui ne change QUE l'épaisseur de seal sert des îles périmées. Même famille
|
||||||
* que `AUDIT §C2` et que la régression d'overhang du 2026-07-27 : une clé de cache
|
* que `AUDIT §C2` : une clé de cache incomplète ne se voit pas, elle produit du terrain
|
||||||
* incomplète ne se voit pas, elle produit du terrain plausible. Ajouter le champ ne coûte
|
* plausible. Ajouter le champ ne coûte
|
||||||
* qu'un recalcul, jamais une valeur différente — donc l'égalité binaire tient.
|
* qu'un recalcul, jamais une valeur différente — donc l'égalité binaire tient.
|
||||||
*
|
*
|
||||||
* The key includes BoundarySealThickness, which the original omits although SpreadZ reads it.
|
* The key includes BoundarySealThickness, which the original omits although SpreadZ reads it.
|
||||||
@@ -3953,10 +3941,9 @@ namespace
|
|||||||
// clé « égalité de chunk » se retournait à chaque cellule de bord et reconstruisait le
|
// clé « égalité de chunk » se retournait à chaque cellule de bord et reconstruisait le
|
||||||
// cache (coûteux) en boucle. Comme le cache couvre la boîte + MaxInfluence, toute requête
|
// cache (coûteux) en boucle. Comme le cache couvre la boîte + MaxInfluence, toute requête
|
||||||
// DANS la boîte est correcte. Ne pas « simplifier » en clé de chunk.
|
// DANS la boîte est correcte. Ne pas « simplifier » en clé de chunk.
|
||||||
// (Les champs de cache vivent maintenant dans `FState`, au-dessus, pour que les
|
// (Les champs de cache vivent dans `FState`, au-dessus, pour que les modificateurs de
|
||||||
// modificateurs de détail de l'étape B puissent les lire. Même durée de vie, même
|
// détail puissent les lire ; ce sont des `thread_local` partagés entre instances.)
|
||||||
// partage entre instances qu'avant : ce sont les mêmes `thread_local`, déménagés.)
|
// ⚠️ LA CLÉ INCLUT LES PARAMS, CONTRAIREMENT À L'ORIGINAL.
|
||||||
// ⚠️ AJOUTÉ PAR RAPPORT À L'ORIGINAL — la leçon du 2026-07-27 (régression d'overhang).
|
|
||||||
// L'original ne clé QUE sur (boîte, strate, seed) : deux jeux de params différents dans
|
// L'original ne clé QUE sur (boîte, strate, seed) : deux jeux de params différents dans
|
||||||
// la MÊME strate au MÊME seed se servent mutuellement leur cache. En production
|
// la MÊME strate au MÊME seed se servent mutuellement leur cache. En production
|
||||||
// `RebuildStrates` masque le trou en bougeant la strate ; en test, deux piles construites
|
// `RebuildStrates` masque le trou en bougeant la strate ; en test, deux piles construites
|
||||||
@@ -8243,7 +8230,7 @@ namespace
|
|||||||
// sortie, ce qui est la raison pour laquelle l'ordre de `BuildTunnelNetworkStack` reste celui
|
// sortie, ce qui est la raison pour laquelle l'ordre de `BuildTunnelNetworkStack` reste celui
|
||||||
// de l'original, ligne pour ligne.
|
// de l'original, ligne pour ligne.
|
||||||
//
|
//
|
||||||
// TIER 3b AUDIT (2026-09-04): this op is intentionally still separate. Ten operators lie
|
// This op is intentionally separate from 4b. Ten operators lie
|
||||||
// between 4b and this phase (4c through 4h), and every one reads+writes Density. Moving this
|
// between 4b and this phase (4c through 4h), and every one reads+writes Density. Moving this
|
||||||
// phase next to 4b would reorder those density updates, so a bit-identical fuse is not legal.
|
// phase next to 4b would reorder those density updates, so a bit-identical fuse is not legal.
|
||||||
class FFloorBiasMod final : public IVoxelDensityOp
|
class FFloorBiasMod final : public IVoxelDensityOp
|
||||||
@@ -9381,7 +9368,7 @@ namespace VoxelDensityOps
|
|||||||
TUniquePtr<IVoxelBiomeField> BiomeField,
|
TUniquePtr<IVoxelBiomeField> BiomeField,
|
||||||
bool bAppendStructuralPosts)
|
bool bAppendStructuralPosts)
|
||||||
{
|
{
|
||||||
// ARCHÉTYPE COMPLET depuis l'étape 2c : vide + overhang + mélange de biomes.
|
// ARCHÉTYPE COMPLET : vide + overhang + mélange de biomes.
|
||||||
// `PerBiomeParams` vide ⇒ chemin sans biomes, strictement inchangé.
|
// `PerBiomeParams` vide ⇒ chemin sans biomes, strictement inchangé.
|
||||||
TUniquePtr<FSurfaceColumnSource> ColumnSource =
|
TUniquePtr<FSurfaceColumnSource> ColumnSource =
|
||||||
MakeUnique<FSurfaceColumnSource>(P, Seed, PerBiomeParams, MoveTemp(BiomeField));
|
MakeUnique<FSurfaceColumnSource>(P, Seed, PerBiomeParams, MoveTemp(BiomeField));
|
||||||
@@ -9427,8 +9414,7 @@ namespace VoxelDensityOps
|
|||||||
// opérateurs ci-dessous sont ceux de Maze, **repris sans une ligne de changement** —
|
// opérateurs ci-dessous sont ceux de Maze, **repris sans une ligne de changement** —
|
||||||
// `ConstantRock`, `SdfRoughness`, `SdfCarve`. Dans le `switch`, Maze et VerticalShafts sont
|
// `ConstantRock`, `SdfRoughness`, `SdfCarve`. Dans le `switch`, Maze et VerticalShafts sont
|
||||||
// deux fonctions de ~100 lignes qui n'ont rien en commun à l'œil ; en opérateurs, ce sont
|
// deux fonctions de ~100 lignes qui n'ont rien en commun à l'œil ; en opérateurs, ce sont
|
||||||
// les MÊMES trois ops avec une source différente. C'est exactement ce que `§2.5` prédisait
|
// les MÊMES trois ops avec une source différente. C'est exactement ce que `§2.5` prédisait.
|
||||||
// et ce que la Phase 1 avait parié.
|
|
||||||
//
|
//
|
||||||
// THREE of the five ops below are Maze's, reused without a line changed. In the switch,
|
// THREE of the five ops below are Maze's, reused without a line changed. In the switch,
|
||||||
// Maze and VerticalShafts are two unrelated ~100-line functions; as operators they are the
|
// Maze and VerticalShafts are two unrelated ~100-line functions; as operators they are the
|
||||||
@@ -9472,20 +9458,6 @@ namespace VoxelDensityOps
|
|||||||
// plus proche appliqué ; les onze modificateurs concernés y lisent leurs champs au lieu des
|
// plus proche appliqué ; les onze modificateurs concernés y lisent leurs champs au lieu des
|
||||||
// leurs. La rugosité (4b) NON — dans l'original elle précède la déclaration du shadow.
|
// leurs. La rugosité (4b) NON — dans l'original elle précède la déclaration du shadow.
|
||||||
//
|
//
|
||||||
// ⚠️ CE PARAGRAPHE ÉTAIT PÉRIMÉ ET DISAIT LE CONTRAIRE DU CODE (corrigé 2026-08-16).
|
|
||||||
// Il annonçait « étape A sur trois, les douze modificateurs et l'override par salle ne sont
|
|
||||||
// pas encore portés, c'est pour ça que `UsesOperatorStackForChunk` rend **false** pour
|
|
||||||
// TunnelNetwork ». Les trois étapes sont terminées : les douze modificateurs sont ajoutés
|
|
||||||
// douze lignes plus bas, l'override C1 est en place, et `UsesOperatorStackForChunk` rend
|
|
||||||
// **true** pour TunnelNetwork. Un commentaire qui contredit le code sous lui est
|
|
||||||
// exactement le piège « lire le code, pas le commentaire » à l'envers.
|
|
||||||
//
|
|
||||||
// STALE PARAGRAPH REMOVED 2026-08-16. It claimed "stage A of three, the detail modifiers and
|
|
||||||
// the per-room override are not ported yet, which is why the archetype is still off in
|
|
||||||
// UsesOperatorStackForChunk". All three stages are done, the twelve modifiers are added a
|
|
||||||
// dozen lines below, and that function returns TRUE for TunnelNetwork. A comment that
|
|
||||||
// contradicts the code beneath it is the "read the code, not the comment" trap in reverse.
|
|
||||||
//
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
// ⚠️ CE PORTAGE RETIRE L'IDÉE DE « FRAME OPS » (OPSTACK-DECOMPOSITION §1)
|
// ⚠️ CE PORTAGE RETIRE L'IDÉE DE « FRAME OPS » (OPSTACK-DECOMPOSITION §1)
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
@@ -9499,7 +9471,7 @@ namespace VoxelDensityOps
|
|||||||
// qu'ajouter un canal pour éviter une division.
|
// qu'ajouter un canal pour éviter une division.
|
||||||
// Il restait le warp d'îles (§7), déjà gardé local pour la même raison. **Zéro frame sur
|
// Il restait le warp d'îles (§7), déjà gardé local pour la même raison. **Zéro frame sur
|
||||||
// trois candidats** : ce n'était pas une infrastructure manquante, c'était trois fois la
|
// trois candidats** : ce n'était pas une infrastructure manquante, c'était trois fois la
|
||||||
// même chose vue de loin. Noté ici plutôt que laissé en TODO permanent.
|
// même chose vue de loin.
|
||||||
constexpr float CarveMinDivisor = 1.0f; // TunnelNetwork plancher son diviseur, cf. FSdfConvertOp
|
constexpr float CarveMinDivisor = 1.0f; // TunnelNetwork plancher son diviseur, cf. FSdfConvertOp
|
||||||
|
|
||||||
// La source de salles est retenue par pointeur non possédant : les terrasses re-interrogent
|
// La source de salles est retenue par pointeur non possédant : les terrasses re-interrogent
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ void FVoxelForgeModule::StartupModule()
|
|||||||
RequestedFma3,
|
RequestedFma3,
|
||||||
Fma3After);
|
Fma3After);
|
||||||
#endif
|
#endif
|
||||||
// These parsers used to be entered lazily by worker-side density/mesher calls. Resolve the
|
// Resolve the command line once while the module is starting, before any generation task is
|
||||||
// command line once while the module is starting, before any generation task is submitted.
|
// submitted, instead of lazily from worker-side density/mesher calls.
|
||||||
VoxelGenLOD::InitializeConsoleSwitches();
|
VoxelGenLOD::InitializeConsoleSwitches();
|
||||||
UVoxelMarchingCubesMesher::InitializeConsoleSwitches();
|
UVoxelMarchingCubesMesher::InitializeConsoleSwitches();
|
||||||
UE_LOG(LogTemp, Log, TEXT("VoxelForge module started!"));
|
UE_LOG(LogTemp, Log, TEXT("VoxelForge module started!"));
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
#include "VoxelWormField.h"
|
#include "VoxelWormField.h"
|
||||||
#include "VoxelDensityAblation.h" // fingerprinted development-only stage measurements
|
#include "VoxelDensityAblation.h" // fingerprinted development-only stage measurements
|
||||||
#include "VoxelDensityPrimitives.h" // spine / seals / passage — shared with the operator stack
|
#include "VoxelDensityPrimitives.h" // spine / seals / passage — shared with the operator stack
|
||||||
#include "VoxelDensityOpStack.h" // OPSTACK Phase 1: the opt-in per-strate operator stack
|
#include "VoxelDensityOpStack.h" // the opt-in per-strate operator stack
|
||||||
#include "VoxelHeightOp.h" // IVoxelBiomeField — the adapter below implements it
|
#include "VoxelHeightOp.h" // IVoxelBiomeField — the adapter below implements it
|
||||||
#include "VoxelStats.h"
|
#include "VoxelStats.h"
|
||||||
#include "VoxelDensityProfile.h"
|
#include "VoxelDensityProfile.h"
|
||||||
@@ -969,12 +969,11 @@ static float RidgedNoise3D(const FVector3f& Position, int32 Octaves = 4,
|
|||||||
// Using F2-F1 (difference of two closest distances) gives smooth cell
|
// Using F2-F1 (difference of two closest distances) gives smooth cell
|
||||||
// boundaries with ridges between cells — more interesting than raw distance.
|
// boundaries with ridges between cells — more interesting than raw distance.
|
||||||
|
|
||||||
// Le CORPS a déménagé dans Public/VoxelCaveMorphology.h (namespace VoxelNoise), au plus bas point
|
// Le CORPS vit dans Public/VoxelCaveMorphology.h (namespace VoxelNoise), au plus bas point
|
||||||
// qui voit déjà `VoxelHash` : la pile d'opérateurs a besoin exactement du même bruit pour la
|
// qui voit déjà `VoxelHash` : la pile d'opérateurs a besoin exactement du même bruit pour la
|
||||||
// rugosité (4b, type Cellular) et pour les festons (4f), et deux copies d'une fonction pure finissent
|
// rugosité (4b, type Cellular) et pour les festons (4f), et deux copies d'une fonction pure finissent
|
||||||
// par diverger — c'est littéralement `AUDIT §C1`. Ce forwarder garde les ~3 sites d'appel ci-dessous
|
// par diverger — c'est littéralement `AUDIT §C1`. Ce forwarder garde les ~3 sites d'appel ci-dessous
|
||||||
// inchangés, comme l'ont fait FractalNoise3D et RidgedNoise3D lors de T2.a. Le chemin chaud garde
|
// inchangés, comme FractalNoise3D et RidgedNoise3D. Le chemin chaud garde les coordonnées en float.
|
||||||
// maintenant les coordonnées en float : la compatibilité d'arrondi historique est abandonnée.
|
|
||||||
static float CellularNoise3D(const FVector3f& Position)
|
static float CellularNoise3D(const FVector3f& Position)
|
||||||
{
|
{
|
||||||
return VoxelNoise::Cellular3D(Position);
|
return VoxelNoise::Cellular3D(Position);
|
||||||
@@ -1198,9 +1197,9 @@ static void ApplyDisturbances(float& MC, float X, float Y, float Z,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The disturbance pass is deterministic and spatially sparse. ClassifyTile used to treat a
|
// The disturbance pass is deterministic and spatially sparse. Treating a non-zero density as a
|
||||||
// non-zero density as a world-wide influence, which made an otherwise proven all-solid/all-air
|
// world-wide influence would make an otherwise proven all-solid/all-air LOD0 box fall back to the
|
||||||
// LOD0 box fall back to the full mesher. These predicates are deliberately one-sided: they only
|
// full mesher. These predicates are deliberately one-sided: they only
|
||||||
// prove that no hash-placed primitive can touch the queried box. An intersecting candidate, an
|
// prove that no hash-placed primitive can touch the queried box. An intersecting candidate, an
|
||||||
// invalid range, or an unknown parameter keeps the conservative Mixed result.
|
// invalid range, or an unknown parameter keeps the conservative Mixed result.
|
||||||
static FORCEINLINE bool VF_DisturbanceBandTouchesBox(
|
static FORCEINLINE bool VF_DisturbanceBandTouchesBox(
|
||||||
@@ -2860,7 +2859,7 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
|
|||||||
// tile-window context. A cache hit moves the prepared stack/core out of these TLS values;
|
// tile-window context. A cache hit moves the prepared stack/core out of these TLS values;
|
||||||
// when the other context misses, this bit must force the rebuild guard below to run.
|
// when the other context misses, this bit must force the rebuild guard below to run.
|
||||||
thread_local bool CP_UsesTileCacheWindow = false;
|
thread_local bool CP_UsesTileCacheWindow = false;
|
||||||
// OPSTACK Phase 1 — la pile d'opérateurs, construite dans le MÊME bloc de refetch que les
|
// OPSTACK — la pile d'opérateurs, construite dans le MÊME bloc de refetch que les
|
||||||
// params (donc même clé owner+chunk+version, aucune logique d'invalidation en plus). Vide tant que
|
// params (donc même clé owner+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é.
|
// la strate n'a pas coché `bUseOperatorStack` ET que son archétype n'est pas porté.
|
||||||
thread_local FVoxelOpStack CP_OpStack;
|
thread_local FVoxelOpStack CP_OpStack;
|
||||||
@@ -3086,7 +3085,7 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
|
|||||||
}
|
}
|
||||||
CP_Dist = StrateManager->GetDisturbanceParamsForChunk(ChunkCoord);
|
CP_Dist = StrateManager->GetDisturbanceParamsForChunk(ChunkCoord);
|
||||||
|
|
||||||
// ── OPSTACK Phase 1 : (re)construire la pile si cette strate l'a demandée. ──
|
// ── OPSTACK : (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
|
// 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.
|
// par chunk (comme le refetch de params juste au-dessus), jamais par voxel.
|
||||||
CP_UseOpStack = bUseChunkRoutingCache
|
CP_UseOpStack = bUseChunkRoutingCache
|
||||||
@@ -3999,7 +3998,7 @@ float UVoxelGenerator::GetDensityWithParams(float WorldX, float WorldY, float Wo
|
|||||||
thread_local float CachedSMinY = 0.0f, CachedSMaxY = 0.0f;
|
thread_local float CachedSMinY = 0.0f, CachedSMaxY = 0.0f;
|
||||||
thread_local int32 CachedStrate = INT32_MIN;
|
thread_local int32 CachedStrate = INT32_MIN;
|
||||||
thread_local uint32 CachedSeed = 0;
|
thread_local uint32 CachedSeed = 0;
|
||||||
// ⚠️ AUDIT §C2 (corrigé le 2026-07-28). Les deux lignes qui manquaient à cette clé.
|
// ⚠️ AUDIT §C2 — `CachedFingerprint` et `CachedLayout` ci-dessous sont OBLIGATOIRES dans cette clé.
|
||||||
// La clé ci-dessus décrit la GÉOMÉTRIE de la fenêtre (boîte, strate, seed) et rien de ce qui
|
// La clé ci-dessus décrit la GÉOMÉTRIE de la fenêtre (boîte, strate, seed) et rien de ce qui
|
||||||
// détermine les PARAMS avec lesquels les salles ont été cuites. Comme `GetGenerationParams`
|
// détermine les PARAMS avec lesquels les salles ont été cuites. Comme `GetGenerationParams`
|
||||||
// blende à l'intérieur d'une strate (`Alpha` = f(chunk Z), et f(chunk XY) aussi en
|
// blende à l'intérieur d'une strate (`Alpha` = f(chunk Z), et f(chunk XY) aussi en
|
||||||
@@ -4010,7 +4009,7 @@ float UVoxelGenerator::GetDensityWithParams(float WorldX, float WorldY, float Wo
|
|||||||
// Pourquoi ça ne casse PAS l'invariant de perf de §8.10 : la clé reste une BOÎTE, donc les
|
// Pourquoi ça ne casse PAS l'invariant de perf de §8.10 : la clé reste une BOÎTE, donc les
|
||||||
// sondes de gradient à `WorldX ± 1` ne font toujours pas tourner le cache. Ce qui le fait
|
// sondes de gradient à `WorldX ± 1` ne font toujours pas tourner le cache. Ce qui le fait
|
||||||
// tourner en plus, c'est un changement RÉEL de params — une fois par chunk dans une bande de
|
// tourner en plus, c'est un changement RÉEL de params — une fois par chunk dans une bande de
|
||||||
// transition, ce qui est le nombre de reconstructions que ce cache aurait toujours dû faire.
|
// transition, ce qui est exactement le nombre de reconstructions nécessaire.
|
||||||
thread_local uint32 CachedFingerprint = 0xFFFFFFFFu;
|
thread_local uint32 CachedFingerprint = 0xFFFFFFFFu;
|
||||||
thread_local uint32 CachedLayout = 0xFFFFFFFFu;
|
thread_local uint32 CachedLayout = 0xFFFFFFFFu;
|
||||||
thread_local bool CachedUsesFusedCacheWindow = false;
|
thread_local bool CachedUsesFusedCacheWindow = false;
|
||||||
@@ -4518,7 +4517,7 @@ float UVoxelGenerator::GetDensityWithParams(float WorldX, float WorldY, float Wo
|
|||||||
//
|
//
|
||||||
// We copy Params and apply the nearest room's terrain op on top.
|
// We copy Params and apply the nearest room's terrain op on top.
|
||||||
// Base Params has all terrain op fields = 0 (disabled) since
|
// Base Params has all terrain op fields = 0 (disabled) since
|
||||||
// BuildParamsFromDefinition no longer merges them globally.
|
// BuildParamsFromDefinition does not merge them globally.
|
||||||
//
|
//
|
||||||
// Result: voxels inside different rooms see different terrain ops.
|
// Result: voxels inside different rooms see different terrain ops.
|
||||||
// Rooms with no assigned op leave terrain fields at 0 → no terrain op. Clean.
|
// Rooms with no assigned op leave terrain fields at 0 → no terrain op. Clean.
|
||||||
@@ -5931,10 +5930,9 @@ bool UVoxelGenerator::GetSurfaceHeightAt(float WorldX, float WorldY, int32 Chunk
|
|||||||
//=============================================================================
|
//=============================================================================
|
||||||
// ~84 % des tuiles générées sortent VIDES (tout-roc sous le terrain, tout-air
|
// ~84 % des tuiles générées sortent VIDES (tout-roc sous le terrain, tout-air
|
||||||
// au-dessus, cap solide) mais payaient quand même le pré-échantillonnage 33³+
|
// au-dessus, cap solide) mais payaient quand même le pré-échantillonnage 33³+
|
||||||
// complet (trace 2026-07-05 : 83 925 GenerateMesh pour 13 227 maillages réels).
|
// complet (une trace : 83 925 GenerateMesh pour 13 227 maillages réels).
|
||||||
// Une 1re tentative (2026-06-26) a été REVERTÉE : borne analytique GLOBALE du
|
// ⚠️ Pas de borne analytique GLOBALE du plafond : pas assez conservative (cap
|
||||||
// plafond pas assez conservative (cap bas ⇒ trous dans le toit). Ici on suit la
|
// bas ⇒ trous dans le toit). Les colonnes sont donc ÉCHANTILLONNÉES sur le treillis
|
||||||
// prescription du revert : les colonnes sont ÉCHANTILLONNÉES sur le treillis
|
|
||||||
// exact du mesher (mêmes fonctions ⇒ mêmes floats ⇒ verdict exact, pas une
|
// exact du mesher (mêmes fonctions ⇒ mêmes floats ⇒ verdict exact, pas une
|
||||||
// estimation), et le cas tout-solide porte des gardes spine/passages/
|
// estimation), et le cas tout-solide porte des gardes spine/passages/
|
||||||
// disturbances/diff. Tout ce qui n'est pas prouvable ⇒ Mixed (le seul coût d'un
|
// disturbances/diff. Tout ce qui n'est pas prouvable ⇒ Mixed (le seul coût d'un
|
||||||
@@ -6293,9 +6291,9 @@ static EVoxelTileClass VF_ClassifyBoxRefined(const FVoxelOpStack& Stack,
|
|||||||
&& (StructuralManager->AnyLandingFloorAtLattice(
|
&& (StructuralManager->AnyLandingFloorAtLattice(
|
||||||
Box, Context.LatticeOriginVoxels, Context.Step)
|
Box, Context.LatticeOriginVoxels, Context.Step)
|
||||||
// The passage's authored D-floor is a final MC-space writer, not part of the
|
// The passage's authored D-floor is a final MC-space writer, not part of the
|
||||||
// interpreted stack. It was deliberately left out of the old support-floor query
|
// interpreted stack, and no support-floor query covers it; omitting the
|
||||||
// when the legacy slab was removed; omitting the native-floor reach here lets an
|
// native-floor reach here lets an exact core-lattice AllAir claim skip a tile
|
||||||
// exact core-lattice AllAir claim skip a tile whose final density is solid.
|
// whose final density is solid.
|
||||||
|| StructuralManager->AnyPassageStructuralPostNearLattice(
|
|| StructuralManager->AnyPassageStructuralPostNearLattice(
|
||||||
Box, Context.LatticeOriginVoxels, Context.Step));
|
Box, Context.LatticeOriginVoxels, Context.Step));
|
||||||
const bool bHasStructuralCandidate = Whole == EVoxelTileClass::AllSolid
|
const bool bHasStructuralCandidate = Whole == EVoxelTileClass::AllSolid
|
||||||
@@ -6994,14 +6992,14 @@ EVoxelTileClass UVoxelGenerator::ClassifyTileUncached(
|
|||||||
// même endroit, que `GetDensityAt`.
|
// même endroit, que `GetDensityAt`.
|
||||||
if (!StrateManager->UsesOperatorStackForChunk(CC))
|
if (!StrateManager->UsesOperatorStackForChunk(CC))
|
||||||
{
|
{
|
||||||
// Attribution DIAGNOSTIQUE uniquement : l'ancien compteur mélangeait une
|
// Attribution DIAGNOSTIQUE uniquement : distingue une strate cave entièrement
|
||||||
// strate cave entièrement désactivée avec une tuile de frontière qui avait
|
// désactivée d'une tuile de frontière qui a rencontré un slot désactivé avant
|
||||||
// rencontré un slot désactivé avant la garde « slot différent » ci-dessous.
|
// la garde « slot différent » ci-dessous.
|
||||||
// On résout les bornes APRÈS l'échec du même prédicat ; elles ne changent ni
|
// On résout les bornes APRÈS l'échec du même prédicat ; elles ne changent ni
|
||||||
// la condition, ni le point de retour, ni le verdict.
|
// la condition, ni le point de retour, ni le verdict.
|
||||||
// Diagnostic attribution only: the old counter mixed a wholly disabled cave
|
// Diagnostic attribution only: separates a wholly disabled cave slot from a
|
||||||
// slot with a boundary tile that met a disabled slot before the different-slot
|
// boundary tile that met a disabled slot before the different-slot guard
|
||||||
// guard below. Resolve bounds only after the same predicate fails; classification
|
// below. Resolve bounds only after the same predicate fails; classification
|
||||||
// control flow and return value stay unchanged.
|
// control flow and return value stay unchanged.
|
||||||
int32 FailedTopCZ = 0, FailedBotCZ = 0;
|
int32 FailedTopCZ = 0, FailedBotCZ = 0;
|
||||||
if (!StrateManager->GetStrateChunkZBounds(ChunkZ, FailedTopCZ, FailedBotCZ))
|
if (!StrateManager->GetStrateChunkZBounds(ChunkZ, FailedTopCZ, FailedBotCZ))
|
||||||
@@ -7140,8 +7138,8 @@ EVoxelTileClass UVoxelGenerator::ClassifyTileUncached(
|
|||||||
bCanAir = false;
|
bCanAir = false;
|
||||||
}
|
}
|
||||||
// Native passage D-floors are authored structural posts applied after the operator stack.
|
// Native passage D-floors are authored structural posts applied after the operator stack.
|
||||||
// They are not the removed legacy support slab, but they still add solid MC samples and
|
// They add solid MC samples and therefore must kill the AllAir hypothesis before a tile can
|
||||||
// therefore must kill the AllAir hypothesis before a tile can be skipped.
|
// be skipped.
|
||||||
if (bHasPassageStructuralPostCandidate)
|
if (bHasPassageStructuralPostCandidate)
|
||||||
{
|
{
|
||||||
bCanAir = false;
|
bCanAir = false;
|
||||||
@@ -7179,7 +7177,7 @@ EVoxelTileClass UVoxelGenerator::ClassifyTileUncached(
|
|||||||
// `GetGenerationParams` et ses homologues BLENDENT les params dans les bandes de transition :
|
// `GetGenerationParams` et ses homologues BLENDENT les params dans les bandes de transition :
|
||||||
// `Alpha` dépend du chunk Z pour `Gradient`, et du chunk XY EN PLUS pour `Interleaved`. Deux
|
// `Alpha` dépend du chunk Z pour `Gradient`, et du chunk XY EN PLUS pour `Interleaved`. Deux
|
||||||
// chunks d'une même sous-boîte peuvent donc porter des params différents — c'est le constat de
|
// chunks d'une même sous-boîte peuvent donc porter des params différents — c'est le constat de
|
||||||
// `AUDIT §C2`, confirmé par lecture le 2026-07-28 — et UNE pile ne peut pas représenter DEUX
|
// `AUDIT §C2` — et UNE pile ne peut pas représenter DEUX
|
||||||
// champs. On construit donc les params pour CHAQUE coordonnée de chunk que la boîte touche et
|
// champs. On construit donc les params pour CHAQUE coordonnée de chunk que la boîte touche et
|
||||||
// on exige qu'ils soient identiques bit à bit.
|
// on exige qu'ils soient identiques bit à bit.
|
||||||
//
|
//
|
||||||
@@ -8383,12 +8381,10 @@ float UVoxelGenerator::GetFloatingIslandDensity(float WorldX, float WorldY, floa
|
|||||||
// organic instead of perfect circles. Computed once per voxel and shared by all nearby
|
// organic instead of perfect circles. Computed once per voxel and shared by all nearby
|
||||||
// islands (each samples a different part of the field → distinct silhouettes).
|
// islands (each samples a different part of the field → distinct silhouettes).
|
||||||
const float WarpAmp = (Params.IslandMinRadius + Params.IslandMaxRadius) * 0.5f * 0.35f;
|
const float WarpAmp = (Params.IslandMinRadius + Params.IslandMaxRadius) * 0.5f * 0.35f;
|
||||||
// ⚠️ AUDIT §C1 — DERNIER SITE DU PLUGIN, trouvé en portant cet archétype (2026-07-28). Le
|
// ⚠️ AUDIT §C1 — ne pas écrire l'offset de seed `(float)S * K` : à Seed = 2e9 le terme atteint
|
||||||
// balayage du 2026-07-27 cherchait le motif `SeedF * K` et celui-ci s'écrit `(float)S * K`, donc
|
// ~1.4e6, où l'ULP du float vaut 0.125 contre un pas de 0.04 par voxel — le warp s'aplatit et
|
||||||
// il a survécu : à Seed = 2e9 le terme atteint ~1.4e6, où l'ULP du float vaut 0.125 contre un pas
|
// les îles redeviennent des cercles parfaits. `VoxelHash::SeedOffset` est utilisé dans les DEUX
|
||||||
// de 0.04 par voxel — le warp s'aplatit et les îles redeviennent des cercles parfaits. Corrigé
|
// chemins (ici et FIslandBlobSource), pour que le test d'équivalence reste un oracle valable.
|
||||||
// dans les DEUX chemins (ici et FIslandBlobSource) en une passe, pour que le test d'équivalence
|
|
||||||
// reste un oracle valable.
|
|
||||||
// NOTE : `SeedOffset` quantifie la clé de site par ×100, donc 0.0007 → site 0. Unique aujourd'hui
|
// NOTE : `SeedOffset` quantifie la clé de site par ×100, donc 0.0007 → site 0. Unique aujourd'hui
|
||||||
// (toutes les autres clés du plugin sont ≥ 0.19) ; la prochaine clé sous 0.005 devra en choisir
|
// (toutes les autres clés du plugin sont ≥ 0.19) ; la prochaine clé sous 0.005 devra en choisir
|
||||||
// une autre plutôt que de collisionner en silence.
|
// une autre plutôt que de collisionner en silence.
|
||||||
|
|||||||
@@ -91,8 +91,7 @@ void UVoxelMarchingCubesMesher::InitializeConsoleSwitches()
|
|||||||
//=============================================================================
|
//=============================================================================
|
||||||
// MAIN ALGORITHM
|
// MAIN ALGORITHM
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
// (L'ancien trio GetDensity / InterpolateEdge / ComputeGradientNormal a été retiré :
|
// La grille pré-échantillonnée fournit positions ET gradients.
|
||||||
// mort depuis T1.b — la grille pré-échantillonnée fournit positions ET gradients.)
|
|
||||||
|
|
||||||
FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels, int32 Step, int32 InCellsPerAxis,
|
FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels, int32 Step, int32 InCellsPerAxis,
|
||||||
TArray<uint8>* OutCaptureGrid,
|
TArray<uint8>* OutCaptureGrid,
|
||||||
@@ -141,8 +140,8 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
|||||||
//=========================================================================
|
//=========================================================================
|
||||||
// Le discriminant est SÉMANTIQUE, pas géométrique (fable-idea F17) : un vertex orienté vers
|
// Le discriminant est SÉMANTIQUE, pas géométrique (fable-idea F17) : un vertex orienté vers
|
||||||
// le bas est un sky-cap seulement s'il est proche de CeilSurf ; proche de TerrainZ c'est un
|
// le bas est un sky-cap seulement s'il est proche de CeilSurf ; proche de TerrainZ c'est un
|
||||||
// surplomb de terrain (reste "sol" — l'ancien vote par tuile mettait le matériau ciel sous
|
// surplomb de terrain (reste "sol" — un vote par tuile mettrait le matériau ciel sous les
|
||||||
// les surplombs des tuiles majoritairement plafond). Un futur toit de grotte (aussi down-
|
// surplombs des tuiles majoritairement plafond). Un futur toit de grotte (aussi down-
|
||||||
// facing, mais SOUS TerrainZ) tombera correctement côté "sol/roche" par la même règle.
|
// facing, mais SOUS TerrainZ) tombera correctement côté "sol/roche" par la même règle.
|
||||||
// Coût : GetSurfaceHeightAt (pile XY complète) UNIQUEMENT pour les vertex down-facing,
|
// Coût : GetSurfaceHeightAt (pile XY complète) UNIQUEMENT pour les vertex down-facing,
|
||||||
// mémoïsé par colonne quantifiée au pas de la grille → ≤ (colonnes touchées) appels ;
|
// mémoïsé par colonne quantifiée au pas de la grille → ≤ (colonnes touchées) appels ;
|
||||||
|
|||||||
@@ -4872,7 +4872,7 @@ FVoxelStrateRollInfo VF_RollStrateParamsDetailed(const FVoxelStrateCorpus& Corpu
|
|||||||
Repeat.ArchetypeParams, Result.ArchetypeParams, Result.Archetype)
|
Repeat.ArchetypeParams, Result.ArchetypeParams, Result.Archetype)
|
||||||
&& Repeat.ParentEntryIndices == Result.ParentEntryIndices
|
&& Repeat.ParentEntryIndices == Result.ParentEntryIndices
|
||||||
&& Repeat.ParentWeights == Result.ParentWeights,
|
&& Repeat.ParentWeights == Result.ParentWeights,
|
||||||
TEXT("VF_RollStrateParams lost determinism for the same corpus/seed/index."));
|
TEXT("VF_RollStrateParamsDetailed lost determinism for the same corpus/seed/index."));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return Result;
|
return Result;
|
||||||
|
|||||||
@@ -234,8 +234,8 @@ namespace
|
|||||||
{
|
{
|
||||||
// One projection result per passage is enough for the current voxel sample. The surrounding
|
// One projection result per passage is enough for the current voxel sample. The surrounding
|
||||||
// passage cache already moves with (manager, layout, chunk); a generation invalidates old flags
|
// passage cache already moves with (manager, layout, chunk); a generation invalidates old flags
|
||||||
// before any caller can observe them. Keeping the entries indexed by passage avoids the TMap
|
// before any caller can observe them. Keeping the entries indexed by passage avoids a TMap
|
||||||
// rehash path that this hot loop used to carry, while a collision-free hit remains exact.
|
// rehash path in this hot loop, while a collision-free hit remains exact.
|
||||||
struct FPassageFloorProjectionCacheEntry
|
struct FPassageFloorProjectionCacheEntry
|
||||||
{
|
{
|
||||||
// The sample key lives once in FPassageEvaluationCache. A monotonically increasing
|
// The sample key lives once in FPassageEvaluationCache. A monotonically increasing
|
||||||
@@ -1804,13 +1804,13 @@ void UVoxelStrateManager::GeneratePassages()
|
|||||||
case ECaveGeneratorType::TunnelNetwork:
|
case ECaveGeneratorType::TunnelNetwork:
|
||||||
case ECaveGeneratorType::Underwater:
|
case ECaveGeneratorType::Underwater:
|
||||||
// Une salle est une CIBLE ÉPARSE en XY, exactement comme un couloir de labyrinthe : le
|
// Une salle est une CIBLE ÉPARSE en XY, exactement comme un couloir de labyrinthe : le
|
||||||
// budget doit donc être réel. Zéro ici était un bug — la requête trouvait la salle la
|
// budget doit donc être réel. Zéro ici est un bug — la requête trouve la salle la plus
|
||||||
// plus proche puis atterrissait à côté d'elle dans 92,8 % des cas (balayage d'un million
|
// proche puis atterrit à côté d'elle dans 92,8 % des cas (balayage d'un million de
|
||||||
// de seeds). Un espacement de salles est le voisinage local naturel.
|
// seeds). Un espacement de salles est le voisinage local naturel.
|
||||||
//
|
//
|
||||||
// A room is an XY-SPARSE target, exactly like a maze corridor, so the budget must be
|
// A room is an XY-SPARSE target, exactly like a maze corridor, so the budget must be
|
||||||
// real. Zero here was the bug: the query found the nearest room and then landed beside
|
// real. Zero here is a bug: the query finds the nearest room and then lands beside it
|
||||||
// it 92.8% of the time (million-seed sweep). One room spacing is the natural local
|
// 92.8% of the time (million-seed sweep). One room spacing is the natural local
|
||||||
// neighbourhood, and it protects the configured spine-distance distribution.
|
// neighbourhood, and it protects the configured spine-distance distribution.
|
||||||
return FMath::Max(Definition.GenerationParams.RoomSpacing, 1.0f);
|
return FMath::Max(Definition.GenerationParams.RoomSpacing, 1.0f);
|
||||||
|
|
||||||
@@ -2771,9 +2771,9 @@ float UVoxelStrateManager::EvaluateModifierSDF(float WorldX, float WorldY, float
|
|||||||
{
|
{
|
||||||
const FVoxelPassage& P = PassageData[PIdx];
|
const FVoxelPassage& P = PassageData[PIdx];
|
||||||
// BOUNDING-SPHERE REJECT: skip passages this voxel can't possibly be inside.
|
// BOUNDING-SPHERE REJECT: skip passages this voxel can't possibly be inside.
|
||||||
// EvaluateModifierSDF runs PER VOXEL and used to evaluate every passage's full
|
// EvaluateModifierSDF runs PER VOXEL; evaluating every passage's full capsule chain
|
||||||
// capsule chain unconditionally — the dominant lag source once passages became
|
// unconditionally is the dominant lag source with 12-segment worm passages. Far
|
||||||
// 12-segment worms. Now far passages cost a single squared-distance compare.
|
// passages cost a single squared-distance compare.
|
||||||
if (FVector::DistSquared(Pos, P.BoundCenter) > P.BoundRadiusSq) continue;
|
if (FVector::DistSquared(Pos, P.BoundCenter) > P.BoundRadiusSq) continue;
|
||||||
if (VoxelDensityProfile::AreCountersEnabled())
|
if (VoxelDensityProfile::AreCountersEnabled())
|
||||||
{
|
{
|
||||||
@@ -3254,8 +3254,8 @@ void UVoxelStrateManager::ApplyPassageStructuralPostsMC(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The graph tunnel no longer exposes a separate support slab. Inter-strate landing carving
|
// The graph tunnel does not expose a separate support slab. Inter-strate landing carving
|
||||||
// remains independent and is still composed here when its own reach proof allows it.
|
// is independent and is composed here when its own reach proof allows it.
|
||||||
if (!bDisableLandingPosts
|
if (!bDisableLandingPosts
|
||||||
&& !bAnyLandingFloor && !bLegacyTunnelSupportFloor && MinLandingSDF < FLT_MAX)
|
&& !bAnyLandingFloor && !bLegacyTunnelSupportFloor && MinLandingSDF < FLT_MAX)
|
||||||
{
|
{
|
||||||
@@ -4773,7 +4773,7 @@ bool UVoxelStrateManager::AnyPassageAirPostNearLattice(
|
|||||||
|
|
||||||
// The union above is conservative but can be much smaller than the queried box. Restrict the
|
// The union above is conservative but can be much smaller than the queried box. Restrict the
|
||||||
// exact walk to its lattice intersection; a false positive still pays the exact predicates,
|
// exact walk to its lattice intersection; a false positive still pays the exact predicates,
|
||||||
// while a far-away long-passage sphere no longer does.
|
// while a far-away long-passage sphere does not.
|
||||||
if (!VF_LatticeAxisRange(
|
if (!VF_LatticeAxisRange(
|
||||||
FMath::Max((float)VoxelBox.Min.X, PotentialMinX),
|
FMath::Max((float)VoxelBox.Min.X, PotentialMinX),
|
||||||
FMath::Min((float)VoxelBox.Max.X, PotentialMaxX),
|
FMath::Min((float)VoxelBox.Max.X, PotentialMaxX),
|
||||||
@@ -6307,22 +6307,21 @@ bool UVoxelStrateManager::UsesOperatorStackForChunk(const FIntVector& ChunkCoord
|
|||||||
// and falls back to the switch, so the box can be ticked anywhere without breaking anything.
|
// and falls back to the switch, so the box can be ticked anywhere without breaking anything.
|
||||||
switch (Def->GeneratorType)
|
switch (Def->GeneratorType)
|
||||||
{
|
{
|
||||||
case ECaveGeneratorType::Maze: return true; // Phase 1
|
case ECaveGeneratorType::Maze: return true;
|
||||||
case ECaveGeneratorType::FlatPlain: // Phase 2 — les deux partagent
|
case ECaveGeneratorType::FlatPlain: // les deux partagent
|
||||||
case ECaveGeneratorType::CrystalChamber: return true; // UNE seule pile (BuildSlabStack)
|
case ECaveGeneratorType::CrystalChamber: return true; // UNE seule pile (BuildSlabStack)
|
||||||
|
|
||||||
case ECaveGeneratorType::SurfaceWorld:
|
case ECaveGeneratorType::SurfaceWorld:
|
||||||
// ✅ La garde « pas de biomes » est TOMBÉE (étape 2c) : le combiner `Mask` existe, donc une
|
// Pas de garde « pas de biomes » : le combiner `Mask` existe, donc une strate à biomes
|
||||||
// strate à biomes mélange bien ses hauteurs comme le chemin d'origine. Les trois archétypes
|
// mélange bien ses hauteurs comme le chemin d'origine.
|
||||||
// du dessus plus celui-ci font 5 des 8 portés.
|
// No no-biome guard: the Mask combiner exists, so a biome strate blends its heights
|
||||||
// The no-biome guard is GONE: the Mask combiner exists, so a biome strate blends its heights
|
|
||||||
// exactly as the original path does.
|
// exactly as the original path does.
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case ECaveGeneratorType::VerticalShafts: return true; // Phase 2 — 3 ops repris de Maze tels quels
|
case ECaveGeneratorType::VerticalShafts: return true; // 3 ops repris de Maze tels quels
|
||||||
|
|
||||||
case ECaveGeneratorType::FloatingIslands:
|
case ECaveGeneratorType::FloatingIslands:
|
||||||
// Phase 2 — la pile qui tourne à l'ENVERS : source de VIDE + fill, au lieu de source de ROC
|
// La pile qui tourne à l'ENVERS : source de VIDE + fill, au lieu de source de ROC
|
||||||
// + carve, avec les MÊMES opérateurs au signe près.
|
// + carve, avec les MÊMES opérateurs au signe près.
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -6331,15 +6330,13 @@ bool UVoxelStrateManager::UsesOperatorStackForChunk(const FIntVector& ChunkCoord
|
|||||||
// côté rendu. `GetDensityAt` les met dans le même `case`, et `WaterLevelRelative` n'est lu
|
// côté rendu. `GetDensityAt` les met dans le même `case`, et `WaterLevelRelative` n'est lu
|
||||||
// que par `GetWaterLevel*` de ce manager — jamais par la densité (vérifié, pas supposé).
|
// que par `GetWaterLevel*` de ce manager — jamais par la densité (vérifié, pas supposé).
|
||||||
case ECaveGeneratorType::TunnelNetwork:
|
case ECaveGeneratorType::TunnelNetwork:
|
||||||
// Phase 2, LE DERNIER, et le plus gros : ~1080 lignes portées en trois étapes (squelette
|
// Le plus gros : squelette SDF → douze modificateurs de détail → override d'op par salle,
|
||||||
// SDF → douze modificateurs de détail → override d'op par salle), 19 opérateurs, dont
|
// 19 opérateurs, dont `FRoomGraphSource` qui **APPELLE** `BuildChunkCache`/`EvaluateSDFCached`
|
||||||
// `FRoomGraphSource` qui **APPELLE** `BuildChunkCache`/`EvaluateSDFCached` au lieu de les
|
// au lieu de les transcrire — c'est là que vit la discipline d'invariance de fenêtre
|
||||||
// transcrire — c'est là que vit la discipline d'invariance de fenêtre d'ARCHITECTURE §8.4,
|
// d'ARCHITECTURE §8.4 ; en forker une copie la casserait.
|
||||||
// et en forker une copie aurait été le pire résultat possible de ce refactor.
|
|
||||||
//
|
//
|
||||||
// **8 SUR 8.** Le `switch` d'archétypes a désormais un jumeau en pile d'opérateurs, opt-in
|
// **8 SUR 8.** Le `switch` d'archétypes a un jumeau en pile d'opérateurs, opt-in par
|
||||||
// par strate, chacun vérifié par un test d'équivalence bit à bit contre sa fonction
|
// strate, chacun vérifié par un test d'équivalence bit à bit contre sa fonction d'origine.
|
||||||
// d'origine. Ce qui n'est PAS fait : `ClassifyTile` n'utilise toujours pas `ClassifyBox`.
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default: return false;
|
default: return false;
|
||||||
@@ -6903,7 +6900,7 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
|
|||||||
//=============================================================================
|
//=============================================================================
|
||||||
// Returns the definition's base GenerationParams (cave shape, SDF, roughness, etc.).
|
// Returns the definition's base GenerationParams (cave shape, SDF, roughness, etc.).
|
||||||
//
|
//
|
||||||
// NOTE: Terrain op fields (TerraceStepHeight, ColumnDensity, etc.) are no longer
|
// NOTE: Terrain op fields (TerraceStepHeight, ColumnDensity, etc.) are not
|
||||||
// merged here. They default to 0 (disabled) in the base params, and are applied
|
// merged here. They default to 0 (disabled) in the base params, and are applied
|
||||||
// per-room during BuildChunkCache() via FCachedRoom::RoomOp — each room hash-rolls
|
// per-room during BuildChunkCache() via FCachedRoom::RoomOp — each room hash-rolls
|
||||||
// one op from the strate's probability pool (FStrateTerrainOpEntry::Probability).
|
// one op from the strate's probability pool (FStrateTerrainOpEntry::Probability).
|
||||||
|
|||||||
@@ -23,11 +23,10 @@
|
|||||||
#include "VoxelTilePostReach.h"
|
#include "VoxelTilePostReach.h"
|
||||||
#include "VoxelClipmapDesiredTiles.h"
|
#include "VoxelClipmapDesiredTiles.h"
|
||||||
// IWYU (FPSemantics = Precise ⇒ plus de PCH partagé) : GetPlayerPosition déréférence le pawn, donc
|
// IWYU (FPSemantics = Precise ⇒ plus de PCH partagé) : GetPlayerPosition déréférence le pawn, donc
|
||||||
// APawn doit être COMPLET — `Casts.h` n'en donne qu'une déclaration avant. APlayerController était
|
// APawn doit être COMPLET — `Casts.h` n'en donne qu'une déclaration avant. APlayerController n'est
|
||||||
// complet par transitivité seulement : on l'inclut explicitement, c'est exactement la fragilité
|
// complet que par transitivité : on l'inclut explicitement.
|
||||||
// qu'on est en train de retirer.
|
|
||||||
// GetPlayerPosition dereferences the pawn, so APawn must be COMPLETE — Casts.h only forward-declares
|
// GetPlayerPosition dereferences the pawn, so APawn must be COMPLETE — Casts.h only forward-declares
|
||||||
// it. APlayerController was complete transitively only; include it explicitly.
|
// it. APlayerController is complete only transitively; include it explicitly.
|
||||||
#include "GameFramework/Pawn.h"
|
#include "GameFramework/Pawn.h"
|
||||||
#include "GameFramework/PlayerController.h"
|
#include "GameFramework/PlayerController.h"
|
||||||
#include "GameFramework/PawnMovementComponent.h"
|
#include "GameFramework/PawnMovementComponent.h"
|
||||||
@@ -185,11 +184,11 @@ AVoxelWorld::AVoxelWorld()
|
|||||||
//=============================================================================
|
//=============================================================================
|
||||||
// T1.f — build the RMC geometry buffers OFF the game thread.
|
// T1.f — build the RMC geometry buffers OFF the game thread.
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
// FRealtimeMeshStreamSet is plain CPU data; the per-vertex/per-triangle builder loop used to run
|
// FRealtimeMeshStreamSet is plain CPU data; on the game thread (in ApplyMeshToTile) the
|
||||||
// in ApplyMeshToTile ON THE GAME THREAD, where it was the dominant streaming cost (game thread
|
// per-vertex/per-triangle builder loop is the dominant streaming cost (game thread >6 ms while
|
||||||
// >6 ms while moving, GPU/draw idle). It touches ONLY the POD MeshData arrays — no UObject, no
|
// moving, GPU/draw idle). It touches ONLY the POD MeshData arrays — no UObject, no generator — so
|
||||||
// generator — so it's safe on the gen worker. The game thread then just uploads the finished
|
// it's safe on the gen worker. The game thread then just uploads the finished streams
|
||||||
// streams (CreateSectionGroup). Byte-identical geometry; the only thing that moved is WHERE it runs.
|
// (CreateSectionGroup). Byte-identical geometry; only WHERE it runs differs.
|
||||||
static void BuildTileStreamSet(RealtimeMesh::FRealtimeMeshStreamSet& Streams, const FVoxelMeshData& MeshData)
|
static void BuildTileStreamSet(RealtimeMesh::FRealtimeMeshStreamSet& Streams, const FVoxelMeshData& MeshData)
|
||||||
{
|
{
|
||||||
RealtimeMesh::TRealtimeMeshBuilderLocal<uint32, FPackedNormal, FVector2DHalf, 1> Builder(Streams);
|
RealtimeMesh::TRealtimeMeshBuilderLocal<uint32, FPackedNormal, FVector2DHalf, 1> Builder(Streams);
|
||||||
@@ -8044,7 +8043,7 @@ void AVoxelWorld::UnloadTile(const FVoxelTileKey& Tile)
|
|||||||
CollisionNotRequiredTiles.Remove(Tile);
|
CollisionNotRequiredTiles.Remove(Tile);
|
||||||
CollisionSolidTiles.Remove(Tile);
|
CollisionSolidTiles.Remove(Tile);
|
||||||
PendingCollisionCooks.Remove(Tile);
|
PendingCollisionCooks.Remove(Tile);
|
||||||
// Water + decorations are no longer tile-bound (water is one player-following ocean plane via
|
// Water + decorations are not tile-bound (water is one player-following ocean plane via
|
||||||
// UpdateWater; decorations stream by distance via UpdateDecorations) — nothing to clear per tile.
|
// UpdateWater; decorations stream by distance via UpdateDecorations) — nothing to clear per tile.
|
||||||
if (URealtimeMeshComponent** Comp = TileComponents.Find(Tile))
|
if (URealtimeMeshComponent** Comp = TileComponents.Find(Tile))
|
||||||
{
|
{
|
||||||
@@ -8220,7 +8219,7 @@ bool AVoxelWorld::ApplyMeshToTile(FChunkResult& Result)
|
|||||||
}
|
}
|
||||||
BindRealtimeMeshCollisionEvent(RTMesh);
|
BindRealtimeMeshCollisionEvent(RTMesh);
|
||||||
// Shadow casting: far (level >= 2) tiles never cast; the sky-cap SECTION never casts either
|
// Shadow casting: far (level >= 2) tiles never cast; the sky-cap SECTION never casts either
|
||||||
// — otherwise the high rock ceiling shadows the entire terrain below it. F17: shadow is now
|
// — otherwise the high rock ceiling shadows the entire terrain below it. F17: shadow is
|
||||||
// PER SECTION, so a mixed tile keeps its ground shadow while its cap stays shadowless.
|
// PER SECTION, so a mixed tile keeps its ground shadow while its cap stays shadowless.
|
||||||
const bool bCastShadow = (Tile.Level <= 1);
|
const bool bCastShadow = (Tile.Level <= 1);
|
||||||
MeshComp->SetCastShadow(bCastShadow);
|
MeshComp->SetCastShadow(bCastShadow);
|
||||||
@@ -8340,7 +8339,7 @@ bool AVoxelWorld::ApplyMeshToTile(FChunkResult& Result)
|
|||||||
TotalSeconds);
|
TotalSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Water is no longer spawned per tile — it's a single player-following ocean plane (UpdateWater,
|
// Water is not spawned per tile — it's a single player-following ocean plane (UpdateWater,
|
||||||
// driven from Tick), so it renders at every LOD and to the horizon with no per-tile gaps.
|
// driven from Tick), so it renders at every LOD and to the horizon with no per-tile gaps.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -9022,9 +9021,9 @@ void AVoxelWorld::UpdateOrbLightMPC()
|
|||||||
const FVoxelActiveOrb& O = Orbs[i];
|
const FVoxelActiveOrb& O = Orbs[i];
|
||||||
V = FLinearColor((float)O.WorldPos.X, (float)O.WorldPos.Y, (float)O.WorldPos.Z, O.FalloffWorld);
|
V = FLinearColor((float)O.WorldPos.X, (float)O.WorldPos.Y, (float)O.WorldPos.Z, O.FalloffWorld);
|
||||||
}
|
}
|
||||||
// ALWAYS write, even when unchanged. A skip-if-identical cache was tried here and BROKE the
|
// ALWAYS write, even when unchanged. A skip-if-identical cache BREAKS the lighting: the
|
||||||
// lighting: the MPC's world INSTANCE can be reset/recreated behind our back (PIE init order,
|
// MPC's world INSTANCE can be reset/recreated behind our back (PIE init order, asset
|
||||||
// asset recompile), and a cached skip then leaves it holding defaults forever. The per-frame
|
// recompile), and a cached skip then leaves it holding defaults forever. The per-frame
|
||||||
// rewrite is what makes the collection self-healing — and 4 vector writes cost nothing.
|
// rewrite is what makes the collection self-healing — and 4 vector writes cost nothing.
|
||||||
UKismetMaterialLibrary::SetVectorParameterValue(this, OrbLightMPC, OrbNames[i], V);
|
UKismetMaterialLibrary::SetVectorParameterValue(this, OrbLightMPC, OrbNames[i], V);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,9 @@
|
|||||||
#include "VoxelStrateTypes.h" // FStrateDecoration / FStrateAmbientActor
|
#include "VoxelStrateTypes.h" // FStrateDecoration / FStrateAmbientActor
|
||||||
#include "VoxelBiomeDefinition.generated.h"
|
#include "VoxelBiomeDefinition.generated.h"
|
||||||
|
|
||||||
// IWYU : utilisé en pointeur seulement ⇒ déclaration avant. Fournie gratuitement par le PCH
|
// IWYU : utilisé en pointeur seulement ⇒ déclaration avant. Le PCH partagé ne la fournit pas :
|
||||||
// partagé jusqu'ici ; `FPSemantics = Precise` nous en sort. / Pointer-only use, so a forward
|
// `FPSemantics = Precise` nous en sort. / Pointer-only use, so a forward declaration is enough.
|
||||||
// declaration is enough. The shared PCH used to provide this for free.
|
// The shared PCH does not provide it (FPSemantics = Precise opts this module out of it).
|
||||||
class UMaterialInterface;
|
class UMaterialInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ namespace VoxelHash
|
|||||||
* collident donneraient un bruit identique PARTOUT. Salé par site, il faudrait qu'ils
|
* collident donneraient un bruit identique PARTOUT. Salé par site, il faudrait qu'ils
|
||||||
* collident sur les ~50 sites à la fois — c'est-à-dire jamais.
|
* collident sur les ~50 sites à la fois — c'est-à-dire jamais.
|
||||||
*
|
*
|
||||||
* The multiplier no longer decorrelates by amplifying — it IDENTIFIES the site, and the hash
|
* The multiplier does not decorrelate by amplifying — it IDENTIFIES the site, and the hash
|
||||||
* decorrelates. Output is already in final units and bounded, so the ULP is 10% of a voxel step.
|
* decorrelates. Output is already in final units and bounded, so the ULP is 10% of a voxel step.
|
||||||
*
|
*
|
||||||
* @param SiteKey la constante littérale d'origine (`7.3f`, `97.7f`, …). Gardée VISIBLE au site
|
* @param SiteKey la constante littérale d'origine (`7.3f`, `97.7f`, …). Gardée VISIBLE au site
|
||||||
@@ -439,10 +439,10 @@ namespace VoxelMazeTopology
|
|||||||
// le motif qui a produit `AUDIT §C1` (un correctif appliqué à une copie sur deux). Il monte donc au
|
// le motif qui a produit `AUDIT §C1` (un correctif appliqué à une copie sur deux). Il monte donc au
|
||||||
// point le plus bas qui voit déjà le hash, et le générateur comme la pile d'opérateurs l'appellent.
|
// point le plus bas qui voit déjà le hash, et le générateur comme la pile d'opérateurs l'appellent.
|
||||||
//
|
//
|
||||||
// Ce corps était `static float CellularNoise3D(const FVector&)` dans VoxelGenerator.cpp, invisible
|
// Il vit ici pour que le générateur et la pile d'opérateurs partagent la même implémentation.
|
||||||
// à la pile d'opérateurs. Il vit ici pour que les deux chemins partagent la même implémentation.
|
|
||||||
// The hot overload uses FVector3f: noise coordinates are floats, so no double round-trip is paid
|
// The hot overload uses FVector3f: noise coordinates are floats, so no double round-trip is paid
|
||||||
// before the hash/lattice arithmetic. Output compatibility with the old field is not a contract.
|
// before the hash/lattice arithmetic. Output compatibility with a double-precision evaluation is
|
||||||
|
// not a contract.
|
||||||
//
|
//
|
||||||
// Algorithme : distance au point-feature le plus proche dans une grille hachée.
|
// Algorithme : distance au point-feature le plus proche dans une grille hachée.
|
||||||
// 1. cellule entière du point 2. voisinage 3×3×3 3. rendre (F2 − F1), normalisé ~[-1, 1]
|
// 1. cellule entière du point 2. voisinage 3×3×3 3. rendre (F2 − F1), normalisé ~[-1, 1]
|
||||||
@@ -520,7 +520,7 @@ namespace VoxelNoise
|
|||||||
// PER-CHUNK SDF CACHE
|
// PER-CHUNK SDF CACHE
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
// The room list and tunnel connections are IDENTICAL for all voxels in a chunk.
|
// The room list and tunnel connections are IDENTICAL for all voxels in a chunk.
|
||||||
// Without caching, EvaluateSDF rebuilds these 32,768 times per 32³ chunk:
|
// Without caching, a per-voxel evaluation would rebuild these 32,768 times per 32³ chunk:
|
||||||
// - Hash every grid cell in the search area
|
// - Hash every grid cell in the search area
|
||||||
// - Determine room existence, position, size
|
// - Determine room existence, position, size
|
||||||
// - Run O(N²) nearest-neighbor backbone
|
// - Run O(N²) nearest-neighbor backbone
|
||||||
@@ -604,9 +604,9 @@ struct FCachedRoom
|
|||||||
float RoomOpWeight = 1.0f;
|
float RoomOpWeight = 1.0f;
|
||||||
|
|
||||||
// PRE-BAKED SHAPE (BuildChunkCache). The shape roll, variety thresholds and the capsule's
|
// PRE-BAKED SHAPE (BuildChunkCache). The shape roll, variety thresholds and the capsule's
|
||||||
// Cos/Sin direction used to be re-derived PER VOXEL per room inside EvaluateSDFCached — hash
|
// Cos/Sin direction are constants of the room: re-deriving them PER VOXEL inside
|
||||||
// mixes + trig in the hottest loop of the plugin for values that are constants of the room.
|
// EvaluateSDFCached would put hash mixes + trig in the hottest loop of the plugin.
|
||||||
// Bit-identical to the old per-voxel roll (same hashes, same math, done once per chunk).
|
// Bit-identical to a per-voxel roll (same hashes, same math, done once per chunk).
|
||||||
// 0 = ellipsoid → ShapeA = radii (x=y=RadiusXY, z=RadiusZ)
|
// 0 = ellipsoid → ShapeA = radii (x=y=RadiusXY, z=RadiusZ)
|
||||||
// 1 = rounded box → ShapeA = half-extents, ShapeR = corner rounding
|
// 1 = rounded box → ShapeA = half-extents, ShapeR = corner rounding
|
||||||
// 2 = capsule → ShapeA/ShapeB = world endpoints, ShapeR = tube radius
|
// 2 = capsule → ShapeA/ShapeB = world endpoints, ShapeR = tube radius
|
||||||
@@ -929,8 +929,7 @@ struct FChunkSDFCache
|
|||||||
VoxelDensityProfile::FCacheMemoryBreakdown GetAllocatedSizeBreakdown() const;
|
VoxelDensityProfile::FCacheMemoryBreakdown GetAllocatedSizeBreakdown() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** World-space tunnel shape query. Legacy support-floor fields remain source-compatible but are
|
/** World-space tunnel shape query. */
|
||||||
* no longer produced or consumed by the graph-tunnel generation path. */
|
|
||||||
struct FTunnelCoreWorldEvaluation
|
struct FTunnelCoreWorldEvaluation
|
||||||
{
|
{
|
||||||
float SDF = FLT_MAX;
|
float SDF = FLT_MAX;
|
||||||
@@ -949,7 +948,6 @@ struct FTunnelCoreWorldEvaluation
|
|||||||
// CAVE MORPHOLOGY EVALUATOR
|
// CAVE MORPHOLOGY EVALUATOR
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
// Two-phase evaluation: BuildChunkCache (once per chunk) + EvaluateSDFCached (per voxel).
|
// Two-phase evaluation: BuildChunkCache (once per chunk) + EvaluateSDFCached (per voxel).
|
||||||
// The original EvaluateSDF is kept as a convenience wrapper for backward compatibility.
|
|
||||||
|
|
||||||
namespace VoxelCaveMorphology
|
namespace VoxelCaveMorphology
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// VoxelDensityOp.h
|
// VoxelDensityOp.h
|
||||||
// LE CONTRAT de la pile d'opérateurs de densité / THE density operator stack CONTRACT.
|
// LE CONTRAT de la pile d'opérateurs de densité / THE density operator stack CONTRACT.
|
||||||
// Phase 1 de Docs/archive/OPSTACK-PLAN.md. The contract is implemented by the opt-in stack path in
|
// Design : Docs/archive/OPSTACK-PLAN.md. The contract is implemented by the opt-in stack path in
|
||||||
// UVoxelGenerator::GetDensityAt; the legacy archetype switch remains for non-opt-in strates.
|
// UVoxelGenerator::GetDensityAt; the legacy archetype switch remains for non-opt-in strates.
|
||||||
//
|
//
|
||||||
// ─────────────────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────────────────
|
||||||
@@ -714,13 +714,12 @@ public:
|
|||||||
/**
|
/**
|
||||||
* DIAGNOSTIC UNIQUEMENT — le nom que les rapports de test impriment pour cet opérateur.
|
* DIAGNOSTIC UNIQUEMENT — le nom que les rapports de test impriment pour cet opérateur.
|
||||||
*
|
*
|
||||||
* ⚠️ POURQUOI CETTE MÉTHODE EXISTE, ET CE QU'ELLE A COÛTÉ DE NE PAS AVOIR. Le premier build de
|
* ⚠️ POURQUOI CETTE MÉTHODE EXISTE. Sans nom par opérateur, un rapport « 0 tuile prouvée sur
|
||||||
* l'`EffectOverBox` spatial est revenu **vert avec 0 tuile prouvée sur 40**, et la seule chose
|
* 40 » ne peut dire que « ou bien les tuiles traversent toutes une grotte, ou bien la source
|
||||||
* que le rapport pouvait dire était « ou bien les tuiles traversent toutes une grotte, ou bien
|
* n'atteint pas sa branche `Identity` » — deux causes, zéro nombre pour les départager, alors
|
||||||
* la source n'atteint pas sa branche `Identity` ». Deux causes, zéro nombre pour les
|
* que la vraie cause peut être un TROISIÈME opérateur (p. ex. un ver qui rend `CarveOnly`
|
||||||
* départager — exactement le piège que ce projet a déjà payé plusieurs fois. La vraie cause
|
* partout). Avec un nom par opérateur, `ClassifyBoxAttributed` répond « c'est celui-là » au
|
||||||
* était un TROISIÈME opérateur (le ver, qui rendait `CarveOnly` partout). Avec un nom par
|
* lieu de laisser deviner.
|
||||||
* opérateur, `ClassifyBoxAttributed` répond « c'est celui-là » au lieu de laisser deviner.
|
|
||||||
*
|
*
|
||||||
* N'entre dans AUCUNE clé de cache, dans aucun hash, dans aucune décision de génération : le
|
* N'entre dans AUCUNE clé de cache, dans aucun hash, dans aucune décision de génération : le
|
||||||
* changer ne peut pas changer le monde. Le défaut est volontairement laconique — un opérateur
|
* changer ne peut pas changer le monde. Le défaut est volontairement laconique — un opérateur
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// La PILE : un conteneur ordonné d'opérateurs, plus les fabriques d'opérateurs concrets.
|
// La PILE : un conteneur ordonné d'opérateurs, plus les fabriques d'opérateurs concrets.
|
||||||
// The STACK: an ordered container of operators, plus the concrete-operator factories.
|
// The STACK: an ordered container of operators, plus the concrete-operator factories.
|
||||||
//
|
//
|
||||||
// ⚠️ CECI ALIMENTE LE JEU, MAIS SEULEMENT SUR OPT-IN (depuis OPSTACK-PLAN §4, Phase 1, point 3).
|
// ⚠️ CECI ALIMENTE LE JEU, MAIS SEULEMENT SUR OPT-IN.
|
||||||
// `UVoxelGenerator::GetDensityAt` construit la pile par chunk et l'évalue à la place du `switch`
|
// `UVoxelGenerator::GetDensityAt` construit la pile par chunk et l'évalue à la place du `switch`
|
||||||
// UNIQUEMENT quand `UVoxelStrateManager::UsesOperatorStackForChunk` rend true — c.-à-d. quand la
|
// UNIQUEMENT quand `UVoxelStrateManager::UsesOperatorStackForChunk` rend true — c.-à-d. quand la
|
||||||
// strate a coché `bUseOperatorStack` ET que son archétype figure dans la liste des portés :
|
// strate a coché `bUseOperatorStack` ET que son archétype figure dans la liste des portés :
|
||||||
@@ -18,18 +18,17 @@
|
|||||||
// archetypes; gaps and SurfaceWorld retain their exact hand-written proofs.
|
// archetypes; gaps and SurfaceWorld retain their exact hand-written proofs.
|
||||||
//
|
//
|
||||||
// ⛔ NE JAMAIS faire tourner les deux chemins dans le même monde.
|
// ⛔ NE JAMAIS faire tourner les deux chemins dans le même monde.
|
||||||
// ⚠️ EN REVANCHE, LES COMPARER EST DEVENU LÉGITIME — cette ligne disait l'inverse et elle est
|
// ⚠️ EN REVANCHE, LES COMPARER EST LÉGITIME. `AUDIT §C10` (le résidu ~1 ULP) est clos par
|
||||||
// périmée. `AUDIT §C10` (le résidu ~1 ULP) est CLOS depuis `FPSemantics = Precise` : les cinq tests
|
// `FPSemantics = Precise` : les cinq tests d'équivalence comparent bit à bit. Ce ne sont pas des
|
||||||
// d'équivalence comparent bit à bit et sont verts. Ils ne sont plus des contrôles de FIDÉLITÉ (la
|
// contrôles de FIDÉLITÉ (la barre `§2.6.1` n'exige aucune ressemblance avec l'ancien monde) mais
|
||||||
// barre `§2.6.1` n'exige aucune ressemblance avec l'ancien monde) mais des oracles de
|
// des oracles de CORRECTION DE PORTAGE — une faute de transcription reste un vrai bug, et l'ancienne fonction est
|
||||||
// CORRECTION DE PORTAGE — une faute de transcription reste un vrai bug, et l'ancienne fonction est
|
|
||||||
// le moyen le moins cher de l'attraper.
|
// le moyen le moins cher de l'attraper.
|
||||||
//
|
//
|
||||||
// POURQUOI CETTE FORME / WHY THIS SHAPE
|
// POURQUOI CETTE FORME / WHY THIS SHAPE
|
||||||
// La question à laquelle la Phase 1 doit répondre n'est pas « est-ce que ça marche ? » mais
|
// La question à laquelle cette forme répond n'est pas « est-ce que ça marche ? » mais
|
||||||
// **« est-ce que la séparation source / modifier tombe naturellement du code existant ? »**
|
// **« est-ce que la séparation source / modifier tombe naturellement du code existant ? »**
|
||||||
// (OPSTACK-PLAN §4, le déclencheur d'arrêt). En portant Maze hors du chemin chaud et en le
|
// (OPSTACK-PLAN §4). Chaque archétype porté est comparé bit à bit à l'original : la réponse est
|
||||||
// comparant à l'original, cette question reçoit une réponse MESURÉE plutôt qu'une opinion.
|
// MESURÉE plutôt qu'une opinion.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@@ -229,11 +228,11 @@ public:
|
|||||||
|
|
||||||
// The room-graph source publishes the authored floor ownership while evaluating the
|
// The room-graph source publishes the authored floor ownership while evaluating the
|
||||||
// shape, but it must not be reasserted here. This stack is followed by the common
|
// shape, but it must not be reasserted here. This stack is followed by the common
|
||||||
// disturbance/passage writers; composing the floor at this point made every authored
|
// disturbance/passage writers; composing the floor at this point would make every authored
|
||||||
// floor sample look like a post-hoc clamp (and counted 100% of them). The generator now
|
// floor sample look like a post-hoc clamp (and counted 100% of them). The generator
|
||||||
// consumes the same immutable core result after those writers and composes the floor once,
|
// consumes the same immutable core result after those writers and composes the floor once,
|
||||||
// at the final MC boundary. The metadata remains on the sample for that hand-off and for
|
// at the final MC boundary. The metadata remains on the sample for that hand-off and for
|
||||||
// diagnostics, but it is no longer allowed to modify density inside the operator stack.
|
// diagnostics, but it is not allowed to modify density inside the operator stack.
|
||||||
return S;
|
return S;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ FORCEINLINE void VF_ApplyBoundarySeal(float& Density, float WorldZ,
|
|||||||
// CARVE DE PASSAGE / PASSAGE CARVING
|
// CARVE DE PASSAGE / PASSAGE CARVING
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
// Creuse un passage inter-strates. Évalué APRÈS le seal pour que les passages puissent percer à
|
// Creuse un passage inter-strates. Évalué APRÈS le seal pour que les passages puissent percer à
|
||||||
// travers le bouchon solide. Le rayon de blend hard-codé à 4.0f correspond à l'ancienne valeur —
|
// travers le bouchon solide. Le rayon de blend est hard-codé à 4.0f — à exposer via
|
||||||
// à exposer via UVoxelSettings si on veut pouvoir le tweaker.
|
// UVoxelSettings si on veut pouvoir le tweaker.
|
||||||
FORCEINLINE void VF_ApplyPassageCarving(float& Density, float ModSDF,
|
FORCEINLINE void VF_ApplyPassageCarving(float& Density, float ModSDF,
|
||||||
float BaseDensity, float SealThickness)
|
float BaseDensity, float SealThickness)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -177,9 +177,9 @@ private:
|
|||||||
void LaunchPendingFills(); // flush PendingFills onto the dedicated fill thread
|
void LaunchPendingFills(); // flush PendingFills onto the dedicated fill thread
|
||||||
void DrainResults(); // apply finished fills into the toroidal arrays (+ GPU dirty)
|
void DrainResults(); // apply finished fills into the toroidal arrays (+ GPU dirty)
|
||||||
|
|
||||||
// DEDICATED FILL THREAD. The volume fill used to run on the shared UE::Tasks pool (BackgroundLow),
|
// DEDICATED FILL THREAD. On the shared UE::Tasks pool (BackgroundLow) the volume fill STARVES
|
||||||
// where it STARVED behind mesh-gen (10 s to resolve shadows at a fresh spot). It now runs on its own
|
// behind mesh-gen (10 s to resolve shadows at a fresh spot). It runs on its own thread (off the
|
||||||
// thread (off the pool) so it's fast AND never steals a core from mesh-gen. Game thread enqueues
|
// pool) so it's fast AND never steals a core from mesh-gen. Game thread enqueues
|
||||||
// FPendingFill (Spsc), the thread re-evaluates GetDensityAt and pushes FFillResult into Results
|
// FPendingFill (Spsc), the thread re-evaluates GetDensityAt and pushes FFillResult into Results
|
||||||
// (existing Mpsc, drained on the game thread by DrainResults). Capture still short-circuits most of
|
// (existing Mpsc, drained on the game thread by DrainResults). Capture still short-circuits most of
|
||||||
// this (cache hits blit on the game thread, no fill); the thread is the backstop for misses.
|
// this (cache hits blit on the game thread, no fill); the thread is the backstop for misses.
|
||||||
|
|||||||
@@ -205,9 +205,8 @@ public:
|
|||||||
* Utilisée en interne par GetDensityAt quand la strate est de ce type.
|
* Utilisée en interne par GetDensityAt quand la strate est de ce type.
|
||||||
* Exposée pour permettre des tests isolés avec des params custom.
|
* Exposée pour permettre des tests isolés avec des params custom.
|
||||||
*
|
*
|
||||||
* ⚠️ `ParamsFingerprint` ET `LayoutVersion` SONT OBLIGATOIRES, ET C'EST LE CORRECTIF
|
* ⚠️ `ParamsFingerprint` ET `LayoutVersion` SONT OBLIGATOIRES (`AUDIT §C2`). Le cache SDF
|
||||||
* D'`AUDIT §C2` (2026-07-28). Le cache SDF interne est clé sur (boîte XY, strate, seed) et
|
* interne est clé sur (boîte XY, strate, seed) et PAS sur les params. Or `GetGenerationParams` BLENDE les params à l'intérieur d'une même
|
||||||
* PAS sur les params. Or `GetGenerationParams` BLENDE les params à l'intérieur d'une même
|
|
||||||
* strate — `Alpha` dépend du chunk Z en mode `Gradient` (le DÉFAUT, avec
|
* strate — `Alpha` dépend du chunk Z en mode `Gradient` (le DÉFAUT, avec
|
||||||
* `TransitionBlendChunks = 2`) et du chunk XY en plus en mode `Interleaved`. Deux chunks de la
|
* `TransitionBlendChunks = 2`) et du chunk XY en plus en mode `Interleaved`. Deux chunks de la
|
||||||
* même strate, même seed, donc même clé, mais des params DIFFÉRENTS : le worker évalue le
|
* même strate, même seed, donc même clé, mais des params DIFFÉRENTS : le worker évalue le
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ enum class ECaveGeneratorType : uint8
|
|||||||
* is defined on the UPPER strate's definition and controls the boundary between
|
* is defined on the UPPER strate's definition and controls the boundary between
|
||||||
* itself and the strate below it.
|
* itself and the strate below it.
|
||||||
*
|
*
|
||||||
* Gradient: Smooth linear interpolation of generation params across BlendChunks.
|
* Gradient: Smooth linear interpolation of generation params across TransitionBlendChunks.
|
||||||
* This is the classic approach — no seam visible, but also no dramatic
|
* This is the classic approach — no seam visible, but also no dramatic
|
||||||
* geological feature at the boundary. Best for similar-looking strates.
|
* geological feature at the boundary. Best for similar-looking strates.
|
||||||
*
|
*
|
||||||
@@ -228,7 +228,7 @@ UENUM(BlueprintType)
|
|||||||
enum class EVoxelStrateTransition : uint8
|
enum class EVoxelStrateTransition : uint8
|
||||||
{
|
{
|
||||||
// Smooth linear interpolation of all generation params across the blend zone.
|
// Smooth linear interpolation of all generation params across the blend zone.
|
||||||
// No visible boundary — params change gradually over BlendChunks distance.
|
// No visible boundary — params change gradually over TransitionBlendChunks distance.
|
||||||
Gradient UMETA(DisplayName = "Gradient (smooth blend)"),
|
Gradient UMETA(DisplayName = "Gradient (smooth blend)"),
|
||||||
|
|
||||||
// No blending — params switch instantly at the boundary.
|
// No blending — params switch instantly at the boundary.
|
||||||
@@ -2256,7 +2256,7 @@ enum class ELandmarkAnchor : uint8
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* FStrateLandmark — a RARE, deliberately-placed object: mini-suns, ruins, shrines, monuments (§8.5, F7).
|
* FStrateLandmark — a RARE, deliberately-placed object: mini-suns, ruins, shrines, monuments (§8.5, F7).
|
||||||
* The one placement primitive for "notable things you navigate by" (set-pieces folded in here 2026-07-06).
|
* The one placement primitive for "notable things you navigate by" (set-pieces included).
|
||||||
*
|
*
|
||||||
* This is the right primitive for things like the underground "mini-suns" (in-lore light sources): one
|
* This is the right primitive for things like the underground "mini-suns" (in-lore light sources): one
|
||||||
* object per ~`SpacingChunks` lattice cell, so the work scales with how MANY landmarks are in range
|
* object per ~`SpacingChunks` lattice cell, so the work scales with how MANY landmarks are in range
|
||||||
|
|||||||
@@ -203,11 +203,10 @@ namespace VoxelMath
|
|||||||
// Pourquoi 32 ? Puissance de 2 → astuces bit à bit + bon alignement GPU.
|
// Pourquoi 32 ? Puissance de 2 → astuces bit à bit + bon alignement GPU.
|
||||||
// VOXEL_SIZE = 25 cm/voxel (Unreal travaille en centimètres). 1 chunk = 8 m.
|
// VOXEL_SIZE = 25 cm/voxel (Unreal travaille en centimètres). 1 chunk = 8 m.
|
||||||
//
|
//
|
||||||
// NOTE: 64³ a été essayé ("B", 2026-06-16) pour couper les draw calls (8× moins de
|
// NOTE: 64³ coupe les draw calls (8× moins de chunks) mais rend le streaming trop
|
||||||
// chunks) mais le streaming devenait trop saccadé (briques 8× plus lourdes → applies
|
// saccadé (briques 8× plus lourdes → applies + spawns de contenu en gros à-coups sur
|
||||||
// + spawns de contenu en gros à-coups sur le game thread). Reverté à 32³ : le fps se
|
// le game thread). Le fps se règle côté RENDU (ombres off sur LOD lointain, voir
|
||||||
// règle côté RENDU (ombres off sur LOD lointain, voir ApplyMeshToChunk), pas via la
|
// ApplyMeshToChunk), pas via la taille de chunk. La taille de chunk reste le levier streaming-vs-draws si besoin.
|
||||||
// taille de chunk. La taille de chunk reste le levier streaming-vs-draws si besoin.
|
|
||||||
|
|
||||||
constexpr int32 CHUNK_SIZE = 32;
|
constexpr int32 CHUNK_SIZE = 32;
|
||||||
|
|
||||||
|
|||||||
@@ -1246,8 +1246,8 @@ public:
|
|||||||
// Desired-set membership STAMPÉE : clé → numéro du dernier crossing où la tuile était désirée.
|
// Desired-set membership STAMPÉE : clé → numéro du dernier crossing où la tuile était désirée.
|
||||||
// BuildDesiredTiles upserte le stamp courant puis balaie la map UNE fois : les entrées à stamp
|
// BuildDesiredTiles upserte le stamp courant puis balaie la map UNE fois : les entrées à stamp
|
||||||
// périmé sont les "leavers" (retirées + renvoyées). Le cull ne considère que ces leavers + la
|
// périmé sont les "leavers" (retirées + renvoyées). Le cull ne considère que ces leavers + la
|
||||||
// TransitionHold — fini le scan O(toutes-les-tuiles-chargées) à chaque crossing (le spike
|
// TransitionHold — pas de scan O(toutes-les-tuiles-chargées) à chaque crossing (un tel scan
|
||||||
// CullTiles ~1.6 ms/crossing de la trace 2026-07-05).
|
// coûtait ~1.6 ms/crossing dans CullTiles).
|
||||||
TMap<FVoxelTileKey, uint32> DesiredStamped;
|
TMap<FVoxelTileKey, uint32> DesiredStamped;
|
||||||
uint32 DesiredStamp = 0;
|
uint32 DesiredStamp = 0;
|
||||||
bool IsDesired(const FVoxelTileKey& T) const
|
bool IsDesired(const FVoxelTileKey& T) const
|
||||||
|
|||||||
@@ -739,7 +739,7 @@ bool ParseArguments(const FString& Params, FExploreArguments& OutArguments, FStr
|
|||||||
OutError = TEXT("rendermaxdistance must be finite and in (0,2048] voxels.");
|
OutError = TEXT("rendermaxdistance must be finite and in (0,2048] voxels.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Render no longer budgets density samples: pixels are rasterised from the one canonical
|
// Render does not budget density samples: pixels are rasterised from the one canonical
|
||||||
// mesh. Keep the legacy renderstep/maxdistance arguments for compatible invocations and use
|
// mesh. Keep the legacy renderstep/maxdistance arguments for compatible invocations and use
|
||||||
// only maxdistance as the raster depth clip.
|
// only maxdistance as the raster depth clip.
|
||||||
if (OutArguments.ExportSize < CHUNK_SIZE
|
if (OutArguments.ExportSize < CHUNK_SIZE
|
||||||
|
|||||||
Reference in new issue
Block a user