#ifndef DC_DENSITY_H
#define DC_DENSITY_H
// Density function for Dual Contouring terrain generation.
// Pure C++ — no Godot dependencies. Evaluates a 7-layer noise field
// ported from voxel_terrain.gd.
//
// Positive density = solid, negative = air.
//
// Layers:
// 1. Domain warping (organic coordinate distortion)
// 2. Ground plane bias (weak downward pull)
// 3. Primary 3D noise (dominant shape — caves, masses)
// 4. Worm tunnels (two perpendicular noises carve tubes)
// 5. Ridged noise (pillars, arches, bridges)
// 6. Detail noise (surface roughness)
// 7. Voronoi floors/ceilings (flat walkable areas)
// + Height clamping (solid floor, open sky)
// + Flat spawn area (safe zone near origin)
#include "dc_edit.h"
#include <memory>
#include <vector>
// CSG primitive for density field composition.
// Primitives in the same group are combined sequentially (first = base, rest apply op).
// Each group result is UNION'd with the terrain density.
struct CSGPrimitive {
enum Type : uint8_t { CSG_SPHERE = 0, CSG_BOX = 1 };
enum Op : uint8_t { CSG_UNION = 0, CSG_SUBTRACT = 1, CSG_INTERSECT = 2 };
Type type = CSG_SPHERE;
Op op = CSG_UNION;
int group = 0;
float center[3] = {0.0f, 0.0f, 0.0f};
float size[3] = {1.0f, 1.0f, 1.0f}; // sphere: [0]=radius, box: half-extents
};
struct DCDensityNoise; // forward declaration (defined in dc_density.cpp)
struct DensityConfig {
int seed = 42;
float voxel_size = 1.0f; // used for Nyquist-aware octave clamping
// When false, skip all noise layers — density = flat ground plane + CSG only.
bool use_noise = true;
// Frequencies are in cycles/meter. Nyquist limit: max freq = 0.5/voxel_size.
// With fbm octaves and lacunarity=2, highest octave freq = base * 2^(oct-1).
// Defaults tuned for voxel_size=1.0 (Nyquist=0.5):
// primary: 0.02 * 4 = 0.08 (wavelength 12.5m = 12.5 voxels) ✓
// tunnel: 0.03 * 2 = 0.06 (wavelength 16.7m) ✓
// ridge: 0.03 * 2 = 0.06 (wavelength 16.7m) ✓
// detail: 0.06 * 2 = 0.12 (wavelength 8.3m = 8 voxels) ✓
// warp: 0.015 * 2 = 0.03 (wavelength 33m) ✓, strength 4 = ±4 voxels
float primary_frequency = 0.02f;
float primary_strength = 1.2f;
float tunnel_frequency = 0.03f;
float tunnel_strength = 1.5f;
float tunnel_radius = 0.3f;
float ridge_frequency = 0.03f;
float ridge_strength = 0.5f;
float warp_frequency = 0.015f;
float warp_strength = 4.0f; // ±4m warp (was 8 — caused massive aliasing)
float detail_frequency = 0.06f;
float detail_strength = 0.15f;
float ground_bias = 0.06f; // slightly stronger ground plane
float solid_floor_depth = 30.0f;
float sky_height = 25.0f;
float flat_center_radius = 12.0f;
int voronoi_cell_count = 6;
float floor_strength = 0.7f;
float floor_height_min = -15.0f;
float floor_height_max = 10.0f;
float room_height = 8.0f;
float terrain_size = 60.0f;
// Spherical/planetoid mode
bool spherical = false;
float planet_radius = 250000.0f;
float planet_center[3] = {0.0f, 0.0f, 0.0f};
// Floating island mode
bool floating_islands = false;
float island_radius = 100.0f; // XZ radius of main island
float island_depth_ratio = 3.0f; // Y depth = radius * ratio (iceberg)
int island_count = 3; // number of islands
float island_spacing = 300.0f; // distance between island centers
// Cap on noise octaves (0 = no cap, use Nyquist limit).
// Lower values = smoother surface while preserving large-scale features.
// E.g., 6 keeps mountains but removes meter-scale bumps.
int max_noise_octaves = 0;
// Separate cap for ridge octaves (0 = use max_noise_octaves).
// Ridge noise uses 1-|x| which creates derivative discontinuities at peaks.
// Fewer octaves = broader, smoother ridges without stair-stepping.
// E.g., 3 gives continent-scale ridges only.
int max_ridge_octaves = 0;
// Terrain edits: shared snapshot, safe for workers to read concurrently.
// Set via DCTerrain — workers get a copy of the shared_ptr (ref-counted).
std::shared_ptr<const std::vector<TerrainEdit>> edits;
// CSG primitives: sorted by group, evaluated per-group then UNION'd with terrain.
std::vector<CSGPrimitive> csg_primitives;
// Force direct density evaluation (Gildea path) even for noise-only terrain.
// Slower (~3x) but gives precise bisection everywhere.
bool force_direct_eval = false;
// Use GPU compute pipeline for terrain generation.
// Falls back to CPU if GPU initialization fails.
bool use_gpu = false; // default off until validated
// CSG refinement factor: subdivide N× finer near CSG boundaries.
// The octree prunes empty cells, so cost = O(surface_area), not O(volume).
// 2 = 2× finer (64 effective cells at LOD0), 4 = 4× finer (128 cells).
int csg_refinement = 2;
};
class DCDensity {
public:
struct VoronoiCell {
float x;
float z;
float floor_height;
};
DCDensity();
~DCDensity();
DCDensity(const DCDensity&) = delete;
DCDensity& operator=(const DCDensity&) = delete;
DCDensity(DCDensity&&) noexcept;
DCDensity& operator=(DCDensity&&) noexcept;
// Configure (or reconfigure) all noise generators and Voronoi cells.
void configure(const DensityConfig& config);
// Evaluate density at world position (x, y, z).
// Returns positive for solid, negative for air.
// Uses pre-computed octave counts from configure() (base voxel_size).
float evaluate(float x, float y, float z) const;
// Evaluate with per-LOD Nyquist clamping.
// lod_voxel_size: actual voxel size at this LOD level (e.g., 8m for LOD4).
// Computes octave counts on-the-fly to avoid aliasing at coarse LODs.
float evaluate(float x, float y, float z, float lod_voxel_size) const;
// Access config for inspection (e.g., tests).
const DensityConfig& config() const { return config_; }
// GPU upload accessors
// Get permutation tables for all 6 noise generators (6 x 512 = 3072 ints).
// Order: primary, tunnel_a, tunnel_b, ridge, warp, detail.
std::vector<int> get_perm_tables() const;
// Get Voronoi cell data: returns {x, z, floor_height} triples.
const std::vector<VoronoiCell>& get_voronoi_cells() const { return voronoi_cells_; }
// Get pre-computed octave counts.
int get_primary_octaves() const { return primary_octaves_; }
int get_tunnel_octaves() const { return tunnel_octaves_; }
int get_ridge_octaves() const { return ridge_octaves_; }
int get_warp_octaves() const { return warp_octaves_; }
int get_detail_octaves() const { return detail_octaves_; }
private:
DensityConfig config_;
std::unique_ptr<DCDensityNoise> noise_;
// Pre-computed Nyquist-clamped octave counts (set in configure())
int primary_octaves_ = 3;
int tunnel_octaves_ = 2;
int ridge_octaves_ = 2;
int warp_octaves_ = 2;
int detail_octaves_ = 2;
std::vector<VoronoiCell> voronoi_cells_;
// Floating island density evaluation
float evaluate_floating_islands(float x, float y, float z, float lod_voxel_size) const;
// Spherical density evaluation (called from evaluate when spherical=true)
float evaluate_spherical(float x, float y, float z) const;
// Per-LOD spherical evaluation with Nyquist-correct octave counts
float evaluate_spherical_lod(float x, float y, float z, float lod_voxel_size) const;
// Internal flat terrain evaluation with explicit octave counts
float evaluate_internal(float x, float y, float z,
int p_oct, int t_oct, int r_oct, int w_oct, int d_oct) const;
};
#endif // DC_DENSITY_H