Files
dusk/.claude/display-mesh.md
T
2026-06-16 12:29:36 -05:00

241 lines
5.8 KiB
Markdown

# 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.