OverviewFilesCommitsIssuesDocsReleasesPeers

dc-terrain / libdc / include / dc_contour.h

117 lines · c ref:
#ifndef DC_CONTOUR_H
#define DC_CONTOUR_H

// Dual Contouring mesh generation: hermite data collection, QEF solving,
// and recursive contouring (cellProc/faceProc/edgeProc).
// Pure C++ -- no Godot dependencies.

#include "dc_octree.h"
#include "dc_density_grid.h"
#include <vector>

struct DCMeshData {
    std::vector<float> positions;  // x,y,z triples
    std::vector<float> normals;    // x,y,z triples
    std::vector<float> materials;  // per-vertex material_id (route ② texturing; empty on CPU path)
    // Per-vertex horizontal Laplacian of density, in metres — the third
    // material attribute (altitude, slope, curvature). Computed in qef_solve
    // from the density grid it already reads; empty on the CPU path.
    std::vector<float> curv;
    std::vector<float> proj;       // per-vertex projection index, candidate 0 (Stage-2; empty on CPU path)
    std::vector<float> proj2;      // per-vertex projection index, candidate 1 (crease other face; Stage-2)
    std::vector<int> indices;      // triangle indices (visual: includes +1 boundary extension)
    std::vector<int> collision_indices;  // collision: strict nominal bounds (no overlap)
    std::vector<float> positions_coarse;  // x,y,z triples (parent-LOD positions)
    std::vector<float> normals_coarse;    // x,y,z triples (parent-LOD normals)
};

// Post-process mesh normals: compute face normals from geometry, then split
// vertices where adjacent face normals exceed the angle threshold.
// This reveals DC's sharp feature preservation in the rendered mesh.
// angle_threshold_degrees: faces with angle > this get split normals (default 30°).
void dc_auto_smooth(DCMeshData& mesh, float angle_threshold_degrees = 60.0f);

// Laplacian mesh smoothing: moves vertices toward the average of their
// neighbors, reducing noise and spiky geometry. Constrained to preserve
// sharp features (edges where normal angle > threshold are not smoothed across).
// iterations: number of smoothing passes (1-5 typical)
// lambda: smoothing factor per step (0.0-1.0, default 0.5)
// angle_threshold_degrees: edges sharper than this are preserved
void dc_laplacian_smooth(DCMeshData& mesh, int iterations = 2,
                         float lambda = 0.5f,
                         float angle_threshold_degrees = 60.0f);

// Remove degenerate triangles with extreme aspect ratio.
// max_aspect_ratio: triangles with longest_edge/shortest_altitude > this are removed.
void dc_remove_degenerate_tris(DCMeshData& mesh, float max_aspect_ratio = 15.0f);

// Remove small disconnected mesh islands (floating fragments).
// Finds connected components via triangle adjacency, removes any
// component with fewer than min_triangles. Cleans up SDF artifacts.
void dc_remove_islands(DCMeshData& mesh, int min_triangles = 8);

// Full pipeline: collect hermite data, solve QEFs, contour tree -> mesh.
// density_fn and userdata are needed for hermite data (zero-crossing search
// + normal computation via central differences).
void dc_generate_mesh(
    OctreeNode* root,
    float voxel_scale,
    float origin[3],
    DensityFunc density_fn,
    void* userdata,
    DCMeshData& out_mesh
);

// Grid-accelerated version: uses pre-computed density grid for
// bisection and normal computation instead of calling density_fn.
// Much faster (~10x) since trilinear interpolation replaces noise evaluation.
struct TerrainEdit;  // forward declaration

void dc_generate_mesh_grid(
    OctreeNode* root,
    float voxel_scale,
    float origin[3],
    const DensityGrid& grid,
    int overlap,
    DCMeshData& out_mesh,
    const std::vector<TerrainEdit>* edits = nullptr
);

// SDF-guided edge subdivision: subdivide edges on curved surfaces
// and project new vertices onto the actual SDF surface.
// Flat edges are left untouched. This smooths sphere/cylinder CSG
// without adding geometry to flat box faces.
//   angle_threshold_degrees: edges with normal angle > this get subdivided
//   iterations: number of subdivision passes (1 = 2× on curves, 2 = 4×)
void dc_subdivide_curved(DCMeshData& mesh,
                         DensityFunc density_fn, void* userdata,
                         float voxel_scale,
                         float angle_threshold_degrees = 15.0f,
                         int iterations = 1);

// Convex decomposition: group adjacent coplanar triangles by normal similarity.
// Coplanar groups emit a box (AABB extended into solid along normal).
// Non-coplanar groups emit a convex hull point cloud.
struct ConvexHull {
    enum Type { HULL, BOX };
    Type type = HULL;
    std::vector<float> points;    // HULL: xyz triples (point cloud)
    float box_min[3] = {};        // BOX: AABB min
    float box_max[3] = {};        // BOX: AABB max
};

struct ConvexHullSet {
    std::vector<ConvexHull> hulls;
};

void dc_convex_decompose(const DCMeshData& mesh, ConvexHullSet& out,
                         float cos_threshold = 0.7071f);

// Clip mesh to an AABB: discard faces where ALL 3 vertices are outside
// the bounding box. This is the Cepero seam fix — each chunk generates
// overlap geometry but only keeps faces within its nominal bounds.
void dc_clip_to_aabb(DCMeshData& mesh,
                     const float aabb_min[3], const float aabb_max[3]);

#endif // DC_CONTOUR_H