#ifndef DC_LEVEL_MESH_H
#define DC_LEVEL_MESH_H
// ---------------------------------------------------------------------------
// dc_level_mesh — csg4 faces -> renderable mesh + per-brush convex collision.
//
// docs/level-mesh-implementation.md §6, §10, §12 step 3.
//
// SHARP EDGES ARE STRUCTURAL, NOT A SETTING. Every face is triangulated with its
// OWN vertices carrying its OWN plane normal. Nothing is welded and nothing is
// smoothed, so a crease is sharp because the geometry says so — no QEF, no
// auto_smooth, no angle threshold. That is the whole point of the exact path:
// hard-surface-research-ledger.md's "fidelity" problem does not arise here, and
// Kobbelt et al. 2001's non-convergence result (reconstructed normals never
// converge to the true normal field) simply does not apply to geometry that IS
// the brush planes.
//
// RENDER vs COLLISION DIVERGE, deliberately (§10):
// render = the csg4-CLIPPED visible skin (hidden surface removed)
// collision = the UNCLIPPED convex brush hull
// Clipping removes hidden SURFACE, not VOLUME. A per-brush convex shape is both
// exact and cheaper than a trimesh.
//
// UVs ARE NOT COMPUTED HERE. Each vertex carries its face's `proj` index (1-based
// into the importer's projection table, 0 = none) plus `material_id`. The
// fragment shader already resolves Quake face projections from that table — the
// existing Stage-2 per-face texturing path. Computing UVs here would duplicate
// it and would drag the table into libdc, which must stay engine-free.
// ---------------------------------------------------------------------------
#include <cstdint>
#include <vector>
#include "dc_csg4.h"
struct DCLevelMeshData {
// Parallel per-vertex arrays. NOT welded — see the sharp-edge note above.
std::vector<float> positions; // xyz triples
std::vector<float> normals; // xyz triples (the face plane normal)
std::vector<uint32_t> material; // per vertex
std::vector<uint32_t> proj; // per vertex; 1-based, 0 = none
std::vector<uint32_t> brush_id; // per vertex; provenance for picking/editing
std::vector<int32_t> indices; // triangle list
CSG4Vec3 lo{0,0,0}, hi{0,0,0};
bool bounds_valid = false;
uint32_t vertex_count() const { return (uint32_t)(positions.size() / 3); }
uint32_t triangle_count() const { return (uint32_t)(indices.size() / 3); }
void clear();
};
// One convex collision shape per brush: the point set of its UNCLIPPED hull,
// ready for a ConvexPolygonShape3D.
struct DCConvexCollider {
uint32_t brush_id = 0;
std::vector<float> points; // xyz triples
uint32_t point_count() const { return (uint32_t)(points.size() / 3); }
};
namespace dc_level_mesh {
// Triangulate csg4 faces into a render mesh. Fan per winding; an n-gon costs
// n-2 triangles, matching the counts the csg4 gate asserts.
void build(const std::vector<CSG4Face>& faces, DCLevelMeshData& out);
// Per-brush convex hull points, from the UNCLIPPED face windings (§10).
void build_colliders(const std::vector<CSG4Brush>& brushes,
std::vector<DCConvexCollider>& out);
// Largest angular disagreement (radians) between a triangle's geometric normal
// (from its winding) and the face normal stored on its vertices. On exact brush
// geometry this must be ~0 — it is the numeric form of "sharp by construction",
// and it catches winding/orientation bugs that a triangle count cannot.
float max_normal_deviation(const DCLevelMeshData& m);
} // namespace dc_level_mesh
#endif // DC_LEVEL_MESH_H