241 lines
5.8 KiB
Markdown
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.
|