diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/procedural/generator.cc | 119 | ||||
| -rw-r--r-- | src/procedural/generator.h | 20 | ||||
| -rw-r--r-- | src/tests/3d/test_3d_render.cc | 37 | ||||
| -rw-r--r-- | src/tests/util/test_procedural.cc | 88 | ||||
| -rw-r--r-- | src/util/asset_manager.cc | 4 |
5 files changed, 257 insertions, 11 deletions
diff --git a/src/procedural/generator.cc b/src/procedural/generator.cc index 7c165db..a2a383b 100644 --- a/src/procedural/generator.cc +++ b/src/procedural/generator.cc @@ -4,6 +4,7 @@ #include "procedural/generator.h" #include <cmath> #include <cstdlib> +#include <vector> namespace procedural { @@ -94,9 +95,6 @@ bool gen_perlin(uint8_t* buffer, int w, int h, const float* params, // Params[1]: Frequency (Scale) bool gen_noise(uint8_t* buffer, int w, int h, const float* params, int num_params) { - if (num_params > 0 && params[0] == -1337.0f) - return false; - const float seed = (num_params > 0) ? params[0] : 0.0f; const float freq = (num_params > 1) ? params[1] : 4.0f; @@ -191,16 +189,123 @@ bool make_periodic(uint8_t* buffer, int w, int h, const float* params, return true; } -#if !defined(DEMO_STRIP_ALL) -// Test-only: 256x256 noise generator -bool gen_noise_256(uint8_t* buffer, int w, int h, const float* params, +// Plasma: classic sine-sum color texture +// Params[0]: time/seed, Params[1]: frequency multiplier +bool gen_plasma(uint8_t* buffer, int w, int h, const float* params, + int num_params) { + const float time = (num_params > 0) ? params[0] : 0.0f; + const float freq = (num_params > 1) ? params[1] : 2.0f; + + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + const float nx = (float)x / (float)w * freq; + const float ny = (float)y / (float)h * freq; + + float v = sinf(nx * 6.2832f + time); + v += sinf(ny * 6.2832f + time * 1.3f); + v += sinf((nx + ny) * 3.1416f + time * 0.7f); + v += sinf(sqrtf(nx * nx + ny * ny) * 6.2832f + time); + v = v * 0.125f + 0.5f; + + // Map value to RGB via 120-degree phase-shifted sines + const float kTau = 6.2832f; + const float kPhase = 2.0944f; // 2*pi/3 + const int idx = (y * w + x) * 4; + buffer[idx + 0] = (uint8_t)(fminf(fmaxf(sinf(v * kTau) * 0.5f + 0.5f, 0.0f), 1.0f) * 255.0f); + buffer[idx + 1] = (uint8_t)(fminf(fmaxf(sinf(v * kTau + kPhase) * 0.5f + 0.5f, 0.0f), 1.0f) * 255.0f); + buffer[idx + 2] = (uint8_t)(fminf(fmaxf(sinf(v * kTau + 2.0f * kPhase) * 0.5f + 0.5f, 0.0f), 1.0f) * 255.0f); + buffer[idx + 3] = 255; + } + } + return true; +} + +// Voronoi (Worley) cellular noise +// Params[0]: cell scale, Params[1]: mode (0=F1, 1=F2, 2=F2-F1), Params[2]: seed +bool gen_voronoi(uint8_t* buffer, int w, int h, const float* params, + int num_params) { + const float scale = (num_params > 0) ? params[0] : 4.0f; + const int mode = (num_params > 1) ? (int)params[1] : 2; + const float seed = (num_params > 2) ? params[2] : 0.0f; + + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + const float px = (float)x / (float)w * scale; + const float py = (float)y / (float)h * scale; + const float cell_x = floorf(px); + const float cell_y = floorf(py); + + float f1 = 1e10f, f2 = 1e10f; + + for (int dy = -1; dy <= 1; ++dy) { + for (int dx = -1; dx <= 1; ++dx) { + const float cx = cell_x + dx; + const float cy = cell_y + dy; + const float fx = cx + hash_2f(cx + seed, cy + seed * 1.37f); + const float fy = cy + hash_2f(cx + seed * 1.13f, cy + seed * 0.97f); + const float ddx = px - fx; + const float ddy = py - fy; + const float dist2 = ddx * ddx + ddy * ddy; + + if (dist2 < f1) { f2 = f1; f1 = dist2; } + else if (dist2 < f2) { f2 = dist2; } + } + } + + float value; + if (mode == 0) value = sqrtf(f1) * 1.5f; + else if (mode == 1) value = sqrtf(f2) * 1.2f; + else value = (sqrtf(f2) - sqrtf(f1)) * 3.0f; + + value = fminf(fmaxf(value, 0.0f), 1.0f); + const uint8_t uval = (uint8_t)(value * 255.0f); + const int idx = (y * w + x) * 4; + buffer[idx + 0] = uval; + buffer[idx + 1] = uval; + buffer[idx + 2] = uval; + buffer[idx + 3] = 255; + } + } + return true; +} + +// Normal map: post-process converting grayscale R channel to RGB normals +// Params[0]: strength +bool gen_normalmap(uint8_t* buffer, int w, int h, const float* params, int num_params) { - return gen_noise(buffer, w, h, params, num_params); + const float strength = (num_params > 0) ? params[0] : 4.0f; + + std::vector<float> height(w * h); + for (int i = 0; i < w * h; ++i) { + height[i] = buffer[i * 4] / 255.0f; + } + + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + const float hL = height[y * w + (x - 1 + w) % w]; + const float hR = height[y * w + (x + 1) % w]; + const float hD = height[((y - 1 + h) % h) * w + x]; + const float hU = height[((y + 1) % h) * w + x]; + + const float dx = (hR - hL) * strength; + const float dy = (hU - hD) * strength; + const float len = sqrtf(dx * dx + dy * dy + 1.0f); + + const int idx = (y * w + x) * 4; + buffer[idx + 0] = (uint8_t)((-dx / len * 0.5f + 0.5f) * 255.0f); + buffer[idx + 1] = (uint8_t)((-dy / len * 0.5f + 0.5f) * 255.0f); + buffer[idx + 2] = (uint8_t)((1.0f / len * 0.5f + 0.5f) * 255.0f); + buffer[idx + 3] = 255; + } + } + return true; } +#if !defined(DEMO_STRIP_ALL) // Test-only: Failing generator bool gen_fail(uint8_t* buffer, int w, int h, const float* params, int num_params) { + (void)buffer; (void)w; (void)h; (void)params; (void)num_params; return false; } #endif diff --git a/src/procedural/generator.h b/src/procedural/generator.h index 96d7651..9224d00 100644 --- a/src/procedural/generator.h +++ b/src/procedural/generator.h @@ -17,6 +17,7 @@ typedef bool (*ProcGenFunc)(uint8_t* buffer, int w, int h, const float* params, namespace procedural { // Simple noise generator +// Params[0]: Seed, Params[1]: Frequency bool gen_noise(uint8_t* buffer, int w, int h, const float* params, int num_params); @@ -38,11 +39,24 @@ bool gen_grid(uint8_t* buffer, int w, int h, const float* params, bool make_periodic(uint8_t* buffer, int w, int h, const float* params, int num_params); -#if !defined(DEMO_STRIP_ALL) -// Test-only: 256x256 noise generator -bool gen_noise_256(uint8_t* buffer, int w, int h, const float* params, +// Plasma: classic sine-sum color texture +// Params[0]: time/seed offset, Params[1]: frequency multiplier (default 2.0) +bool gen_plasma(uint8_t* buffer, int w, int h, const float* params, + int num_params); + +// Voronoi (Worley) cellular noise +// Params[0]: cell scale (default 4.0) +// Params[1]: mode: 0=F1, 1=F2, 2=F2-F1 borders (default 2) +// Params[2]: seed +bool gen_voronoi(uint8_t* buffer, int w, int h, const float* params, + int num_params); + +// Post-process: convert grayscale height (R channel) to RGB normal map +// Params[0]: strength (default 4.0) +bool gen_normalmap(uint8_t* buffer, int w, int h, const float* params, int num_params); +#if !defined(DEMO_STRIP_ALL) // Test-only: Failing generator bool gen_fail(uint8_t* buffer, int w, int h, const float* params, int num_params); diff --git a/src/tests/3d/test_3d_render.cc b/src/tests/3d/test_3d_render.cc index bd301bd..de9718e 100644 --- a/src/tests/3d/test_3d_render.cc +++ b/src/tests/3d/test_3d_render.cc @@ -102,6 +102,17 @@ bool gen_periodic_noise(uint8_t* buffer, int w, int h, const float* params, return procedural::make_periodic(buffer, w, h, p_params, 1); } +// Wrapper: fBm noise → normal map +bool gen_fbm_normalmap(uint8_t* buffer, int w, int h, const float* params, + int num_params) { + (void)params; (void)num_params; + float fbm_params[] = {0.0f, 4.0f, 1.0f, 0.5f, 5.0f}; + if (!procedural::gen_perlin(buffer, w, h, fbm_params, 5)) + return false; + float nm_params[] = {8.0f}; + return procedural::gen_normalmap(buffer, w, h, nm_params, 1); +} + int main(int argc, char** argv) { printf("Running 3D Renderer Test...\n"); @@ -134,6 +145,32 @@ int main(int argc, char** argv) { setup_standard_textures(g_renderer, g_textures, g_device, g_queue); + // Plasma → sky texture + ProceduralTextureDef plasma_def; + plasma_def.width = 256; + plasma_def.height = 256; + plasma_def.gen_func = procedural::gen_plasma; + plasma_def.params = {0.0f, 3.0f}; // time=0, freq=3 + g_textures.create_procedural_texture("plasma", plasma_def); + g_renderer.set_sky_texture(g_textures.get_texture_view("plasma")); + + // Voronoi (F2-F1 borders) → noise texture + ProceduralTextureDef voronoi_def; + voronoi_def.width = 256; + voronoi_def.height = 256; + voronoi_def.gen_func = procedural::gen_voronoi; + voronoi_def.params = {8.0f, 2.0f, 42.0f}; // scale=8, mode=F2-F1, seed=42 + g_textures.create_procedural_texture("voronoi", voronoi_def); + g_renderer.set_noise_texture(g_textures.get_texture_view("voronoi")); + + // fBm normal map (stored for material/debug use) + ProceduralTextureDef normalmap_def; + normalmap_def.width = 256; + normalmap_def.height = 256; + normalmap_def.gen_func = gen_fbm_normalmap; + normalmap_def.params = {}; + g_textures.create_procedural_texture("normalmap", normalmap_def); + // GPU Grid texture (additional texture for this test) GpuProceduralParams grid_params = {}; grid_params.width = 256; diff --git a/src/tests/util/test_procedural.cc b/src/tests/util/test_procedural.cc index e9f9a02..c6616ad 100644 --- a/src/tests/util/test_procedural.cc +++ b/src/tests/util/test_procedural.cc @@ -127,11 +127,99 @@ void test_periodic() { assert(res); // Should return true but do nothing } +void test_plasma() { + std::cout << "Testing Plasma Generator..." << std::endl; + int w = 64, h = 64; + std::vector<uint8_t> buffer(w * h * 4); + + float params[] = {0.0f, 2.0f}; + bool res = procedural::gen_plasma(buffer.data(), w, h, params, 2); + assert(res); + assert(buffer[3] == 255); + + // RGB should differ (color output, not grayscale) + bool has_color = false; + for (size_t i = 0; i < buffer.size(); i += 4) { + if (buffer[i] != buffer[i + 1] || buffer[i + 1] != buffer[i + 2]) { + has_color = true; + break; + } + } + assert(has_color); + + // Test defaults + res = procedural::gen_plasma(buffer.data(), w, h, nullptr, 0); + assert(res); +} + +void test_voronoi() { + std::cout << "Testing Voronoi Generator..." << std::endl; + int w = 64, h = 64; + std::vector<uint8_t> buffer(w * h * 4); + + // F1 mode + float params_f1[] = {4.0f, 0.0f, 0.0f}; + bool res = procedural::gen_voronoi(buffer.data(), w, h, params_f1, 3); + assert(res); + assert(buffer[3] == 255); + + bool nonzero = false; + for (size_t i = 0; i < buffer.size(); i += 4) { + if (buffer[i] > 0) { nonzero = true; break; } + } + assert(nonzero); + + // F2-F1 (borders) mode + float params_border[] = {4.0f, 2.0f, 0.0f}; + res = procedural::gen_voronoi(buffer.data(), w, h, params_border, 3); + assert(res); + + // Test defaults + res = procedural::gen_voronoi(buffer.data(), w, h, nullptr, 0); + assert(res); +} + +void test_normalmap() { + std::cout << "Testing Normal Map Generator..." << std::endl; + int w = 64, h = 64; + std::vector<uint8_t> buffer(w * h * 4); + + // Fill with a horizontal gradient as height + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + int idx = (y * w + x) * 4; + buffer[idx] = (uint8_t)(x * 255 / (w - 1)); + buffer[idx + 1] = buffer[idx]; + buffer[idx + 2] = buffer[idx]; + buffer[idx + 3] = 255; + } + } + + float params[] = {4.0f}; + bool res = procedural::gen_normalmap(buffer.data(), w, h, params, 1); + assert(res); + assert(buffer[3] == 255); + + // For a horizontal gradient, X-normal (R) should be shifted from 128 + // (non-flat normals expected) + bool has_normal_variation = false; + for (size_t i = 0; i < buffer.size(); i += 4) { + if (buffer[i] != 128) { has_normal_variation = true; break; } + } + assert(has_normal_variation); + + // Z component (B) should be > 0 (normal faces up) + assert(buffer[2] > 0); +} + int main() { test_noise(); test_perlin(); test_grid(); test_periodic(); + test_plasma(); + test_voronoi(); + test_normalmap(); std::cout << "--- PROCEDURAL TESTS PASSED ---" << std::endl; return 0; }
\ No newline at end of file diff --git a/src/util/asset_manager.cc b/src/util/asset_manager.cc index 0b96cb2..26a82bf 100644 --- a/src/util/asset_manager.cc +++ b/src/util/asset_manager.cc @@ -28,8 +28,10 @@ static const ProcGenEntry kAssetManagerProcGenFuncs[] = { {"gen_perlin", procedural::gen_perlin}, {"gen_grid", procedural::gen_grid}, {"make_periodic", procedural::make_periodic}, + {"gen_plasma", procedural::gen_plasma}, + {"gen_voronoi", procedural::gen_voronoi}, + {"gen_normalmap", procedural::gen_normalmap}, #if !defined(STRIP_ALL) - {"gen_noise_256", procedural::gen_noise_256}, {"gen_fail", procedural::gen_fail}, #endif }; |
