diff --git a/Source/VoxelForge/Private/VoxelAtmosphereManager.cpp b/Source/VoxelForge/Private/VoxelAtmosphereManager.cpp index 28d7d4b..7e95bbd 100644 --- a/Source/VoxelForge/Private/VoxelAtmosphereManager.cpp +++ b/Source/VoxelForge/Private/VoxelAtmosphereManager.cpp @@ -43,11 +43,11 @@ void UVoxelAtmosphereManager::UpdateForPlayer(const FVector& PlayerWorldPos) if (!O) return; // 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 - // qui est exactement pourquoi ce fichier supposait "acteur a l origine" avant le 2026-08-17. + // coordonnees LOCALES. Le placement d acteurs, lui, veut du MONDE. Les deux cohabitent ici : ne + // pas supposer "acteur a l origine". // 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 - // assume "actor at origin / identity". + // actor placement wants WORLD. Both live in this function - do not assume "actor at origin / + // identity". const FTransform Xf = O->GetActorTransform(); const FVector LocalPos = Xf.InverseTransformPosition(PlayerWorldPos); diff --git a/Source/VoxelForge/Private/VoxelCaveMorphology.cpp b/Source/VoxelForge/Private/VoxelCaveMorphology.cpp index 04f4f63..302adc0 100644 --- a/Source/VoxelForge/Private/VoxelCaveMorphology.cpp +++ b/Source/VoxelForge/Private/VoxelCaveMorphology.cpp @@ -51,7 +51,7 @@ namespace // 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 // 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( 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 - // 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 MaxReferences = 4 * 1024 * 1024; TArray ItemMinX; @@ -3626,11 +3626,11 @@ namespace // 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 // 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 - // arithmetic and iteration count so the endpoint remains identical to the old scan. + // the exact bisection in that bucket. The latter is kept byte-for-byte in its arithmetic + // 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), - // fall back to the original one-voxel walk below; malformed/unsupported inputs therefore - // retain the old behavior. + // fall back to that one-voxel walk; malformed/unsupported inputs therefore keep its + // result. auto BisectBracket = [&](float BracketLow, float BracketHigh, 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 // cluster stays internally chained even when it can't bridge to the origin side.) // - // bTunnelsFlowTowardOrigin = false (legacy): plain nearest-neighbor pairing. NOTE: - // despite what this comment used to claim, an NN-graph is a FOREST of small clusters, - // not a connected tree — isolated cave pockets are expected in this mode. + // bTunnelsFlowTowardOrigin = false (legacy): plain nearest-neighbor pairing. NOTE: an + // NN-graph is a FOREST of small clusters, not a connected tree — isolated cave pockets + // are expected in this mode. // // Selection metric (not the reach filter) penalizes vertical separation via // TunnelHorizontalBias, so the GUARANTEED links also prefer walkable slopes — - // previously only the random TunnelDensity extras were biased, which is why - // backbone tunnels could come out absurdly steep. + // biasing only the random TunnelDensity extras lets backbone tunnels come out + // absurdly steep. TArray> NearestNeighbor; NearestNeighbor.SetNumUninitialized(NumRooms); @@ -8266,7 +8266,7 @@ void VoxelCaveMorphology::BuildChunkCache( MaxExtent += FloorReliefEnvelope + BlendEnvelope * 3.0f; // --- 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. { 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 // 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 - // point no longer performs a second spatial-index traversal. + // point does not perform a second spatial-index traversal. auto EvaluateTunnel = [&](int32 TunnelIdx) { const FCachedTunnel& Tunnel = TunnelData[TunnelIdx]; diff --git a/Source/VoxelForge/Private/VoxelClipmapDesiredTiles.h b/Source/VoxelForge/Private/VoxelClipmapDesiredTiles.h index 9834bac..afb71e7 100644 --- a/Source/VoxelForge/Private/VoxelClipmapDesiredTiles.h +++ b/Source/VoxelForge/Private/VoxelClipmapDesiredTiles.h @@ -9,7 +9,7 @@ namespace VoxelClipmapDesiredTiles int32 ClipRadius = 3; int32 MaxClipLevel = 4; 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. bool bFarSheetRing = true; int32 FarSheetSpanLevels = 2; diff --git a/Source/VoxelForge/Private/VoxelDensityOpStack.cpp b/Source/VoxelForge/Private/VoxelDensityOpStack.cpp index d4bc33b..dffe8ae 100644 --- a/Source/VoxelForge/Private/VoxelDensityOpStack.cpp +++ b/Source/VoxelForge/Private/VoxelDensityOpStack.cpp @@ -1,6 +1,6 @@ // VoxelDensityOpStack.cpp -// Les opérateurs concrets de la Phase 1 : la décomposition de Maze + le post-traitement structurel. -// The concrete Phase 1 operators: the Maze decomposition + the structural post-process. +// Les opérateurs concrets : les décompositions d'archétypes + le post-traitement structurel. +// 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. // @@ -92,23 +92,20 @@ namespace * 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. * - * ⚠️⚠️ **CORRIGÉ DE 2.0 À 1.5 LE 2026-07-28, ET CETTE CONSTANTE ÉTAIT LE TERME DOMINANT DE - * TOUTE LA FONCTION PENDANT TROIS BUILDS.** À lire avant d'y retoucher. + * ⚠️⚠️ **CETTE CONSTANTE EST LE TERME DOMINANT DE TOUTE LA FONCTION.** À lire avant d'y + * retoucher. * * 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 - * côtés de chaque axe d'une tuile de **10 voxels**, soit une boîte de requête de 50 voxels, - * **125× le volume de la tuile**. Trois passes de resserrement (le ver, les colonnes, - * l'échantillonneur, la disjonction des tunnels) ont été faites AUTOUR de ce terme sans que - * 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. + * (`CaveWarpStrength = 8`, `SCALE = 1.25`), une borne de 2.0 vaut **20 voxels** — appliquée + * des deux côtés de chaque axe d'une tuile de **10 voxels**, soit une boîte de requête de + * 50 voxels, **125× le volume de la tuile** : `BoxHalfDiag` est alors dominé par cette + * dilatation et non par la géométrie, et tout resserrement ailleurs ne gagne presque rien. * - * ⚠️ 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 - * `|Perlin3D| · SCALE ≤ CaveWarpStrength`, donc `|Perlin3D| ≤ 0.8`. Le code qui tourne en - * production depuis toujours parie déjà là-dessus. Prendre 2.0 était 2,5× plus conservateur - * que l'hypothèse dont dépend déjà la correction du cache. + * `|Perlin3D| · SCALE ≤ CaveWarpStrength`, donc `|Perlin3D| ≤ 0.8`. Le code de production + * parie déjà là-dessus. 2.0 serait 2,5× plus conservateur que l'hypothèse dont dépend déjà la + * correction du cache. * * 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 @@ -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 * 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: - * it inflated a 10-voxel tile into a 50-voxel query box (125x the volume), which is why the - * "order of magnitude tighter" tunnel test only won 25%. The rest of the plugin has always - * assumed |Perlin3D| <= 0.8 (BuildChunkCache's Expansion = CaveWarpStrength + 2). 1.5 is - * PROVED above from GradDot's two-distinct-axes form and the per-axis weighted bound of 0.5. + * This bound is the dominant term of the whole function: 2.0 would inflate a 10-voxel tile + * into a 50-voxel query box (125x the volume). The rest of the plugin already assumes + * |Perlin3D| <= 0.8 (BuildChunkCache's Expansion = CaveWarpStrength + 2). 1.5 is 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; @@ -356,7 +352,7 @@ namespace // ⚠️ 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 // — 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 // 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; } 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 // 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É, // 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 - // colonnes T1.a devient générique (l'étape suivante), un `true` ici ferait partager UNE - // valeur de densité sur TOUTE la pile verticale de chunks — un monde silencieusement faux, - // que `ValidateDeterminism` ne verrait pas parce qu'il échantillonne le long d'un bord X. - // 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. + // Le jour où un cache de colonnes générique lit ce drapeau, un `true` ici ferait partager + // UNE valeur de densité sur TOUTE la pile verticale de chunks — un monde silencieusement + // faux, que `ValidateDeterminism` ne verrait pas parce qu'il échantillonne le long d'un + // bord X. C'est exactement le piège que l'avertissement de `VoxelDensityOp.h` décrit. // - // ⚠️ FIXED: this said `true` and was WRONG. The contract is "**Eval** does not depend on Z", - // and Eval computes min(Z - floor, ceil - Z). §3.1 made the SURFACES XY-pure; the DENSITY - // never was and cannot be — it is a distance to a surface. Latent only because nothing reads - // the flag yet; a generic T1.a column cache would have shared one density down the whole - // vertical chunk stack. + // ⚠️ Must stay `false`. The contract is "**Eval** does not depend on Z", and Eval computes + // min(Z - floor, ceil - Z). §3.1 made the SURFACES XY-pure; the DENSITY never was and + // cannot be — it is a distance to a surface. A generic column cache reading `true` here + // would share one density down the whole vertical chunk stack. // // 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. @@ -956,8 +949,8 @@ namespace } private: - // The surface coordinates stay in float; the old FVector round-trip only preserved - // legacy rounding and is no longer part of the output contract. + // The surface coordinates stay in float; an FVector (double) round-trip is not part of + // the output contract. float SurfaceFloor(float WorldX, float WorldY) const { if (FloorRoughness <= 0.0f) { return FloorZ; } @@ -1307,29 +1300,29 @@ namespace 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, - * c'est-à-dire à chaque chunk. Résultat : une strate haute de 4 chunks recalculait ses - * colonnes **4 fois**, resamples du cliff compris. Le chemin d'origine ne fait pas ça — - * `GSurfColCache` est clé sur `(boîte XY, StrateKey, Seed, LayoutVersion)` **SANS ChunkZ**, - * délibérément, « shared down the whole vertical strate stack ». Cette pile reprend la - * même identité de strate/layout/seed, en ajoutant l'empreinte obligatoire des params pour - * protéger ses sorties propres ; son mémo est maintenant un LRU spatial de six boîtes. + * Ne PAS clé ce mémo sur `InstanceId` : il change à CHAQUE reconstruction de pile, + * c'est-à-dire à chaque chunk, et une strate haute de 4 chunks recalculerait ses colonnes + * **4 fois**, resamples du cliff compris. `GSurfColCache` est clé sur + * `(boîte XY, StrateKey, Seed, LayoutVersion)` **SANS ChunkZ**, délibérément, « shared down + * the whole vertical strate stack ». Cette pile reprend la même identité de + * strate/layout/seed, en ajoutant l'empreinte obligatoire des params pour protéger ses + * 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 * 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 * `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 * la justification sur laquelle `GSurfColCache` repose déjà. * - * The memo was keyed on InstanceId, which changes every chunk, so a 4-chunk strate recomputed - * every column 4x. GSurfColCache deliberately omits ChunkZ and shares down the whole vertical - * stack; this now shares the same strate/layout/seed identity and adds the required params - * fingerprint for its own outputs. The six-box LRU keeps independent XY regions alive. Safe + * Do not key the memo on InstanceId: it changes every chunk, so a 4-chunk strate would + * recompute every column 4x. GSurfColCache deliberately omits ChunkZ and shares down the whole + * vertical stack; this shares the same strate/layout/seed identity and adds the required + * 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. */ 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 * 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 - * n'est pas touché par cette Phase 1.) + * mais elle est réelle.) */ 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 // 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 - // encore portés**. Inventer l'infrastructure de frame pour son unique utilisateur actuel, c'est - // la concevoir contre un seul exemple — précisément ce que ce refactor a évité jusqu'ici en - // n'abstrayant qu'à la deuxième occurrence (cf. `IVoxelBiomeField`, né d'un besoin réel). - // ✅ RÉPONDU (2026-08-16) : TunnelNetwork EST porté, et le deuxième usage réel a dissous la - // question au lieu de la trancher. Voir `BuildTunnelNetworkStack` : `CaveWarp` n'enveloppe - // 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. + // usages de frames (îles, caves de TunnelNetwork, tunnels), et aucun n'en a besoin. Voir + // `BuildTunnelNetworkStack` : `CaveWarp` n'enveloppe 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. On n'abstrait qu'à la deuxième occurrence réelle (cf. + // `IVoxelBiomeField`, né d'un besoin réel). // - // The warp stays INSIDE the op against §7's FRAME suggestion. ✅ ANSWERED 2026-08-16: this said - // "revisit when TunnelNetwork brings the second real use" — TunnelNetwork is ported, and the - // second use dissolved the question rather than settling it. See BuildTunnelNetworkStack: + // The warp stays INSIDE the op against §7's FRAME suggestion. TunnelNetwork is ported, and the + // second real 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 // pure function of a scalar. ZERO frames out of three candidates. Do not reopen. class FIslandBlobSource final : public IVoxelDensityOp @@ -3111,8 +3099,8 @@ namespace * ⚠️ LA CLÉ INCLUT `BoundarySealThickness`, QUE L'ORIGINAL OMET. `SpreadZ` s'en sert * (`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 - * que `AUDIT §C2` et que la régression d'overhang du 2026-07-27 : une clé de cache - * incomplète ne se voit pas, elle produit du terrain plausible. Ajouter le champ ne coûte + * que `AUDIT §C2` : une clé de cache incomplète ne se voit pas, elle produit du terrain + * plausible. Ajouter le champ ne coûte * 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. @@ -3953,10 +3941,9 @@ namespace // 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 // 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 - // modificateurs de détail de l'étape B puissent les lire. Même durée de vie, même - // partage entre instances qu'avant : ce sont les mêmes `thread_local`, déménagés.) - // ⚠️ AJOUTÉ PAR RAPPORT À L'ORIGINAL — la leçon du 2026-07-27 (régression d'overhang). + // (Les champs de cache vivent dans `FState`, au-dessus, pour que les modificateurs de + // détail puissent les lire ; ce sont des `thread_local` partagés entre instances.) + // ⚠️ LA CLÉ INCLUT LES PARAMS, CONTRAIREMENT À L'ORIGINAL. // 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 // `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 // 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 // phase next to 4b would reorder those density updates, so a bit-identical fuse is not legal. class FFloorBiasMod final : public IVoxelDensityOp @@ -9381,7 +9368,7 @@ namespace VoxelDensityOps TUniquePtr BiomeField, 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é. TUniquePtr ColumnSource = MakeUnique(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** — // `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 - // 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é. + // les MÊMES trois ops avec une source différente. C'est exactement ce que `§2.5` prédisait. // // 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 @@ -9472,20 +9458,6 @@ namespace VoxelDensityOps // 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. // - // ⚠️ 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) //--------------------------------------------------------------------- @@ -9499,7 +9471,7 @@ namespace VoxelDensityOps // 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 // 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 // La source de salles est retenue par pointeur non possédant : les terrasses re-interrogent diff --git a/Source/VoxelForge/Private/VoxelForgeModule.cpp b/Source/VoxelForge/Private/VoxelForgeModule.cpp index e341c6c..9d54985 100644 --- a/Source/VoxelForge/Private/VoxelForgeModule.cpp +++ b/Source/VoxelForge/Private/VoxelForgeModule.cpp @@ -39,8 +39,8 @@ void FVoxelForgeModule::StartupModule() RequestedFma3, Fma3After); #endif - // These parsers used to be entered lazily by worker-side density/mesher calls. Resolve the - // command line once while the module is starting, before any generation task is submitted. + // Resolve the command line once while the module is starting, before any generation task is + // submitted, instead of lazily from worker-side density/mesher calls. VoxelGenLOD::InitializeConsoleSwitches(); UVoxelMarchingCubesMesher::InitializeConsoleSwitches(); UE_LOG(LogTemp, Log, TEXT("VoxelForge module started!")); diff --git a/Source/VoxelForge/Private/VoxelGenerator.cpp b/Source/VoxelForge/Private/VoxelGenerator.cpp index ae704ef..23d5fe8 100644 --- a/Source/VoxelForge/Private/VoxelGenerator.cpp +++ b/Source/VoxelForge/Private/VoxelGenerator.cpp @@ -16,7 +16,7 @@ #include "VoxelWormField.h" #include "VoxelDensityAblation.h" // fingerprinted development-only stage measurements #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 "VoxelStats.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 // 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 // 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 -// inchangés, comme l'ont fait FractalNoise3D et RidgedNoise3D lors de T2.a. Le chemin chaud garde -// maintenant les coordonnées en float : la compatibilité d'arrondi historique est abandonnée. +// inchangés, comme FractalNoise3D et RidgedNoise3D. Le chemin chaud garde les coordonnées en float. static float CellularNoise3D(const FVector3f& 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 -// non-zero density as a world-wide influence, which made an otherwise proven all-solid/all-air -// LOD0 box fall back to the full mesher. These predicates are deliberately one-sided: they only +// The disturbance pass is deterministic and spatially sparse. Treating a non-zero density as a +// world-wide influence would make an otherwise proven all-solid/all-air LOD0 box fall back to the +// 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 // invalid range, or an unknown parameter keeps the conservative Mixed result. 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; // when the other context misses, this bit must force the rebuild guard below to run. 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 // la strate n'a pas coché `bUseOperatorStack` ET que son archétype n'est pas porté. thread_local FVoxelOpStack CP_OpStack; @@ -3086,7 +3085,7 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co } 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 // par chunk (comme le refetch de params juste au-dessus), jamais par voxel. 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 int32 CachedStrate = INT32_MIN; 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 // 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 @@ -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 // 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 - // 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 CachedLayout = 0xFFFFFFFFu; 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. // 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. // 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 // 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). -// Une 1re tentative (2026-06-26) a été REVERTÉE : borne analytique GLOBALE du -// plafond pas assez conservative (cap bas ⇒ trous dans le toit). Ici on suit la -// prescription du revert : les colonnes sont ÉCHANTILLONNÉES sur le treillis +// complet (une trace : 83 925 GenerateMesh pour 13 227 maillages réels). +// ⚠️ Pas de borne analytique GLOBALE du plafond : pas assez conservative (cap +// bas ⇒ trous dans le toit). Les colonnes sont donc ÉCHANTILLONNÉES sur le treillis // exact du mesher (mêmes fonctions ⇒ mêmes floats ⇒ verdict exact, pas une // 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 @@ -6293,9 +6291,9 @@ static EVoxelTileClass VF_ClassifyBoxRefined(const FVoxelOpStack& Stack, && (StructuralManager->AnyLandingFloorAtLattice( Box, Context.LatticeOriginVoxels, Context.Step) // 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 - // when the legacy slab was removed; omitting the native-floor reach here lets an - // exact core-lattice AllAir claim skip a tile whose final density is solid. + // interpreted stack, and no support-floor query covers it; omitting the + // native-floor reach here lets an exact core-lattice AllAir claim skip a tile + // whose final density is solid. || StructuralManager->AnyPassageStructuralPostNearLattice( Box, Context.LatticeOriginVoxels, Context.Step)); const bool bHasStructuralCandidate = Whole == EVoxelTileClass::AllSolid @@ -6994,14 +6992,14 @@ EVoxelTileClass UVoxelGenerator::ClassifyTileUncached( // même endroit, que `GetDensityAt`. if (!StrateManager->UsesOperatorStackForChunk(CC)) { - // Attribution DIAGNOSTIQUE uniquement : l'ancien compteur mélangeait une - // strate cave entièrement désactivée avec une tuile de frontière qui avait - // rencontré un slot désactivé avant la garde « slot différent » ci-dessous. + // Attribution DIAGNOSTIQUE uniquement : distingue une strate cave entièrement + // désactivée d'une tuile de frontière qui a rencontré un slot désactivé avant + // 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 // la condition, ni le point de retour, ni le verdict. - // Diagnostic attribution only: the old counter mixed a wholly disabled cave - // slot with a boundary tile that met a disabled slot before the different-slot - // guard below. Resolve bounds only after the same predicate fails; classification + // Diagnostic attribution only: separates a wholly disabled cave slot from a + // boundary tile that met a disabled slot before the different-slot guard + // below. Resolve bounds only after the same predicate fails; classification // control flow and return value stay unchanged. int32 FailedTopCZ = 0, FailedBotCZ = 0; if (!StrateManager->GetStrateChunkZBounds(ChunkZ, FailedTopCZ, FailedBotCZ)) @@ -7140,8 +7138,8 @@ EVoxelTileClass UVoxelGenerator::ClassifyTileUncached( bCanAir = false; } // 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 - // therefore must kill the AllAir hypothesis before a tile can be skipped. + // They add solid MC samples and therefore must kill the AllAir hypothesis before a tile can + // be skipped. if (bHasPassageStructuralPostCandidate) { bCanAir = false; @@ -7179,7 +7177,7 @@ EVoxelTileClass UVoxelGenerator::ClassifyTileUncached( // `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 // 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 // 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 // islands (each samples a different part of the field → distinct silhouettes). 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 - // balayage du 2026-07-27 cherchait le motif `SeedF * K` et celui-ci s'écrit `(float)S * K`, donc - // il a survécu : à Seed = 2e9 le terme atteint ~1.4e6, où l'ULP du float vaut 0.125 contre un pas - // de 0.04 par voxel — le warp s'aplatit et les îles redeviennent des cercles parfaits. Corrigé - // dans les DEUX chemins (ici et FIslandBlobSource) en une passe, pour que le test d'équivalence - // reste un oracle valable. + // ⚠️ AUDIT §C1 — ne pas écrire l'offset de seed `(float)S * K` : à Seed = 2e9 le terme atteint + // ~1.4e6, où l'ULP du float vaut 0.125 contre un pas de 0.04 par voxel — le warp s'aplatit et + // les îles redeviennent des cercles parfaits. `VoxelHash::SeedOffset` est utilisé dans les DEUX + // chemins (ici et FIslandBlobSource), 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 // (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. diff --git a/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp b/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp index 3c99e88..304302e 100644 --- a/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp +++ b/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp @@ -91,8 +91,7 @@ void UVoxelMarchingCubesMesher::InitializeConsoleSwitches() //============================================================================= // MAIN ALGORITHM //============================================================================= -// (L'ancien trio GetDensity / InterpolateEdge / ComputeGradientNormal a été retiré : -// mort depuis T1.b — la grille pré-échantillonnée fournit positions ET gradients.) +// La grille pré-échantillonnée fournit positions ET gradients. FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels, int32 Step, int32 InCellsPerAxis, TArray* 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 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 - // les surplombs des tuiles majoritairement plafond). Un futur toit de grotte (aussi down- + // surplomb de terrain (reste "sol" — un vote par tuile mettrait le matériau ciel sous les + // 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. // 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 ; diff --git a/Source/VoxelForge/Private/VoxelStrateComposer.cpp b/Source/VoxelForge/Private/VoxelStrateComposer.cpp index 691c8da..9bc9877 100644 --- a/Source/VoxelForge/Private/VoxelStrateComposer.cpp +++ b/Source/VoxelForge/Private/VoxelStrateComposer.cpp @@ -4872,7 +4872,7 @@ FVoxelStrateRollInfo VF_RollStrateParamsDetailed(const FVoxelStrateCorpus& Corpu Repeat.ArchetypeParams, Result.ArchetypeParams, Result.Archetype) && Repeat.ParentEntryIndices == Result.ParentEntryIndices && 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 return Result; diff --git a/Source/VoxelForge/Private/VoxelStrateManager.cpp b/Source/VoxelForge/Private/VoxelStrateManager.cpp index e4469ae..a8dce8a 100644 --- a/Source/VoxelForge/Private/VoxelStrateManager.cpp +++ b/Source/VoxelForge/Private/VoxelStrateManager.cpp @@ -234,8 +234,8 @@ namespace { // 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 - // before any caller can observe them. Keeping the entries indexed by passage avoids the TMap - // rehash path that this hot loop used to carry, while a collision-free hit remains exact. + // before any caller can observe them. Keeping the entries indexed by passage avoids a TMap + // rehash path in this hot loop, while a collision-free hit remains exact. struct FPassageFloorProjectionCacheEntry { // The sample key lives once in FPassageEvaluationCache. A monotonically increasing @@ -1804,13 +1804,13 @@ void UVoxelStrateManager::GeneratePassages() case ECaveGeneratorType::TunnelNetwork: case ECaveGeneratorType::Underwater: // 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 - // plus proche puis atterrissait à côté d'elle dans 92,8 % des cas (balayage d'un million - // de seeds). Un espacement de salles est le voisinage local naturel. + // budget doit donc être réel. Zéro ici est un bug — la requête trouve la salle la plus + // proche puis atterrit à côté d'elle dans 92,8 % des cas (balayage d'un million de + // 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 - // real. Zero here was the bug: the query found the nearest room and then landed beside - // it 92.8% of the time (million-seed sweep). One room spacing is the natural local + // real. Zero here is a bug: the query finds the nearest room and then lands beside it + // 92.8% of the time (million-seed sweep). One room spacing is the natural local // neighbourhood, and it protects the configured spine-distance distribution. 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]; // BOUNDING-SPHERE REJECT: skip passages this voxel can't possibly be inside. - // EvaluateModifierSDF runs PER VOXEL and used to evaluate every passage's full - // capsule chain unconditionally — the dominant lag source once passages became - // 12-segment worms. Now far passages cost a single squared-distance compare. + // EvaluateModifierSDF runs PER VOXEL; evaluating every passage's full capsule chain + // unconditionally is the dominant lag source with 12-segment worm passages. Far + // passages cost a single squared-distance compare. if (FVector::DistSquared(Pos, P.BoundCenter) > P.BoundRadiusSq) continue; if (VoxelDensityProfile::AreCountersEnabled()) { @@ -3254,8 +3254,8 @@ void UVoxelStrateManager::ApplyPassageStructuralPostsMC( } } - // The graph tunnel no longer exposes a separate support slab. Inter-strate landing carving - // remains independent and is still composed here when its own reach proof allows it. + // The graph tunnel does not expose a separate support slab. Inter-strate landing carving + // is independent and is composed here when its own reach proof allows it. if (!bDisableLandingPosts && !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 // 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( FMath::Max((float)VoxelBox.Min.X, PotentialMinX), 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. switch (Def->GeneratorType) { - case ECaveGeneratorType::Maze: return true; // Phase 1 - case ECaveGeneratorType::FlatPlain: // Phase 2 — les deux partagent + case ECaveGeneratorType::Maze: return true; + case ECaveGeneratorType::FlatPlain: // les deux partagent case ECaveGeneratorType::CrystalChamber: return true; // UNE seule pile (BuildSlabStack) case ECaveGeneratorType::SurfaceWorld: - // ✅ La garde « pas de biomes » est TOMBÉE (étape 2c) : le combiner `Mask` existe, donc une - // strate à biomes mélange bien ses hauteurs comme le chemin d'origine. Les trois archétypes - // du dessus plus celui-ci font 5 des 8 portés. - // The no-biome guard is GONE: the Mask combiner exists, so a biome strate blends its heights + // Pas de garde « pas de biomes » : le combiner `Mask` existe, donc une strate à biomes + // mélange bien ses hauteurs comme le chemin d'origine. + // No no-biome guard: the Mask combiner exists, so a biome strate blends its heights // exactly as the original path does. 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: - // 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. 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 // que par `GetWaterLevel*` de ce manager — jamais par la densité (vérifié, pas supposé). case ECaveGeneratorType::TunnelNetwork: - // Phase 2, LE DERNIER, et le plus gros : ~1080 lignes portées en trois étapes (squelette - // SDF → douze modificateurs de détail → override d'op par salle), 19 opérateurs, dont - // `FRoomGraphSource` qui **APPELLE** `BuildChunkCache`/`EvaluateSDFCached` au lieu de les - // transcrire — c'est là que vit la discipline d'invariance de fenêtre d'ARCHITECTURE §8.4, - // et en forker une copie aurait été le pire résultat possible de ce refactor. + // Le plus gros : squelette SDF → douze modificateurs de détail → override d'op par salle, + // 19 opérateurs, dont `FRoomGraphSource` qui **APPELLE** `BuildChunkCache`/`EvaluateSDFCached` + // au lieu de les transcrire — c'est là que vit la discipline d'invariance de fenêtre + // d'ARCHITECTURE §8.4 ; en forker une copie la casserait. // - // **8 SUR 8.** Le `switch` d'archétypes a désormais un jumeau en pile d'opérateurs, opt-in - // par strate, chacun vérifié par un test d'équivalence bit à bit contre sa fonction - // d'origine. Ce qui n'est PAS fait : `ClassifyTile` n'utilise toujours pas `ClassifyBox`. + // **8 SUR 8.** Le `switch` d'archétypes a un jumeau en pile d'opérateurs, opt-in par + // strate, chacun vérifié par un test d'équivalence bit à bit contre sa fonction d'origine. return true; default: return false; @@ -6903,7 +6900,7 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto //============================================================================= // 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 // per-room during BuildChunkCache() via FCachedRoom::RoomOp — each room hash-rolls // one op from the strate's probability pool (FStrateTerrainOpEntry::Probability). diff --git a/Source/VoxelForge/Private/VoxelWorld.cpp b/Source/VoxelForge/Private/VoxelWorld.cpp index 3b16b2e..2eb2737 100644 --- a/Source/VoxelForge/Private/VoxelWorld.cpp +++ b/Source/VoxelForge/Private/VoxelWorld.cpp @@ -23,11 +23,10 @@ #include "VoxelTilePostReach.h" #include "VoxelClipmapDesiredTiles.h" // 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 -// complet par transitivité seulement : on l'inclut explicitement, c'est exactement la fragilité -// qu'on est en train de retirer. +// APawn doit être COMPLET — `Casts.h` n'en donne qu'une déclaration avant. APlayerController n'est +// complet que par transitivité : on l'inclut explicitement. // 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/PlayerController.h" #include "GameFramework/PawnMovementComponent.h" @@ -185,11 +184,11 @@ AVoxelWorld::AVoxelWorld() //============================================================================= // 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 -// in ApplyMeshToTile ON THE GAME THREAD, where it was the dominant streaming cost (game thread -// >6 ms while moving, GPU/draw idle). It touches ONLY the POD MeshData arrays — no UObject, no -// generator — so it's safe on the gen worker. The game thread then just uploads the finished -// streams (CreateSectionGroup). Byte-identical geometry; the only thing that moved is WHERE it runs. +// FRealtimeMeshStreamSet is plain CPU data; on the game thread (in ApplyMeshToTile) the +// per-vertex/per-triangle builder loop is the dominant streaming cost (game thread >6 ms while +// moving, GPU/draw idle). It touches ONLY the POD MeshData arrays — no UObject, no generator — so +// it's safe on the gen worker. The game thread then just uploads the finished streams +// (CreateSectionGroup). Byte-identical geometry; only WHERE it runs differs. static void BuildTileStreamSet(RealtimeMesh::FRealtimeMeshStreamSet& Streams, const FVoxelMeshData& MeshData) { RealtimeMesh::TRealtimeMeshBuilderLocal Builder(Streams); @@ -8044,7 +8043,7 @@ void AVoxelWorld::UnloadTile(const FVoxelTileKey& Tile) CollisionNotRequiredTiles.Remove(Tile); CollisionSolidTiles.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. if (URealtimeMeshComponent** Comp = TileComponents.Find(Tile)) { @@ -8220,7 +8219,7 @@ bool AVoxelWorld::ApplyMeshToTile(FChunkResult& Result) } BindRealtimeMeshCollisionEvent(RTMesh); // 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. const bool bCastShadow = (Tile.Level <= 1); MeshComp->SetCastShadow(bCastShadow); @@ -8340,7 +8339,7 @@ bool AVoxelWorld::ApplyMeshToTile(FChunkResult& Result) 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. return true; } @@ -9022,9 +9021,9 @@ void AVoxelWorld::UpdateOrbLightMPC() const FVoxelActiveOrb& O = Orbs[i]; 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 - // lighting: the MPC's world INSTANCE can be reset/recreated behind our back (PIE init order, - // asset recompile), and a cached skip then leaves it holding defaults forever. The per-frame + // ALWAYS write, even when unchanged. A skip-if-identical cache BREAKS the lighting: the + // MPC's world INSTANCE can be reset/recreated behind our back (PIE init order, asset + // 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. UKismetMaterialLibrary::SetVectorParameterValue(this, OrbLightMPC, OrbNames[i], V); } diff --git a/Source/VoxelForge/Public/VoxelBiomeDefinition.h b/Source/VoxelForge/Public/VoxelBiomeDefinition.h index 92ab3ff..4b1d451 100644 --- a/Source/VoxelForge/Public/VoxelBiomeDefinition.h +++ b/Source/VoxelForge/Public/VoxelBiomeDefinition.h @@ -22,9 +22,9 @@ #include "VoxelStrateTypes.h" // FStrateDecoration / FStrateAmbientActor #include "VoxelBiomeDefinition.generated.h" -// IWYU : utilisé en pointeur seulement ⇒ déclaration avant. Fournie gratuitement par le PCH -// partagé jusqu'ici ; `FPSemantics = Precise` nous en sort. / Pointer-only use, so a forward -// declaration is enough. The shared PCH used to provide this for free. +// IWYU : utilisé en pointeur seulement ⇒ déclaration avant. Le PCH partagé ne la fournit pas : +// `FPSemantics = Precise` nous en sort. / Pointer-only use, so a forward declaration is enough. +// The shared PCH does not provide it (FPSemantics = Precise opts this module out of it). class UMaterialInterface; /** diff --git a/Source/VoxelForge/Public/VoxelCaveMorphology.h b/Source/VoxelForge/Public/VoxelCaveMorphology.h index 1e62ac9..ffe22c9 100644 --- a/Source/VoxelForge/Public/VoxelCaveMorphology.h +++ b/Source/VoxelForge/Public/VoxelCaveMorphology.h @@ -243,7 +243,7 @@ namespace VoxelHash * collident donneraient un bruit identique PARTOUT. Salé par site, il faudrait qu'ils * 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. * * @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 // 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 -// à la pile d'opérateurs. Il vit ici pour que les deux chemins partagent la même implémentation. +// Il vit ici pour que le générateur et la pile d'opérateurs partagent la même implémentation. // 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. // 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 //============================================================================= // 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 // - Determine room existence, position, size // - Run O(N²) nearest-neighbor backbone @@ -604,9 +604,9 @@ struct FCachedRoom float RoomOpWeight = 1.0f; // 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 - // mixes + trig in the hottest loop of the plugin for values that are constants of the room. - // Bit-identical to the old per-voxel roll (same hashes, same math, done once per chunk). + // Cos/Sin direction are constants of the room: re-deriving them PER VOXEL inside + // EvaluateSDFCached would put hash mixes + trig in the hottest loop of the plugin. + // Bit-identical to a per-voxel roll (same hashes, same math, done once per chunk). // 0 = ellipsoid → ShapeA = radii (x=y=RadiusXY, z=RadiusZ) // 1 = rounded box → ShapeA = half-extents, ShapeR = corner rounding // 2 = capsule → ShapeA/ShapeB = world endpoints, ShapeR = tube radius @@ -929,8 +929,7 @@ struct FChunkSDFCache VoxelDensityProfile::FCacheMemoryBreakdown GetAllocatedSizeBreakdown() const; }; -/** World-space tunnel shape query. Legacy support-floor fields remain source-compatible but are - * no longer produced or consumed by the graph-tunnel generation path. */ +/** World-space tunnel shape query. */ struct FTunnelCoreWorldEvaluation { float SDF = FLT_MAX; @@ -949,7 +948,6 @@ struct FTunnelCoreWorldEvaluation // CAVE MORPHOLOGY EVALUATOR //============================================================================= // Two-phase evaluation: BuildChunkCache (once per chunk) + EvaluateSDFCached (per voxel). -// The original EvaluateSDF is kept as a convenience wrapper for backward compatibility. namespace VoxelCaveMorphology { diff --git a/Source/VoxelForge/Public/VoxelDensityOp.h b/Source/VoxelForge/Public/VoxelDensityOp.h index acbf223..52982ab 100644 --- a/Source/VoxelForge/Public/VoxelDensityOp.h +++ b/Source/VoxelForge/Public/VoxelDensityOp.h @@ -1,6 +1,6 @@ // VoxelDensityOp.h // 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. // // ───────────────────────────────────────────────────────────────────────────────────────── @@ -714,13 +714,12 @@ public: /** * 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 - * l'`EffectOverBox` spatial est revenu **vert avec 0 tuile prouvée sur 40**, et la seule chose - * que le rapport pouvait dire était « ou bien les tuiles traversent toutes une grotte, ou bien - * la source n'atteint pas sa branche `Identity` ». Deux causes, zéro nombre pour les - * départager — exactement le piège que ce projet a déjà payé plusieurs fois. La vraie cause - * était un TROISIÈME opérateur (le ver, qui rendait `CarveOnly` partout). Avec un nom par - * opérateur, `ClassifyBoxAttributed` répond « c'est celui-là » au lieu de laisser deviner. + * ⚠️ POURQUOI CETTE MÉTHODE EXISTE. Sans nom par opérateur, un rapport « 0 tuile prouvée sur + * 40 » ne peut dire que « ou bien les tuiles traversent toutes une grotte, ou bien la source + * n'atteint pas sa branche `Identity` » — deux causes, zéro nombre pour les départager, alors + * que la vraie cause peut être un TROISIÈME opérateur (p. ex. un ver qui rend `CarveOnly` + * partout). Avec un nom par 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 * changer ne peut pas changer le monde. Le défaut est volontairement laconique — un opérateur diff --git a/Source/VoxelForge/Public/VoxelDensityOpStack.h b/Source/VoxelForge/Public/VoxelDensityOpStack.h index 008725b..240e956 100644 --- a/Source/VoxelForge/Public/VoxelDensityOpStack.h +++ b/Source/VoxelForge/Public/VoxelDensityOpStack.h @@ -2,7 +2,7 @@ // 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. // -// ⚠️ 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` // 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 : @@ -18,18 +18,17 @@ // archetypes; gaps and SurfaceWorld retain their exact hand-written proofs. // // ⛔ 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 -// périmée. `AUDIT §C10` (le résidu ~1 ULP) est CLOS depuis `FPSemantics = Precise` : les cinq tests -// d'équivalence comparent bit à bit et sont verts. Ils ne sont plus des contrôles de FIDÉLITÉ (la -// barre `§2.6.1` n'exige aucune ressemblance avec l'ancien monde) mais des oracles de -// CORRECTION DE PORTAGE — une faute de transcription reste un vrai bug, et l'ancienne fonction est +// ⚠️ EN REVANCHE, LES COMPARER EST LÉGITIME. `AUDIT §C10` (le résidu ~1 ULP) est clos par +// `FPSemantics = Precise` : les cinq tests d'équivalence comparent bit à bit. Ce ne sont pas des +// contrôles de FIDÉLITÉ (la barre `§2.6.1` n'exige aucune ressemblance avec l'ancien monde) mais +// des oracles de CORRECTION DE PORTAGE — une faute de transcription reste un vrai bug, et l'ancienne fonction est // le moyen le moins cher de l'attraper. // // 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 ? »** -// (OPSTACK-PLAN §4, le déclencheur d'arrêt). En portant Maze hors du chemin chaud et en le -// comparant à l'original, cette question reçoit une réponse MESURÉE plutôt qu'une opinion. +// (OPSTACK-PLAN §4). Chaque archétype porté est comparé bit à bit à l'original : la réponse est +// MESURÉE plutôt qu'une opinion. #pragma once @@ -229,11 +228,11 @@ public: // 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 - // disturbance/passage writers; composing the floor at this point made every authored - // floor sample look like a post-hoc clamp (and counted 100% of them). The generator now + // 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 // 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 - // 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; } diff --git a/Source/VoxelForge/Public/VoxelDensityPrimitives.h b/Source/VoxelForge/Public/VoxelDensityPrimitives.h index 66101d1..7fb17f4 100644 --- a/Source/VoxelForge/Public/VoxelDensityPrimitives.h +++ b/Source/VoxelForge/Public/VoxelDensityPrimitives.h @@ -68,8 +68,8 @@ FORCEINLINE void VF_ApplyBoundarySeal(float& Density, float WorldZ, // CARVE DE PASSAGE / PASSAGE CARVING //============================================================================= // 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 — -// à exposer via UVoxelSettings si on veut pouvoir le tweaker. +// travers le bouchon solide. Le rayon de blend est hard-codé à 4.0f — à exposer via +// UVoxelSettings si on veut pouvoir le tweaker. FORCEINLINE void VF_ApplyPassageCarving(float& Density, float ModSDF, float BaseDensity, float SealThickness) { diff --git a/Source/VoxelForge/Public/VoxelDensityVolume.h b/Source/VoxelForge/Public/VoxelDensityVolume.h index f410edb..bedc114 100644 --- a/Source/VoxelForge/Public/VoxelDensityVolume.h +++ b/Source/VoxelForge/Public/VoxelDensityVolume.h @@ -177,9 +177,9 @@ private: void LaunchPendingFills(); // flush PendingFills onto the dedicated fill thread 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), - // where it STARVED behind mesh-gen (10 s to resolve shadows at a fresh spot). It now runs on its own - // thread (off the pool) so it's fast AND never steals a core from mesh-gen. Game thread enqueues + // DEDICATED FILL THREAD. On the shared UE::Tasks pool (BackgroundLow) the volume fill STARVES + // behind mesh-gen (10 s to resolve shadows at a fresh spot). It runs on its own thread (off the + // 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 // (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. diff --git a/Source/VoxelForge/Public/VoxelGenerator.h b/Source/VoxelForge/Public/VoxelGenerator.h index 8e49d68..350b9ed 100644 --- a/Source/VoxelForge/Public/VoxelGenerator.h +++ b/Source/VoxelForge/Public/VoxelGenerator.h @@ -205,9 +205,8 @@ public: * 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. * - * ⚠️ `ParamsFingerprint` ET `LayoutVersion` SONT OBLIGATOIRES, ET C'EST LE CORRECTIF - * D'`AUDIT §C2` (2026-07-28). Le cache SDF 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 + * ⚠️ `ParamsFingerprint` ET `LayoutVersion` SONT OBLIGATOIRES (`AUDIT §C2`). Le cache SDF + * 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 * 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 * même strate, même seed, donc même clé, mais des params DIFFÉRENTS : le worker évalue le diff --git a/Source/VoxelForge/Public/VoxelStrateTypes.h b/Source/VoxelForge/Public/VoxelStrateTypes.h index e43b88c..b7b514b 100644 --- a/Source/VoxelForge/Public/VoxelStrateTypes.h +++ b/Source/VoxelForge/Public/VoxelStrateTypes.h @@ -207,7 +207,7 @@ enum class ECaveGeneratorType : uint8 * is defined on the UPPER strate's definition and controls the boundary between * 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 * geological feature at the boundary. Best for similar-looking strates. * @@ -228,7 +228,7 @@ UENUM(BlueprintType) enum class EVoxelStrateTransition : uint8 { // 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)"), // 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). - * 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 * object per ~`SpacingChunks` lattice cell, so the work scales with how MANY landmarks are in range diff --git a/Source/VoxelForge/Public/VoxelTypes.h b/Source/VoxelForge/Public/VoxelTypes.h index 82380ad..358c5bd 100644 --- a/Source/VoxelForge/Public/VoxelTypes.h +++ b/Source/VoxelForge/Public/VoxelTypes.h @@ -203,11 +203,10 @@ namespace VoxelMath // 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. // -// NOTE: 64³ a été essayé ("B", 2026-06-16) pour couper les draw calls (8× moins de -// chunks) mais le streaming devenait trop saccadé (briques 8× plus lourdes → applies -// + spawns de contenu en gros à-coups sur le game thread). Reverté à 32³ : le fps se -// règle côté RENDU (ombres off sur LOD lointain, voir ApplyMeshToChunk), pas via la -// taille de chunk. La taille de chunk reste le levier streaming-vs-draws si besoin. +// NOTE: 64³ coupe les draw calls (8× moins de chunks) mais rend le streaming trop +// saccadé (briques 8× plus lourdes → applies + spawns de contenu en gros à-coups sur +// le game thread). Le fps se règle côté RENDU (ombres off sur LOD lointain, voir +// ApplyMeshToChunk), pas via la taille de chunk. La taille de chunk reste le levier streaming-vs-draws si besoin. constexpr int32 CHUNK_SIZE = 32; diff --git a/Source/VoxelForge/Public/VoxelWorld.h b/Source/VoxelForge/Public/VoxelWorld.h index 86cbdb0..879f1c4 100644 --- a/Source/VoxelForge/Public/VoxelWorld.h +++ b/Source/VoxelForge/Public/VoxelWorld.h @@ -1246,8 +1246,8 @@ public: // 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 // 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 - // CullTiles ~1.6 ms/crossing de la trace 2026-07-05). + // TransitionHold — pas de scan O(toutes-les-tuiles-chargées) à chaque crossing (un tel scan + // coûtait ~1.6 ms/crossing dans CullTiles). TMap DesiredStamped; uint32 DesiredStamp = 0; bool IsDesired(const FVoxelTileKey& T) const diff --git a/Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp b/Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp index 9b9bd41..9774045 100644 --- a/Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp +++ b/Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp @@ -739,7 +739,7 @@ bool ParseArguments(const FString& Params, FExploreArguments& OutArguments, FStr OutError = TEXT("rendermaxdistance must be finite and in (0,2048] voxels."); 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 // only maxdistance as the raster depth clip. if (OutArguments.ExportSize < CHUNK_SIZE