Files
VoxelForge/Source/VoxelForge/Public/VoxelMarchingCubesMesher.h
T
Fr0zkaandClaude Opus 5.5 bc0f9ee791 wip(step3): 12.5 cm voxels, fall-through fixed, brick evaluator in game, organic tunnels -- speed gate FAILS
Stopped by the owner's decision (in-game lit captures were eating the round); he judges the look in
game instead.
1. Fall-through with the research field: fixed. Deep drop, gate on: 2 holds (0.50 s), min foot
   clearance +43 cm; gate off: penetrates (-278 cm). Stress fixture offsets converted to metres.
2. VOXEL_SIZE 25 -> 12.5 cm. Physical sizes kept in metres (player, steps, gate, insets, research
   field sizes, dig radii, test thresholds); class defaults that count chunks/voxels doubled
   (ClipRadius 6, RenderDistanceChunks 1536, ViewDistance*, StrateViewMargin 6,
   PawnCollisionRadius 2, SkirtCells 4, EdgeSealThickness 128, decoration spacings, density volume
   res 256). Asset values override these defaults: the owner must double his own.
   Ring scale, six workers, idle, step-2f field before the look changes: 64 m cube (519^3) 4.55 s;
   32 m cube (259^3) 0.95 s.
3. In game: tiles go through the brick evaluator (proved by a log marker); profile switch rebuilds
   the loaded world (~5,800 tiles); full-detail fill 12.4 s after spawn in the visible-window test.
4. Look: round winding tunnels, per-column ellipsoid/lattice caches. All step-2f geometry gates pass
   at 12.5 cm (0 crumbs, 1 solid component per profile, solid share 69-87%). 259^3 ring, six workers:
   1.08-2.02 s (worst HyperComplex) -- the 600 ms gate FAILS. Lit captures failed (hidden/off-screen
   window, then blank frames); not pursued.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-01 21:21:45 +02:00

145 lines
7.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// VoxelMarchingCubesMesher.h
// Mesh lisse à partir du champ de densité du générateur.
//
// Principe:
// 1. Chaque cellule du chunk (cube 2x2x2 voxels) lit la densité aux 8 coins.
// 2. Un index 8 bits indique quels coins sont au-dessus du IsoLevel.
// 3. Les tables MC (EdgeTable + TriTable) donnent les triangles à générer.
// 4. On interpole la position du vertex le long de chaque arête traversée.
//
// LOD: Step > 1 échantillonne moins souvent → moins de triangles à distance.
#pragma once
#include "CoreMinimal.h"
#include <atomic>
#include "VoxelTypes.h" // Pour FVoxelMeshData, CHUNK_SIZE, VOXEL_SIZE, etc.
#include "VoxelGenerator.h"
#include "VoxelMarchingCubesMesher.generated.h"
// A run-scoped, read-only density lattice. The explorer owns the storage and installs the
// pointer only while its canonical tiles are being meshed. The lattice includes the one-point
// halo needed by every tile, so each world sample is generated once and copied into both adjacent
// tile scratch grids. It is deliberately not a UObject or a cache with eviction: ownership,
// lifetime, and iteration order remain explicit and deterministic.
struct FVoxelSharedDensityGrid
{
FIntVector OriginVoxels = FIntVector::ZeroValue;
int32 Step = 1;
int32 Dim = 0;
TArray<float> Samples;
void Reset()
{
OriginVoxels = FIntVector::ZeroValue;
Step = 1;
Dim = 0;
Samples.Reset();
}
};
UCLASS(BlueprintType)
class VOXELFORGE_API UVoxelMarchingCubesMesher : public UObject
{
GENERATED_BODY()
public:
// Parse command-line switches on the module's game-thread startup. GenerateMesh is a worker
// entry point and therefore only reads the resulting values.
static void InitializeConsoleSwitches();
/**
* Génère le mesh d'une TUILE de clipmap (voir FVoxelTileKey).
*
* @param OriginVoxels - Coin min de la tuile en coords VOXEL (= Tile.OriginVoxels()).
* @param Step - Taille de cellule en voxels. La tuile couvre CellsPerAxis*Step voxels.
* @param CellsPerAxis - Nombre de cellules par axe. Les tuiles GROSSIÈRES en utilisent MOINS
* (gen moins chère, maillage plus grossier au loin) tout en couvrant la
* même étendue (extent = CellsPerAxis*Step). Niveau 0 = CHUNK_SIZE.
* @param OutCaptureGrid - CAPTURE-DURING-MESHING (optionnel). Si non-null ET CellsPerAxis==CHUNK_SIZE
* (tuile pleine résolution, Step==1<<Level, donc 1:1 avec une cellule du clipmap
* de densité), on y recopie les CHUNK_SIZE³ points intérieurs de la grille de
* densité déjà échantillonnée, quantifiés via VF_QuantizeDensity. Cela évite à
* UVoxelDensityVolume de re-sampler GetDensityAt pour ces cellules (le mesher
* les a déjà calculées). Vidé puis rempli ; reste vide si non éligible.
* @param BandZMinVox / BandZMaxVox - COUPE DE CONTENU PAR STRATE (optionnel, voxels Z INCLUSIFS,
* cf. UVoxelSettings::StrateContentCutMinLevel) : seules les cellules dont
* l'intervalle Z chevauche la bande sont maillées. Le runtime ne passe
* jamais de bande aux tuiles LOD0, car elles possèdent la collision du pawn.
* Une tuile grossière qui
* chevauche une frontière de strate ne maille que la strate du joueur —
* supprime les trous d'aliasing (le bouchon seal/gap plus fin que Step
* tombait entre deux points du treillis) et le mélange de matériaux entre
* strates. Les cellules maillées restent identiques au bit près (mêmes
* échantillons monde purs). Jamais combiné avec OutCaptureGrid.
*/
FVoxelMeshData GenerateMesh(FIntVector OriginVoxels, int32 Step = 1, int32 CellsPerAxis = CHUNK_SIZE,
TArray<uint8>* OutCaptureGrid = nullptr,
int32 BandZMinVox = INT32_MIN, int32 BandZMaxVox = INT32_MAX,
int64* OutDensitySampleCount = nullptr,
FVoxelSharedDensityGrid* OutPreEditDensityGrid = nullptr,
const FVoxelSharedDensityGrid* InSharedDensityGrid = nullptr);
//=========================================================================
// SERVICES (injectés par AVoxelWorld)
//=========================================================================
// Le générateur fournit la densité par voxel.
UPROPERTY()
const UVoxelGenerator* Generator = nullptr;
void SetGenerator(const UVoxelGenerator* InGenerator) { Generator = InGenerator; }
// AVoxelWorld installs its bShuttingDown flag here. The commandlet path leaves it null.
// This is intentionally read-only: workers may observe shutdown, but never mutate world state.
void SetShutdownFlag(const std::atomic<bool>* InShutdownFlag) { ShutdownFlag = InShutdownFlag; }
// Optional run-scoped shared lattice. The caller must keep it alive and immutable until all
// GenerateMesh calls finish. A malformed/incomplete lattice is ignored and falls back to
// the canonical generator path rather than changing geometry.
void SetSharedDensityGrid(const FVoxelSharedDensityGrid* InGrid) { SharedDensityGrid = InGrid; }
private:
FORCEINLINE bool ShouldAbortWork() const
{
return ShutdownFlag && ShutdownFlag->load(std::memory_order_relaxed);
}
const std::atomic<bool>* ShutdownFlag = nullptr;
const FVoxelSharedDensityGrid* SharedDensityGrid = nullptr;
public:
//=========================================================================
// SETTINGS
//=========================================================================
// Seuil de l'isosurface dans le champ de densité.
// Convention MC: densité < IsoLevel = solide, >= = air.
float IsoLevel = 0.0f;
// SKIRTS — bouchent les fissures aux frontières de tuiles entre niveaux de clipmap voisins
// (résolutions différentes → les iso-surfaces ne se rejoignent pas exactement). Une jupe
// (mur court) est extrudée vers le solide depuis chaque arête de surface posée sur une des 6
// faces externes de la tuile. Voir GenerateMesh.
bool bGenerateSkirts = true;
// Profondeur de la jupe, en CELLULES de la tuile (× Step × VOXEL_SIZE). Four L0 cells preserve
// the 0.5 m default extrusion at 12.5 cm per voxel.
float SkirtCells = 4.0f;
// Conservative 8x8x8 block classifier. Uniform blocks skip the marching-cubes cell loop;
// when every relevant block has the same verdict, the density grid is skipped as well.
// ClassifyTile is proof-only, so no samples, resolution, or geometry are removed.
// The tile classifier is the cheap, whole-tile proof. Keep the optional 8^3-block refinement
// available for focused diagnostics, but leave it disabled in streaming: repeating a proof on
// every mixed tile can cost more than the density grid it is meant to avoid.
bool bUseBlockEarlyOut = false;
// T2.b — LOD-aware octave reduction (opt-in, copied from UVoxelSettings::LODOctaveDrop).
// Octaves dropped from per-voxel volumetric noise PER Step doubling: a tile at Step=S
// drops LODOctaveDrop * log2(S) octaves (see VoxelGenLOD in VoxelGenerator.h).
// 0 (default) = off — every LOD samples full octaves, byte-identical to before.
// Réduction d'octaves sur les tuiles grossières ; 0 = désactivé.
int32 LODOctaveDrop = 0;
};