#ifndef DC_GENERATOR_GEISS_H
#define DC_GENERATOR_GEISS_H
// GPU Gems 3 ch.1 (Geiss) terrain as a dc-terrain generator.
//
// EXISTS TO BE COMPARED. The same density function is raymarched by
// hyper-tps/shaders/geiss_volume.gdshader and meshed here, so "is marching or
// meshing cheaper for this field" can be measured instead of argued. Both read
// the SAME generated body (shaders/generators/geiss_sdf.glsl, embedded as
// geiss_sdf_glsl.h) from the SAME source include, so the two cannot drift.
//
// The field SAMPLES noise rather than computing it: eight 16^3 RGBA volumes
// shipped with the demo. Those reach the shader through gpu_noise_volumes().
#include "dc_generator.h"
#include "geiss_sdf_glsl.h"
#include <cstdint>
#include <cstdio>
#include <string>
#include <vector>
class DCGeneratorGeiss : public DCGenerator {
public:
bool has_gpu_shader() const override { return volumes_ok_; }
const char* gpu_shader_source() const override { return GEISS_SDF_GLSL; }
const void* const* gpu_noise_volumes() const override {
return volumes_ok_ ? volume_ptrs_ : nullptr;
}
int gpu_noise_dim() const override { return dim_; }
uint32_t gpu_noise_version() const override { return version_; }
// dir must contain the demo's noise_half_16cubed_mips_0N.vol and
// packednoise_half_16cubed_mips_0N.vol. Returns false and leaves the
// generator GPU-disabled if any of the eight is missing, rather than
// meshing a field that silently differs from the raymarched one.
bool load_volumes(const std::string& dir) {
static const char* kNames[8] = {
"noise_half_16cubed_mips_00.vol", "noise_half_16cubed_mips_01.vol",
"noise_half_16cubed_mips_02.vol", "noise_half_16cubed_mips_03.vol",
"packednoise_half_16cubed_mips_00.vol", "packednoise_half_16cubed_mips_01.vol",
"packednoise_half_16cubed_mips_02.vol", "packednoise_half_16cubed_mips_03.vol",
};
volumes_ok_ = false;
for (int i = 0; i < 8; i++) {
if (!_load_one(dir + "/" + kNames[i], storage_[i])) {
fprintf(stderr, "DCGeneratorGeiss: cannot read %s\n", kNames[i]);
return false;
}
volume_ptrs_[i] = storage_[i].data();
}
volumes_ok_ = true;
version_++;
return true;
}
// CPU fallback -- deliberately a CHECKERBOARD, not terrain.
//
// The GPU path is the entire point of this generator; there is no CPU port
// of the field and a mesh built from this one is invalid data. So it must
// be impossible to mistake for the real thing. A flat plane, or anything
// smooth, reads as "terrain that came out wrong" and would quietly pollute
// an A/B run; 8-metre solid/air cubes read as "the GPU shader is not
// running", which is the same service dc-terrain's blue noise map performs
// when a custom shader fails to compile.
float evaluate(float x, float y, float z, float) override {
const float k = 8.0f;
const int cx = int(std::floor(x / k));
const int cy = int(std::floor(y / k));
const int cz = int(std::floor(z / k));
return (((cx ^ cy ^ cz) & 1) != 0) ? 1.0f : -1.0f;
}
private:
// 5-int header (w, h, d, bytes-per-texel, body-bytes), then RGBA half
// floats, mip 0 first. Only mip 0 is read: the density samples
// textureLod(..., 0) everywhere. Kept as HALF -- see gpu_noise_volumes().
static bool _load_one(const std::string& path, std::vector<uint16_t>& out) {
FILE* f = fopen(path.c_str(), "rb");
if (!f) return false;
int32_t hdr[5];
if (fread(hdr, sizeof(int32_t), 5, f) != 5) { fclose(f); return false; }
const int w = hdr[0], h = hdr[1], d = hdr[2];
if (w <= 0 || h <= 0 || d <= 0 || w != h || h != d) { fclose(f); return false; }
const size_t n = size_t(w) * h * d * 4;
out.resize(n);
const bool read_ok = fread(out.data(), sizeof(uint16_t), n, f) == n;
fclose(f);
return read_ok;
}
std::vector<uint16_t> storage_[8];
const void* volume_ptrs_[8] = {};
int dim_ = 16;
uint32_t version_ = 0;
bool volumes_ok_ = false;
};
#endif // DC_GENERATOR_GEISS_H