# Display -- Mesh Source: `src/dusk/display/mesh/` See also: `.claude/display-spritebatch.md`, `.claude/display-shader.md` --- ## Overview The mesh system wraps platform-specific GPU geometry buffers behind a common `mesh_t` type. Geometry is described as an array of `meshvertex_t` values and a primitive type. The platform layer (`meshplatform.h`) provides the concrete buffer and draw implementation (VBO on OpenGL, display list or immediate-mode on Dolphin GX). --- ## Vertex format (`meshvertex.h`) ```c typedef struct { #if MESH_ENABLE_COLOR color_t color; // optional per-vertex colour (disabled by default) #endif float_t uv[2]; // texture coordinates (U, V) float_t pos[3]; // position (X, Y, Z) } meshvertex_t; ``` `MESH_ENABLE_COLOR` is a compile-time flag (default 0). Enable it with `-DMESH_ENABLE_COLOR=1` at CMake configure time if per-vertex colouring is needed; be aware this changes the struct size and breaks binary compatibility with pre-built mesh data. --- ## Core API (`mesh.h`) ```c // Platform alias -- do not use meshplatform_t directly. typedef meshplatform_t mesh_t; typedef meshprimitivetypeplatform_t meshprimitivetype_t; // Upload vertices to the GPU. Must be called from the main thread. errorret_t meshInit( mesh_t *mesh, const meshprimitivetype_t primitiveType, const int32_t vertexCount, const meshvertex_t *vertices ); // Flush a range of updated vertices to the GPU (modern targets only). // vertexCount == -1 flushes all vertices. errorret_t meshFlush( mesh_t *mesh, const int32_t vertexOffset, const int32_t vertexCount ); // Draw the mesh. vertexCount == -1 draws all vertices. errorret_t meshDraw( const mesh_t *mesh, const int32_t vertexOffset, const int32_t vertexCount ); // Compute the axis-aligned bounding box. void meshGetBounds(const mesh_t *mesh, vec3 outMin, vec3 outMax); int32_t meshGetVertexCount(const mesh_t *mesh); errorret_t meshDispose(mesh_t *mesh); ``` On constrained targets (GameCube/Wii) `meshFlush` is a no-op -- the hardware reads vertices from main memory directly. Always call it on desktop/mobile targets after modifying vertex data. --- ## Primitive generators Each generator provides: - A `*Buffer(vertices, ...)` function that writes into a caller-supplied `meshvertex_t` array (no allocation). - A global pre-built `*_MESH_SIMPLE` singleton + `*_MESH_SIMPLE_VERTICES` array initialised at engine startup (for common one-off uses). All generators use CCW winding and `MESH_PRIMITIVE_TYPE_TRIANGLES`. ### Quad (`quad.h`) ```c #define QUAD_VERTEX_COUNT 6 // two triangles // 2D quad in XY plane: void quadBuffer( meshvertex_t *vertices, const float_t minX, const float_t minY, const float_t maxX, const float_t maxY, const float_t u0, const float_t v0, const float_t u1, const float_t v1 ); // 3D quad using full vec3 min/max: void quadBuffer3D( meshvertex_t *vertices, const vec3 min, const vec3 max, const vec2 uvMin, const vec2 uvMax ); extern mesh_t QUAD_MESH_SIMPLE; ``` The SpriteBatch is built on `quadBuffer3D` internally. ### Cube (`cube.h`) ```c #define CUBE_VERTEX_COUNT 36 // 6 faces x 6 vertices // Axis-aligned box from min to max: void cubeBuffer( meshvertex_t *vertices, const vec3 min, const vec3 max ); extern mesh_t CUBE_MESH_SIMPLE; // unit cube (0,0,0) to (1,1,1) ``` ### Plane (`plane.h`) ```c #define PLANE_VERTEX_COUNT 6 typedef enum { PLANE_AXIS_XY, // flat in XY, normal along +Z (billboard / wall face) PLANE_AXIS_XZ, // flat in XZ, normal along +Y (ground / floor) PLANE_AXIS_YZ, // flat in YZ, normal along +X (side wall) } planeaxis_t; void planeBuffer( meshvertex_t *vertices, const planeaxis_t axis, const vec3 min, const vec3 max, const vec2 uvMin, const vec2 uvMax ); extern mesh_t PLANE_MESH_SIMPLE; // unit XZ plane (0,0,0) to (1,0,1) ``` ### Sphere (`sphere.h`) ```c #define SPHERE_STACKS 8 #define SPHERE_SECTORS 16 #define SPHERE_VERTEX_COUNT (SPHERE_STACKS * SPHERE_SECTORS * 6) void sphereBuffer( meshvertex_t *vertices, const vec3 center, const float_t radius, const int32_t stacks, const int32_t sectors ); extern mesh_t SPHERE_MESH_SIMPLE; // unit sphere centered at (0,0,0), r=0.5 ``` ### Capsule (`capsule.h`) ```c #define CAPSULE_CAP_RINGS 4 #define CAPSULE_SECTORS 16 // Total vertex count = (2 * capRings + 1) * sectors * 6 void capsuleBuffer( meshvertex_t *vertices, const vec3 center, const float_t radius, const float_t halfHeight, // half-height of the cylindrical section only const int32_t capRings, const int32_t sectors ); extern mesh_t CAPSULE_MESH_SIMPLE; // r=0.5, halfHeight=0.5 (total h=2.0) ``` The long axis is always Y. This mirrors the physics capsule body (see `.claude/physics.md`). ### Triangular prism (`triprism.h`) ```c #define TRIPRISM_VERTEX_COUNT 24 // Cross-section triangle defined by three 2D points in XY; // extruded along Z from minZ to maxZ. void triPrismBuffer( meshvertex_t *vertices, const float_t x0, const float_t y0, const float_t x1, const float_t y1, const float_t x2, const float_t y2, const float_t minZ, const float_t maxZ ); extern mesh_t TRIPRISM_MESH_SIMPLE; // Unit prism: triangle (0,0),(1,0),(0.5,1) extruded z=0 to z=1. ``` --- ## Custom dynamic mesh If you need to update geometry each frame (e.g. a procedural mesh): ```c static meshvertex_t myVerts[MY_VERT_COUNT]; static mesh_t myMesh; // On init: // Fill myVerts, then: errorChain(meshInit(&myMesh, MESH_PRIMITIVE_TYPE_TRIANGLES, MY_VERT_COUNT, myVerts)); // Each frame (after modifying myVerts): errorChain(meshFlush(&myMesh, 0, -1)); errorChain(meshDraw(&myMesh, 0, -1)); ``` --- ## Notes - `meshInit` must be called on the **main thread** (GPU upload). - `meshFlush` is required on OpenGL targets when vertices change after init. It is a no-op on Dolphin. - All `_MESH_SIMPLE` globals are initialised during engine startup -- do not call `meshInit` on them manually.