OverviewFilesCommitsIssuesDocsReleasesPeers

dc-terrain / test / test_fandisk.cpp

122 lines · cpp ref:
// Test: Fandisk DC verification — checks that Dual Contouring preserves
// sharp features when reconstructing the fandisk benchmark model from its SDF.
//
// Build via CMake (see test/CMakeLists.txt) or manually:
//   cd dc-terrain
//   g++ -std=c++17 -O2 -fopenmp -I libdc/include \
//       test/test_fandisk.cpp libdc/src/*.cpp libdc/src/generators/*.cpp \
//       thirdparty/meshoptimizer/*.cpp -o test/test_fandisk -lm
//
// Run:
//   ./test/test_fandisk [path/to/fandisk.obj]

#include "dc_generator_mesh_sdf.h"
#include "dc_mesher.h"
#include <cstdio>
#include <cassert>
#include <fstream>
#include <string>
#include <algorithm>
#include <sys/stat.h>

static void write_obj(const DCMeshData& mesh, const std::string& path) {
    std::ofstream f(path);
    if (!f.is_open()) {
        fprintf(stderr, "Failed to open %s for writing\n", path.c_str());
        return;
    }
    for (size_t i = 0; i + 2 < mesh.positions.size(); i += 3) {
        f << "v " << mesh.positions[i] << " " << mesh.positions[i+1]
          << " " << mesh.positions[i+2] << "\n";
    }
    for (size_t i = 0; i + 2 < mesh.normals.size(); i += 3) {
        f << "vn " << mesh.normals[i] << " " << mesh.normals[i+1]
          << " " << mesh.normals[i+2] << "\n";
    }
    for (size_t i = 0; i + 2 < mesh.indices.size(); i += 3) {
        f << "f " << (mesh.indices[i]+1) << "//" << (mesh.indices[i]+1) << " "
          << (mesh.indices[i+1]+1) << "//" << (mesh.indices[i+1]+1) << " "
          << (mesh.indices[i+2]+1) << "//" << (mesh.indices[i+2]+1) << "\n";
    }
    printf("  Wrote %s\n", path.c_str());
}

static void ensure_dir(const char* path) {
    mkdir(path, 0755);
}

int main(int argc, char** argv) {
    const char* obj_path = "test/data/fandisk.obj";
    if (argc > 1) obj_path = argv[1];

    printf("=== Fandisk DC Verification Test ===\n");

    DCGeneratorMeshSDF gen;
    if (!gen.load_obj(obj_path)) {
        fprintf(stderr, "FAIL: Could not load %s\n", obj_path);
        return 1;
    }

    float bmin[3], bmax[3];
    gen.get_bounds(bmin, bmax);
    printf("Bounds: (%.4f, %.4f, %.4f) - (%.4f, %.4f, %.4f)\n",
           bmin[0], bmin[1], bmin[2], bmax[0], bmax[1], bmax[2]);

    // Add 10% padding around mesh bounds
    float pad = 0.0f;
    for (int i = 0; i < 3; i++)
        pad = std::max(pad, (bmax[i] - bmin[i]) * 0.1f);

    float origin[3] = { bmin[0] - pad, bmin[1] - pad, bmin[2] - pad };
    float extent[3] = { bmax[0] - bmin[0] + 2*pad,
                         bmax[1] - bmin[1] + 2*pad,
                         bmax[2] - bmin[2] + 2*pad };
    float max_extent = std::max({extent[0], extent[1], extent[2]});

    printf("Origin: (%.4f, %.4f, %.4f)\n", origin[0], origin[1], origin[2]);
    printf("Extent: %.4f (padded)\n", max_extent);

    ensure_dir("test/output");

    int prev_verts = 0;
    for (int res : {32, 64, 128}) {
        printf("\n--- Resolution %d ---\n", res);

        float voxel_size = max_extent / res;
        printf("  Voxel size: %.6f\n", voxel_size);

        DCMesherConfig config;
        config.auto_smooth = true;
        config.smooth_angle_deg = 30.0f;
        config.qef_bias = 0.01f;
        config.laplacian_smooth = false;
        config.clip_to_bounds = false;  // don't clip — we want the full mesh

        DCMeshData mesh = dc_mesh_from_generator(gen, origin, voxel_size, res, config);

        int verts = (int)(mesh.positions.size() / 3);
        int tris = (int)(mesh.indices.size() / 3);
        int norms = (int)(mesh.normals.size() / 3);
        printf("  Vertices: %d\n", verts);
        printf("  Normals:  %d\n", norms);
        printf("  Triangles: %d\n", tris);

        assert(verts > 0 && "DC must produce vertices from fandisk SDF");
        assert(tris > 0 && "DC must produce triangles from fandisk SDF");

        // Higher resolution should produce more geometry
        if (prev_verts > 0) {
            assert(verts > prev_verts && "Higher resolution should produce more vertices");
        }
        prev_verts = verts;

        // Write output OBJ for visual inspection
        char fname[256];
        snprintf(fname, sizeof(fname), "test/output/fandisk_dc_%d.obj", res);
        write_obj(mesh, fname);
    }

    printf("\n=== All fandisk tests PASSED ===\n");
    return 0;
}