2026-03-26 15:27:15 +01:00
|
|
|
#pragma once
|
|
|
|
|
#include "VoxelTypes.h"
|
|
|
|
|
#include <vector>
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
|
|
|
|
|
namespace voxel {
|
|
|
|
|
|
|
|
|
|
class VoxelWorld;
|
|
|
|
|
|
|
|
|
|
// ── Toping mesh vertex (position + normal) ──────────────────────
|
|
|
|
|
struct TopingVertex {
|
|
|
|
|
float px, py, pz; // position in voxel-local space [0,1]³
|
|
|
|
|
float nx, ny, nz; // normal
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ── Reference to a slice of vertices in the shared pool ─────────
|
|
|
|
|
struct MeshSlice {
|
|
|
|
|
uint32_t offset; // first vertex index in TopingSystem::vertices_
|
|
|
|
|
uint32_t count; // number of vertices (multiple of 3)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ── Toping definition (one per decorative type) ─────────────────
|
|
|
|
|
// Each def targets a specific material + face, and provides 16
|
|
|
|
|
// mesh variants indexed by a 4-bit adjacency bitmask.
|
|
|
|
|
//
|
|
|
|
|
// Adjacency bitmask (for FACE_POS_Y):
|
|
|
|
|
// bit 0 = +X neighbor has same material AND exposed +Y face
|
|
|
|
|
// bit 1 = -X neighbor has same material AND exposed +Y face
|
|
|
|
|
// bit 2 = +Z neighbor has same material AND exposed +Y face
|
|
|
|
|
// bit 3 = -Z neighbor has same material AND exposed +Y face
|
|
|
|
|
//
|
|
|
|
|
// A SET bit means the neighbor IS present → no bevel on that edge.
|
|
|
|
|
// An UNSET bit means the edge is "open" → bevel strip generated.
|
|
|
|
|
struct TopingDef {
|
|
|
|
|
uint8_t materialID; // voxel material that triggers this toping
|
|
|
|
|
uint8_t face; // Face enum (FACE_POS_Y, etc.)
|
2026-03-26 17:47:08 +01:00
|
|
|
uint8_t priority; // higher priority topings generate bevels over lower ones at boundaries
|
2026-03-26 15:27:15 +01:00
|
|
|
float height; // bevel peak height (voxel units)
|
|
|
|
|
float width; // bevel inward extent (voxel units)
|
|
|
|
|
int segments; // subdivisions per edge strip (1=smooth, 3+=bumpy)
|
|
|
|
|
MeshSlice variants[16]; // indexed by adjacency bitmask
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ── Placed toping instance ──────────────────────────────────────
|
|
|
|
|
struct TopingInstance {
|
|
|
|
|
float wx, wy, wz; // world position of the source voxel (integer coords)
|
|
|
|
|
uint16_t topingType; // index into TopingSystem::defs_
|
|
|
|
|
uint16_t variant; // adjacency bitmask (0-15)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ── Toping System ───────────────────────────────────────────────
|
|
|
|
|
// Manages toping definitions, procedural mesh generation, and
|
|
|
|
|
// instance collection from the voxel world.
|
|
|
|
|
//
|
|
|
|
|
// Phase 4.1: data structures + mesh gen + collection (no rendering).
|
|
|
|
|
// Phase 4.2 will upload vertices_ to a GPU vertex buffer and
|
|
|
|
|
// instances_ to an instance buffer for instanced drawing.
|
|
|
|
|
class TopingSystem {
|
|
|
|
|
public:
|
|
|
|
|
void initialize();
|
|
|
|
|
void collectInstances(const VoxelWorld& world);
|
|
|
|
|
|
|
|
|
|
// Accessors for Phase 4.2 GPU upload
|
|
|
|
|
const std::vector<TopingVertex>& getVertices() const { return vertices_; }
|
|
|
|
|
const std::vector<TopingInstance>& getInstances() const { return instances_; }
|
|
|
|
|
const std::vector<TopingDef>& getDefs() const { return defs_; }
|
|
|
|
|
|
|
|
|
|
size_t getDefCount() const { return defs_.size(); }
|
|
|
|
|
size_t getInstanceCount() const { return instances_.size(); }
|
|
|
|
|
size_t getVertexCount() const { return vertices_.size(); }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void registerDefs();
|
|
|
|
|
void generateMeshes();
|
|
|
|
|
void generateVariant(TopingDef& def, uint8_t bitmask);
|
|
|
|
|
|
|
|
|
|
std::vector<TopingDef> defs_;
|
|
|
|
|
std::vector<TopingVertex> vertices_; // shared vertex pool for all types/variants
|
|
|
|
|
std::vector<TopingInstance> instances_; // collected per frame/update
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace voxel
|