OverviewFilesCommitsIssuesDocsReleasesPeers

dc-terrain / libdc / include / dc_generator_geiss.h

101 lines · c ref:
#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