// Standalone tests for DCDensity — no Godot dependencies.
// Build: g++ -std=c++17 -O2 -I src test/test_density.cpp src/dc_density.cpp -o test/test_density -lm
#include "dc_density.h"
#include "dc_noise.h"
#include <cmath>
#include <cstdio>
#include <cstdlib>
static int g_pass = 0;
static int g_fail = 0;
static void check(bool cond, const char* name, const char* detail = nullptr) {
if (cond) {
printf(" PASS %s\n", name);
g_pass++;
} else {
printf(" FAIL %s", name);
if (detail) printf(" (%s)", detail);
printf("\n");
g_fail++;
}
}
// -------------------------------------------------------------------
// Noise sanity checks
// -------------------------------------------------------------------
static void test_noise_basic() {
printf("\n--- SimplexNoise basic ---\n");
SimplexNoise n(42);
float v = n.noise3(1.5f, 2.7f, 3.3f);
check(v >= -1.0f && v <= 1.0f, "noise3 in [-1,1]");
// Deterministic: same seed + coords = same value
SimplexNoise n2(42);
float v2 = n2.noise3(1.5f, 2.7f, 3.3f);
check(v == v2, "noise3 deterministic");
// Different seed = different value (avoid integer coords where simplex = 0)
SimplexNoise n3(99);
float v3 = n3.noise3(1.5f, 2.7f, 3.3f);
check(v != v3, "different seed different value");
// fbm also in range
float f = n.fbm3(5.0f, 5.0f, 5.0f, 3);
check(f >= -1.5f && f <= 1.5f, "fbm3 in reasonable range");
}
// -------------------------------------------------------------------
// Ground plane test
// -------------------------------------------------------------------
static void test_ground_plane() {
printf("\n--- Ground plane ---\n");
DCDensity den;
DensityConfig cfg;
den.configure(cfg);
// Deep below ground should be solid (positive)
float below = den.evaluate(0.0f, -10.0f, 0.0f);
check(below > 0.0f, "evaluate(0,-10,0) > 0 (solid below ground)");
// High above should be air (negative)
float above = den.evaluate(0.0f, 50.0f, 0.0f);
check(above < 0.0f, "evaluate(0,50,0) < 0 (air above sky)");
}
// -------------------------------------------------------------------
// Zero noise: only ground bias remains
// -------------------------------------------------------------------
static void test_zero_noise() {
printf("\n--- Zero noise (ground bias only) ---\n");
DCDensity den;
DensityConfig cfg;
cfg.primary_strength = 0.0f;
cfg.tunnel_strength = 0.0f;
cfg.ridge_strength = 0.0f;
cfg.warp_strength = 0.0f;
cfg.detail_strength = 0.0f;
cfg.floor_strength = 0.0f;
cfg.solid_floor_depth = 1000.0f; // disable height clamping
cfg.sky_height = 1000.0f;
cfg.flat_center_radius = 0.0f; // disable flat spawn
den.configure(cfg);
float y_test = -5.0f;
float expected = -y_test * cfg.ground_bias; // = 5 * 0.04 = 0.2
float actual = den.evaluate(5.0f, y_test, 5.0f);
float err = std::fabs(actual - expected);
char buf[128];
snprintf(buf, sizeof(buf), "expected %.4f, got %.4f, err %.6f", expected, actual, err);
check(err < 0.001f, "zero noise = -y * ground_bias", buf);
}
// -------------------------------------------------------------------
// Height clamping
// -------------------------------------------------------------------
static void test_height_clamping() {
printf("\n--- Height clamping ---\n");
DCDensity den;
DensityConfig cfg;
den.configure(cfg);
// Well below solid_floor_depth (default 30) => very positive
float deep = den.evaluate(0.0f, -35.0f, 0.0f);
check(deep > 0.5f, "evaluate(0,-35,0) >> 0 (below solid floor)");
// Well above sky_height (default 25) => very negative
float high = den.evaluate(0.0f, 30.0f, 0.0f);
check(high < -0.5f, "evaluate(0,30,0) << 0 (above sky height)");
}
// -------------------------------------------------------------------
// Flat spawn area
// -------------------------------------------------------------------
static void test_flat_spawn() {
printf("\n--- Flat spawn area ---\n");
DCDensity den;
DensityConfig cfg;
den.configure(cfg);
// At origin, y=0, the flat spawn forces d toward -y*0.8 = 0.
float at_origin = den.evaluate(0.0f, 0.0f, 0.0f);
char buf[128];
snprintf(buf, sizeof(buf), "value = %.4f", at_origin);
check(std::fabs(at_origin) < 0.5f, "evaluate(0,0,0) ≈ 0 (surface at spawn)", buf);
// Slightly below origin should be positive (solid ground for spawn)
float below_spawn = den.evaluate(0.0f, -2.0f, 0.0f);
check(below_spawn > 0.0f, "evaluate(0,-2,0) > 0 (solid below spawn)");
// Slightly above origin should be negative (air above spawn)
float above_spawn = den.evaluate(0.0f, 2.0f, 0.0f);
check(above_spawn < 0.0f, "evaluate(0,2,0) < 0 (air above spawn)");
}
// -------------------------------------------------------------------
// Tunnel carving: verify tunnels produce negative density somewhere
// -------------------------------------------------------------------
static void test_tunnel_carving() {
printf("\n--- Tunnel carving ---\n");
// With strong tunnels and weak everything else, we should find
// negative values inside solid regions (carved tunnels).
DCDensity den;
DensityConfig cfg;
cfg.tunnel_strength = 3.0f; // extra strong tunnels
cfg.tunnel_radius = 0.4f; // wider tunnels
cfg.primary_strength = 0.5f; // weaker primary
cfg.ground_bias = 0.1f; // stronger ground to ensure baseline solid
cfg.flat_center_radius = 0.0f; // disable flat spawn
den.configure(cfg);
// Sample a grid at y=-5 (underground) looking for carved regions
int found_carved = 0;
int total = 0;
for (int xi = -20; xi <= 20; xi += 2) {
for (int zi = -20; zi <= 20; zi += 2) {
float v = den.evaluate(static_cast<float>(xi), -5.0f, static_cast<float>(zi));
total++;
if (v < 0.0f) found_carved++;
}
}
char buf[128];
snprintf(buf, sizeof(buf), "found %d carved of %d samples", found_carved, total);
check(found_carved > 0, "tunnels carve negative regions underground", buf);
}
// -------------------------------------------------------------------
// Different seeds produce different values
// -------------------------------------------------------------------
static void test_seed_change() {
printf("\n--- Seed change ---\n");
DCDensity den1, den2;
DensityConfig cfg1, cfg2;
cfg1.seed = 42;
cfg2.seed = 123;
den1.configure(cfg1);
den2.configure(cfg2);
float v1 = den1.evaluate(10.0f, -3.0f, 7.0f);
float v2 = den2.evaluate(10.0f, -3.0f, 7.0f);
char buf[128];
snprintf(buf, sizeof(buf), "seed42=%.4f, seed123=%.4f", v1, v2);
check(std::fabs(v1 - v2) > 0.001f, "different seeds produce different values", buf);
}
// -------------------------------------------------------------------
// Voronoi cells are generated
// -------------------------------------------------------------------
static void test_voronoi_effect() {
printf("\n--- Voronoi effect ---\n");
// With floor_strength > 0, Voronoi should modify density
DCDensity den_with, den_without;
DensityConfig cfg_with, cfg_without;
cfg_with.floor_strength = 0.7f;
cfg_without.floor_strength = 0.0f;
// Disable flat spawn to not mask the effect
cfg_with.flat_center_radius = 0.0f;
cfg_without.flat_center_radius = 0.0f;
den_with.configure(cfg_with);
den_without.configure(cfg_without);
// Sample at a point where Voronoi floor effect should be active
// (below some floor height inside a cell)
int differ = 0;
for (int xi = -20; xi <= 20; xi += 5) {
for (int zi = -20; zi <= 20; zi += 5) {
float v1 = den_with.evaluate(static_cast<float>(xi), -5.0f, static_cast<float>(zi));
float v2 = den_without.evaluate(static_cast<float>(xi), -5.0f, static_cast<float>(zi));
if (std::fabs(v1 - v2) > 0.01f) differ++;
}
}
char buf[128];
snprintf(buf, sizeof(buf), "%d samples differ", differ);
check(differ > 0, "Voronoi floor_strength changes density", buf);
}
// -------------------------------------------------------------------
// main
// -------------------------------------------------------------------
int main() {
printf("=== DCDensity Tests ===\n");
test_noise_basic();
test_ground_plane();
test_zero_noise();
test_height_clamping();
test_flat_spawn();
test_tunnel_carving();
test_seed_change();
test_voronoi_effect();
printf("\n=== Results: %d passed, %d failed ===\n", g_pass, g_fail);
return g_fail > 0 ? 1 : 0;
}