OverviewFilesCommitsIssuesDocsReleasesPeers

dc-terrain / libdc / include / dc_seam.h

138 lines · c ref:
#ifndef DC_SEAM_H
#define DC_SEAM_H

// Gildea seam stitching for octree clipmap DC terrain.
// Builds temporary octrees from boundary nodes of adjacent cells and
// contours them to fill gaps between LOD levels.
//
// Ported from ngildea/leven (clipmap.cpp + octree.cpp).
// Pure C++ -- no Godot dependencies.

#include "dc_octree_clipmap.h"
#include "dc_contour.h"  // DCMeshData
#include <vector>
#include <cstdint>

// ---------- SeamOctreeNode ----------
// Lightweight node for the temporary seam octree.
// Allocated from a SeamOctreeArena and discarded after mesh generation.

struct SeamOctreeNode {
    enum Type : uint8_t { NONE, INTERNAL, LEAF };

    Type type = NONE;
    int min[3] = {};
    int size = 0;
    SeamOctreeNode* children[8] = {};

    // Leaf data (only valid when type == LEAF)
    int vertex_index = -1;
    float position[3] = {};
    float normal[3] = {};
    uint32_t corner_signs = 0;  // 8-bit corner sign field
};

// ---------- SeamOctreeArena ----------
// Simple arena allocator for seam octree nodes.
// All nodes are freed together when the arena is cleared.

class SeamOctreeArena {
    static constexpr int BLOCK_SIZE = 256;

    struct Block {
        SeamOctreeNode nodes[BLOCK_SIZE];
    };

    std::vector<Block*> blocks_;
    int next_ = BLOCK_SIZE;

public:
    SeamOctreeArena() = default;
    ~SeamOctreeArena() { clear(); }

    SeamOctreeArena(const SeamOctreeArena&) = delete;
    SeamOctreeArena& operator=(const SeamOctreeArena&) = delete;

    SeamOctreeNode* alloc() {
        if (next_ >= BLOCK_SIZE) {
            blocks_.push_back(new Block());
            next_ = 0;
        }
        SeamOctreeNode* n = &blocks_.back()->nodes[next_++];
        *n = SeamOctreeNode{};
        return n;
    }

    void clear() {
        for (auto* b : blocks_) delete b;
        blocks_.clear();
        next_ = BLOCK_SIZE;
    }
};

// ---------- Seam Stitching API ----------

// Filter: does a seam node at [node_min, node_max) belong on the boundary
// between a host cell and its neighbor at octant child_index?
// seam_bounds = host_min + host_size (the shared face/edge/corner).
bool seam_filter_node(int child_index,
                      const int seam_bounds[3],
                      const int node_min[3],
                      const int node_max[3]);

// Select seam nodes from a neighbor that belong in the host cell's seam mesh.
// host_min: world-space min of the host cell
// host_size: host cell size in voxel units
// neighbor_size: neighbor cell size in voxel units
// neighbor_index: which of 8 octant directions the neighbor is in
// seam_nodes / num_seam_nodes: the neighbor's boundary node array
// voxels_per_cell: grid resolution (e.g. 32)
// out: appended with pointers to selected SeamOctreeNodes (caller-allocated)
void seam_select_nodes(
    const int host_min[3],
    int host_size,
    int neighbor_size,
    int neighbor_index,
    const SeamNodeInfo* seam_nodes,
    int num_seam_nodes,
    int voxels_per_cell,
    SeamOctreeArena& arena,
    std::vector<SeamOctreeNode*>& out);

// Build a temporary octree bottom-up from leaf nodes of potentially
// different sizes. Returns the root node (owned by arena).
SeamOctreeNode* seam_construct_upwards(
    SeamOctreeArena& arena,
    const std::vector<SeamOctreeNode*>& input_nodes,
    const int root_min[3],
    int root_size);

// Generate a seam mesh from a temporary seam octree root.
// Uses the standard DC cellProc/faceProc/edgeProc algorithm.
// host_min: used to filter triangles that span multiple cells.
void seam_generate_mesh(
    SeamOctreeNode* root,
    const int host_min[3],
    int host_size,
    DCMeshData& out_mesh);

// High-level: generate a complete seam mesh for a cell by collecting
// seam nodes from its neighbors and contouring them.
// neighbors[8]: pointers to the 8 adjacent cells (null if no neighbor).
//   neighbors[i] is the cell at host_min + CHILD_MIN_OFFSETS[i] * host_size.
// Each neighbor may itself have sub-cells (different LOD) — pass active leaves.
struct SeamNeighborInfo {
    const SeamNodeInfo* seam_nodes = nullptr;
    int num_seam_nodes = 0;
    int cell_size = 0;  // in voxel units
};

void seam_generate_for_cell(
    const int host_min[3],
    int host_size,
    int voxels_per_cell,
    const std::vector<SeamNeighborInfo>& neighbors,  // indexed by octant [0..7]
    DCMeshData& out_mesh);

#endif // DC_SEAM_H