Add claude docs
This commit is contained in:
@@ -75,6 +75,37 @@ if(DUSK_TARGET_SYSTEM STREQUAL "psp")
|
|||||||
endif()
|
endif()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Embedding JS files (`dusk_embed_js`)
|
||||||
|
|
||||||
|
Source: `cmake/modules/duskjs2c.cmake`
|
||||||
|
|
||||||
|
The `dusk_embed_js()` CMake function embeds a `.js` source file as a
|
||||||
|
C string constant in a generated header. It is used to ship script
|
||||||
|
module code alongside the engine binary without a separate file load.
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
dusk_embed_js(
|
||||||
|
TARGET ${DUSK_LIBRARY_TARGET_NAME}
|
||||||
|
JS_FILE path/to/mymodule.js
|
||||||
|
# NAME is optional; defaults to uppercase stem + "_JS"
|
||||||
|
# e.g. "mymodule.js" -> "MYMODULE_JS"
|
||||||
|
NAME MY_CUSTOM_NAME
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
The generated header is placed in
|
||||||
|
`${DUSK_GENERATED_HEADERS_DIR}/<stem>_js.h` and defines:
|
||||||
|
|
||||||
|
```c
|
||||||
|
static const char MY_CUSTOM_NAME[] = "... js source ...";
|
||||||
|
static const size_t MY_CUSTOM_NAME_SIZE = sizeof(MY_CUSTOM_NAME) - 1;
|
||||||
|
```
|
||||||
|
|
||||||
|
Under the hood it calls `python -m tools.js2c` from the repo root.
|
||||||
|
The header is generated at build time; include it in the `.c` file
|
||||||
|
that registers the JS module, then pass `NAME` and `NAME_SIZE` to
|
||||||
|
`jerry_eval()` (or the equivalent module load helper).
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
- Test files live in `test/` mirroring the `src/dusk/` structure.
|
- Test files live in `test/` mirroring the `src/dusk/` structure.
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
# Console
|
||||||
|
|
||||||
|
Source: `src/dusk/console/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The console is a lightweight in-engine debug overlay. It maintains a
|
||||||
|
fixed-size ring buffer of text lines and can render them to the screen
|
||||||
|
as an overlay. On Linux (where `DUSK_CONSOLE_POSIX` is defined), it
|
||||||
|
also reads commands from stdin on a background thread, allowing
|
||||||
|
interactive input during development without pausing the game loop.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Limits
|
||||||
|
|
||||||
|
| Constant | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| `CONSOLE_LINE_MAX` | 512 chars per line |
|
||||||
|
| `CONSOLE_HISTORY_MAX` | 16 lines in the ring buffer |
|
||||||
|
| `CONSOLE_EXEC_BUFFER_MAX` | 32 pending execution slots |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Global state
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
char_t line[CONSOLE_HISTORY_MAX][CONSOLE_LINE_MAX]; // ring buffer
|
||||||
|
bool_t visible;
|
||||||
|
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
threadmutex_t printMutex; // guards ring buffer on POSIX targets
|
||||||
|
#endif
|
||||||
|
} console_t;
|
||||||
|
|
||||||
|
extern console_t CONSOLE;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```c
|
||||||
|
void consoleInit(void);
|
||||||
|
|
||||||
|
// printf-style print into the ring buffer.
|
||||||
|
// Thread-safe on POSIX (uses printMutex).
|
||||||
|
void consolePrint(const char_t *message, ...);
|
||||||
|
|
||||||
|
// Process any queued script input lines. Must be called from the
|
||||||
|
// main thread once per frame.
|
||||||
|
void consoleUpdate(void);
|
||||||
|
|
||||||
|
// Draw the ring buffer as an overlay in UI space.
|
||||||
|
errorret_t consoleDraw(void);
|
||||||
|
|
||||||
|
void consoleDispose(void);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## POSIX stdin mode (`DUSK_CONSOLE_POSIX`)
|
||||||
|
|
||||||
|
On Linux only (`DUSK_CONSOLE_POSIX`), the console launches a background
|
||||||
|
thread that polls stdin using `poll()` at 75 ms intervals
|
||||||
|
(`CONSOLE_POSIX_POLL_RATE`). Lines typed at the terminal are queued
|
||||||
|
and dispatched on the main thread by `consoleUpdate()`.
|
||||||
|
|
||||||
|
This allows typing commands or JS expressions into the running game
|
||||||
|
without blocking the render loop. The ring buffer is protected by
|
||||||
|
`CONSOLE.printMutex` so `consolePrint` is safe to call from either
|
||||||
|
thread.
|
||||||
|
|
||||||
|
POSIX mode is not available on PSP, Vita, or Dolphin targets.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- `CONSOLE.visible` controls whether `consoleDraw` renders anything.
|
||||||
|
Toggle it from a debug keybind or always set it to `true` in
|
||||||
|
development builds.
|
||||||
|
- `consolePrint` wraps lines at `CONSOLE_LINE_MAX` -- long messages are
|
||||||
|
truncated. Use multiple calls for long output.
|
||||||
|
- The console is distinct from the error system (`errorThrow` /
|
||||||
|
`errorPrint`). Use `consolePrint` for diagnostic output; use
|
||||||
|
`errorThrow` for recoverable failures.
|
||||||
|
- Log calls (`logDebug`, `logError`) go to the platform's debug output
|
||||||
|
(stdout/stderr), not to the console ring buffer.
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# Display -- Color
|
||||||
|
|
||||||
|
Source: `build/generated/display/color.h` (generated at build time)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
`color.h` is a generated header. It is not hand-edited -- the source
|
||||||
|
lives in the CMake generator that produces it from platform configuration.
|
||||||
|
Include it via `"display/color.h"`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Types
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef float_t colorchannelf_t; // float channel [0.0, 1.0]
|
||||||
|
typedef uint8_t colorchannel8_t; // 8-bit channel [0, 255]
|
||||||
|
|
||||||
|
typedef struct { colorchannelf_t r, g, b; } color3f_t;
|
||||||
|
typedef struct { colorchannelf_t r, g, b, a; } color4f_t;
|
||||||
|
typedef struct { colorchannel8_t r, g, b; } color3b_t;
|
||||||
|
typedef struct { colorchannel8_t r, g, b, a; } color4b_t;
|
||||||
|
|
||||||
|
typedef color4b_t color_t; // default: RGBA uint8
|
||||||
|
```
|
||||||
|
|
||||||
|
`color_t` is always `color4b_t` -- four `uint8_t` channels.
|
||||||
|
Most engine APIs (text rendering, UI, mesh vertices) take `color_t`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Constructors
|
||||||
|
|
||||||
|
```c
|
||||||
|
color3f(r, g, b) // float RGB
|
||||||
|
color4f(r, g, b, a) // float RGBA
|
||||||
|
color3b(r, g, b) // uint8 RGB
|
||||||
|
color4b(r, g, b, a) // uint8 RGBA
|
||||||
|
color(r, g, b, a) // alias for color4b
|
||||||
|
colorHex(0xRRGGBBAA) // unpack 32-bit hex into color4b
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Predefined constants
|
||||||
|
|
||||||
|
Each named colour comes in four variants: `_4B` (uint8 RGBA, default),
|
||||||
|
`_3B` (uint8 RGB), `_4F` (float RGBA), `_3F` (float RGB). The bare
|
||||||
|
name (e.g. `COLOR_BLACK`) always resolves to the `_4B` variant.
|
||||||
|
|
||||||
|
| Constant | RGBA (uint8) |
|
||||||
|
|----------|-------------|
|
||||||
|
| `COLOR_BLACK` | 0, 0, 0, 255 |
|
||||||
|
| `COLOR_WHITE` | 255, 255, 255, 255 |
|
||||||
|
| `COLOR_RED` | 255, 0, 0, 255 |
|
||||||
|
| `COLOR_GREEN` | 0, 255, 0, 255 |
|
||||||
|
| `COLOR_BLUE` | 0, 0, 255, 255 |
|
||||||
|
| `COLOR_YELLOW` | 255, 255, 0, 255 |
|
||||||
|
| `COLOR_CYAN` | 0, 255, 255, 255 |
|
||||||
|
| `COLOR_MAGENTA` | 255, 0, 255, 255 |
|
||||||
|
| `COLOR_ORANGE` | 255, 165, 0, 255 |
|
||||||
|
| `COLOR_PURPLE` | 127, 0, 127, 255 |
|
||||||
|
| `COLOR_GRAY` | 127, 127, 127, 255 |
|
||||||
|
| `COLOR_LIGHT_GRAY` | 191, 191, 191, 255 |
|
||||||
|
| `COLOR_DARK_GRAY` | 63, 63, 63, 255 |
|
||||||
|
| `COLOR_BROWN` | 153, 102, 51, 255 |
|
||||||
|
| `COLOR_PINK` | 255, 191, 204, 255 |
|
||||||
|
| `COLOR_LIME` | 191, 255, 0, 255 |
|
||||||
|
| `COLOR_NAVY` | 0, 0, 127, 255 |
|
||||||
|
| `COLOR_TEAL` | 0, 127, 127, 255 |
|
||||||
|
| `COLOR_CORNFLOWER_BLUE` | 99, 147, 237, 255 |
|
||||||
|
| `COLOR_TRANSPARENT` | 0, 0, 0, 0 |
|
||||||
|
| `COLOR_TRANSPARENT_WHITE` | 255, 255, 255, 0 |
|
||||||
|
| `COLOR_TRANSPARENT_BLACK` | 0, 0, 0, 0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- `color_t` uses premultiplied-friendly `uint8_t` channels for
|
||||||
|
compatibility with both OpenGL texture uploads and GX on Dolphin.
|
||||||
|
- For shader uniforms that expect float colours, convert manually:
|
||||||
|
`(float_t)col.r / 255.0f` etc.
|
||||||
|
- The `COLOR_SCRIPT` macro is a C string containing the JS Color class
|
||||||
|
static methods. It is concatenated into the embedded JS runtime
|
||||||
|
during module init (see `.claude/script.md`).
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
# 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.
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
# Display -- SpriteBatch
|
||||||
|
|
||||||
|
Source: `src/dusk/display/spritebatch/`
|
||||||
|
|
||||||
|
See also: `.claude/display-mesh.md`, `.claude/display-texture.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The SpriteBatch is the primary 2D rendering primitive. It accumulates
|
||||||
|
axis-aligned quads (sprites) into a shared vertex buffer and draws them
|
||||||
|
in batches. All 2D rendering in the engine -- UI frames, text, tilemaps,
|
||||||
|
HUD -- goes through the global `SPRITEBATCH`.
|
||||||
|
|
||||||
|
The batch flushes automatically when the per-flush limit is reached, or
|
||||||
|
explicitly via `spriteBatchFlush()`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Limits
|
||||||
|
|
||||||
|
| Constant | Value | Meaning |
|
||||||
|
|----------|-------|---------|
|
||||||
|
| `SPRITEBATCH_SPRITES_MAX` | 512 | Total sprites in the vertex buffer |
|
||||||
|
| `SPRITEBATCH_FLUSH_COUNT` | 16 | Number of auto-flush segments |
|
||||||
|
| `SPRITEBATCH_SPRITES_MAX_PER_FLUSH` | 32 | Sprites per auto-flush segment |
|
||||||
|
| `SPRITEBATCH_VERTEX_COUNT` | 3072 | Total vertices (512 * QUAD_VERTEX_COUNT) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sprite structure
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
vec3 min; // minimum XYZ corner of the quad in world/screen space
|
||||||
|
vec3 max; // maximum XYZ corner of the quad in world/screen space
|
||||||
|
vec2 uvMin; // minimum UV (top-left in [0,1] texture space)
|
||||||
|
vec2 uvMax; // maximum UV (bottom-right in [0,1] texture space)
|
||||||
|
} spritebatchsprite_t;
|
||||||
|
```
|
||||||
|
|
||||||
|
Z in `min` and `max` controls draw depth (further from camera = higher Z
|
||||||
|
in a typical orthographic setup). For flat 2D, set `min.z = max.z = 0`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SpriteBatch struct
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
mesh_t mesh;
|
||||||
|
int32_t spriteCount;
|
||||||
|
int32_t spriteFlush;
|
||||||
|
shader_t *shader;
|
||||||
|
shadermaterial_t material;
|
||||||
|
} spritebatch_t;
|
||||||
|
|
||||||
|
extern spritebatch_t SPRITEBATCH;
|
||||||
|
extern meshvertex_t SPRITEBATCH_VERTICES[SPRITEBATCH_VERTEX_COUNT];
|
||||||
|
```
|
||||||
|
|
||||||
|
`SPRITEBATCH_VERTICES` is a separate global (not embedded in the struct)
|
||||||
|
for platform alignment requirements.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```c
|
||||||
|
errorret_t spriteBatchInit();
|
||||||
|
errorret_t spriteBatchDispose();
|
||||||
|
|
||||||
|
// Clear the buffer and reset state. Call before starting a new batch.
|
||||||
|
void spriteBatchClear();
|
||||||
|
|
||||||
|
// Append sprites to the buffer. Flushes automatically when the per-flush
|
||||||
|
// segment fills. shader + material are used on the next flush.
|
||||||
|
errorret_t spriteBatchBuffer(
|
||||||
|
const spritebatchsprite_t *sprites,
|
||||||
|
const uint32_t count,
|
||||||
|
shader_t *shader,
|
||||||
|
const shadermaterial_t material
|
||||||
|
);
|
||||||
|
|
||||||
|
// Upload and draw all buffered sprites. Binds shader and applies
|
||||||
|
// material if set. No-op if the buffer is empty.
|
||||||
|
errorret_t spriteBatchFlush();
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Typical usage
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Beginning of a 2D render pass:
|
||||||
|
spriteBatchClear();
|
||||||
|
|
||||||
|
// Build sprites (e.g. via tilesetTileGetUV, then fill spritebatchsprite_t):
|
||||||
|
spritebatchsprite_t s;
|
||||||
|
glm_vec3_copy((vec3){ x, y, 0 }, s.min);
|
||||||
|
glm_vec3_copy((vec3){ x + w, y + h, 0 }, s.max);
|
||||||
|
glm_vec2_copy(uvMin, s.uvMin);
|
||||||
|
glm_vec2_copy(uvMax, s.uvMax);
|
||||||
|
|
||||||
|
shadermaterial_t mat = { .unlit = { .texture = myTexture } };
|
||||||
|
spriteBatchBuffer(&s, 1, myShader, mat);
|
||||||
|
|
||||||
|
// End of pass -- flush remaining sprites:
|
||||||
|
spriteBatchFlush();
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Relationship to other systems
|
||||||
|
|
||||||
|
- **Text rendering** (`textDraw`) internally calls `spriteBatchBuffer`
|
||||||
|
for each glyph and requires a final `spriteBatchFlush()` after drawing.
|
||||||
|
- **UI frames** (`uiFrameDraw`) push 9 quads to the batch without
|
||||||
|
flushing -- the caller or `uitextboxDraw` is responsible for the flush.
|
||||||
|
- **ECS renderables** of type `ENTITY_RENDERABLE_TYPE_SPRITEBATCH` are
|
||||||
|
drawn via the spritebatch in the scene render pipeline.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- `spriteBatchBuffer` changes the batch's `shader` and `material` fields.
|
||||||
|
If you mix different shaders or textures in one batch, add an explicit
|
||||||
|
`spriteBatchFlush()` call between groups to avoid draws with the wrong
|
||||||
|
material.
|
||||||
|
- The vertex buffer is a static global -- `SPRITEBATCH_VERTICES` must
|
||||||
|
not be written from multiple threads.
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
# Display -- Text Rendering
|
||||||
|
|
||||||
|
Source: `src/dusk/display/text/`
|
||||||
|
|
||||||
|
See also: `.claude/display-spritebatch.md`, `.claude/display-texture.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Text rendering is layered on top of the SpriteBatch. Each character
|
||||||
|
maps to a glyph tile in a bitmap font atlas; `textDraw` builds the
|
||||||
|
corresponding `spritebatchsprite_t` values and pushes them to the
|
||||||
|
global `SPRITEBATCH`. The caller is responsible for flushing the batch.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Font type (`font.h`)
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
texture_t *texture; // glyph atlas texture
|
||||||
|
tileset_t *tileset; // grid describing glyph size + UV layout
|
||||||
|
} font_t;
|
||||||
|
```
|
||||||
|
|
||||||
|
Both pointers are caller-owned. The text system does not allocate or
|
||||||
|
free them.
|
||||||
|
|
||||||
|
```c
|
||||||
|
extern font_t FONT_DEFAULT;
|
||||||
|
```
|
||||||
|
|
||||||
|
`FONT_DEFAULT` is the engine's built-in bitmap font. It is initialised
|
||||||
|
during `textInit()` and available for the engine lifetime.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Character range
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define TEXT_CHAR_START '!' // ASCII 33
|
||||||
|
```
|
||||||
|
|
||||||
|
The glyph atlas begins at `'!'` (ASCII 33). Characters below this value
|
||||||
|
-- space, control characters -- are handled specially:
|
||||||
|
|
||||||
|
- `' '` (space) advances the cursor by one tile width without drawing.
|
||||||
|
- Characters below `TEXT_CHAR_START` other than space are skipped.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API (`text.h`)
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Initialises the text system and FONT_DEFAULT.
|
||||||
|
errorret_t textInit(void);
|
||||||
|
|
||||||
|
// Disposes of the text system.
|
||||||
|
errorret_t textDispose(void);
|
||||||
|
|
||||||
|
// Draw a null-terminated string at (x, y) in screen/world space.
|
||||||
|
// Pushes sprites to SPRITEBATCH. Caller must call spriteBatchFlush()
|
||||||
|
// after all text has been drawn.
|
||||||
|
errorret_t textDraw(
|
||||||
|
const float_t x,
|
||||||
|
const float_t y,
|
||||||
|
const char_t *text,
|
||||||
|
const color_t color,
|
||||||
|
font_t *font
|
||||||
|
);
|
||||||
|
|
||||||
|
// Measure the bounding box of a string without drawing it.
|
||||||
|
void textMeasure(
|
||||||
|
const char_t *text,
|
||||||
|
const font_t *font,
|
||||||
|
int32_t *outWidth,
|
||||||
|
int32_t *outHeight
|
||||||
|
);
|
||||||
|
|
||||||
|
// Low-level: build a single glyph sprite at position pos.
|
||||||
|
// Returns a spritebatchsprite_t ready for spriteBatchBuffer.
|
||||||
|
spritebatchsprite_t textGetSprite(
|
||||||
|
const vec2 pos,
|
||||||
|
const char_t c,
|
||||||
|
const font_t *font
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Typical usage
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Inside a render callback:
|
||||||
|
errorChain(textDraw(10.0f, 10.0f, "Hello", COLOR_WHITE, &FONT_DEFAULT));
|
||||||
|
errorChain(spriteBatchFlush());
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are also drawing UI frames or other sprites in the same pass,
|
||||||
|
batch all the `textDraw` and `spriteBatchBuffer` calls first, then call
|
||||||
|
`spriteBatchFlush()` once at the end.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Text coordinates are in the same space as the scene render (screen
|
||||||
|
space for UI, or world space if placed in the scene).
|
||||||
|
- `textMeasure` returns pixel dimensions based on the font's
|
||||||
|
`tileset.tileWidth` and `tileset.tileHeight`. Use it to centre or
|
||||||
|
right-align text before drawing.
|
||||||
|
- For UI-attached text (dialogue, labels), prefer the `uitextbox_t`
|
||||||
|
system which handles word-wrap and paging automatically
|
||||||
|
(see `.claude/ui.md`).
|
||||||
@@ -17,3 +17,7 @@ Dolphin (GX) implementation in `src/duskdolphin/`.
|
|||||||
| Screen size modes, framebuffer, screen | `.claude/display-core.md` |
|
| Screen size modes, framebuffer, screen | `.claude/display-core.md` |
|
||||||
| Texture, tileset, font | `.claude/display-texture.md` |
|
| Texture, tileset, font | `.claude/display-texture.md` |
|
||||||
| Shader, shader material, display state | `.claude/display-shader.md` |
|
| Shader, shader material, display state | `.claude/display-shader.md` |
|
||||||
|
| Mesh, vertex format, primitive generators | `.claude/display-mesh.md` |
|
||||||
|
| SpriteBatch (2D quad renderer) | `.claude/display-spritebatch.md` |
|
||||||
|
| Text rendering, font, FONT_DEFAULT | `.claude/display-text.md` |
|
||||||
|
| Color types, macros, named constants | `.claude/display-color.md` |
|
||||||
|
|||||||
+226
@@ -0,0 +1,226 @@
|
|||||||
|
# UI System
|
||||||
|
|
||||||
|
Source: `src/dusk/ui/`
|
||||||
|
|
||||||
|
See also: `.claude/display-spritebatch.md`, `.claude/display-text.md`,
|
||||||
|
`.claude/display-color.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The UI system renders overlaid interface elements on top of the scene
|
||||||
|
each frame. It is called by the engine after the scene render pipeline
|
||||||
|
and before the screen unbind -- game code does not drive it directly.
|
||||||
|
|
||||||
|
All UI elements render through the global SpriteBatch.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lifecycle
|
||||||
|
|
||||||
|
```c
|
||||||
|
extern ui_t UI;
|
||||||
|
|
||||||
|
errorret_t uiInit(void);
|
||||||
|
void uiUpdate(void);
|
||||||
|
errorret_t uiRender(void);
|
||||||
|
void uiDispose(void);
|
||||||
|
```
|
||||||
|
|
||||||
|
`uiUpdate` is called each game tick; `uiRender` is called each render
|
||||||
|
tick. The engine calls both automatically -- do not call them from
|
||||||
|
game scripts.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Element registration (`uielement.h`)
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
uielementtype_t type; // UI_ELEMENT_TYPE_NULL or UI_ELEMENT_TYPE_NATIVE
|
||||||
|
errorret_t (*draw)(); // draw callback
|
||||||
|
} uielement_t;
|
||||||
|
|
||||||
|
extern uielement_t UI_ELEMENTS[]; // registered element array
|
||||||
|
```
|
||||||
|
|
||||||
|
`uiRender` iterates `UI_ELEMENTS` and calls each element's `draw`
|
||||||
|
callback. Elements register themselves during `uiInit`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Elements
|
||||||
|
|
||||||
|
### uiframe_t -- 9-slice border (`uiframe.h`)
|
||||||
|
|
||||||
|
A resizable bordered panel rendered using 9-slice (9-patch) technique
|
||||||
|
from a 3x3 tileset.
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
tileset_t tileset;
|
||||||
|
texture_t *texture;
|
||||||
|
} uiframe_t;
|
||||||
|
|
||||||
|
errorret_t uiFrameInit(uiframe_t *frame);
|
||||||
|
|
||||||
|
errorret_t uiFrameDraw(
|
||||||
|
const uiframe_t *frame,
|
||||||
|
const float_t x,
|
||||||
|
const float_t y,
|
||||||
|
const float_t width,
|
||||||
|
const float_t height
|
||||||
|
);
|
||||||
|
|
||||||
|
void uiFrameDispose(uiframe_t *frame); // does not dispose texture
|
||||||
|
```
|
||||||
|
|
||||||
|
`uiFrameDraw` pushes 9 quads (corners + edges + centre) to the
|
||||||
|
SpriteBatch without flushing. The caller must call `spriteBatchFlush()`
|
||||||
|
after all sprites for a draw pass are buffered.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### uitextbox_t -- typewriter dialogue box (`uitextbox.h`)
|
||||||
|
|
||||||
|
A global single-instance dialogue box that displays text one character
|
||||||
|
at a time with word-wrap and multi-page support.
|
||||||
|
|
||||||
|
```c
|
||||||
|
extern uitextbox_t UI_TEXTBOX;
|
||||||
|
|
||||||
|
errorret_t uiTextboxInit(void);
|
||||||
|
void uiTextboxDispose(void);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Setting text:**
|
||||||
|
|
||||||
|
```c
|
||||||
|
uiTextboxSetText("Hello, world!");
|
||||||
|
// Automatically word-wraps and paginates.
|
||||||
|
// Resets currentPage and scroll to 0.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tick update (called from script `update` or uiUpdate):**
|
||||||
|
|
||||||
|
```c
|
||||||
|
uiTextboxUpdate();
|
||||||
|
// Advances typewriter by UI_TEXTBOX_SCROLL_CHARS_PER_TICK (1) each
|
||||||
|
// fixed tick. Skipped on dynamic sub-ticks.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Page control:**
|
||||||
|
|
||||||
|
```c
|
||||||
|
bool_t uiTextboxPageIsComplete(void); // scroll revealed full page
|
||||||
|
bool_t uiTextboxHasNextPage(void); // more pages remain
|
||||||
|
void uiTextboxNextPage(void); // advance; no-op on last page
|
||||||
|
int32_t uiTextboxGetPageCharCount(void);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Events:**
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Fires when the current page is fully scrolled into view:
|
||||||
|
UI_TEXTBOX.onPageComplete
|
||||||
|
|
||||||
|
// Fires when the last page has been fully scrolled:
|
||||||
|
UI_TEXTBOX.onLastPage
|
||||||
|
```
|
||||||
|
|
||||||
|
Subscribe via the event system (see `.claude/events.md`).
|
||||||
|
|
||||||
|
**Limits:**
|
||||||
|
|
||||||
|
| Constant | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| `UI_TEXTBOX_TEXT_MAX` | 1024 chars |
|
||||||
|
| `UI_TEXTBOX_LINES_MAX` | 64 lines |
|
||||||
|
| `UI_TEXTBOX_LINES_PER_PAGE_MAX` | 3 lines per page |
|
||||||
|
| `UI_TEXTBOX_SCROLL_CHARS_PER_TICK` | 1 char per tick |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### uifullbox_t -- full-screen colour overlay (`uifullbox.h`)
|
||||||
|
|
||||||
|
An animated solid-colour overlay that covers the entire screen. Used
|
||||||
|
for fade-to-black, scene transitions, and flash effects.
|
||||||
|
|
||||||
|
Two global instances are provided:
|
||||||
|
|
||||||
|
```c
|
||||||
|
extern uifullbox_t UI_FULLBOX_UNDER; // draws below scene content
|
||||||
|
extern uifullbox_t UI_FULLBOX_OVER; // draws above all content
|
||||||
|
```
|
||||||
|
|
||||||
|
```c
|
||||||
|
void uiFullboxInit(uifullbox_t *fullbox);
|
||||||
|
|
||||||
|
// Start a colour-to-colour transition:
|
||||||
|
void uiFullboxTransition(
|
||||||
|
uifullbox_t *fullbox,
|
||||||
|
color_t from,
|
||||||
|
color_t to,
|
||||||
|
float_t duration,
|
||||||
|
easingtype_t easing
|
||||||
|
);
|
||||||
|
|
||||||
|
void uiFullboxUpdate(uifullbox_t *fullbox, float_t delta);
|
||||||
|
errorret_t uiFullboxDraw(uifullbox_t *fullbox);
|
||||||
|
// Draw is skipped entirely when alpha == 0.
|
||||||
|
```
|
||||||
|
|
||||||
|
`fullbox.onTransitionEnd` event fires once when the transition
|
||||||
|
completes. Subscribe to it to chain scene transitions:
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Typical fade-out before a scene change:
|
||||||
|
uiFullboxTransition(
|
||||||
|
&UI_FULLBOX_OVER,
|
||||||
|
COLOR_TRANSPARENT, COLOR_BLACK,
|
||||||
|
0.5f, EASING_OUT_QUAD
|
||||||
|
);
|
||||||
|
eventSubscribe(&UI_FULLBOX_OVER.onTransitionEnd, onFadeComplete, NULL);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### uiloading_t -- loading indicator (`uiloading.h`)
|
||||||
|
|
||||||
|
An animated loading indicator with fade-in / fade-out transitions.
|
||||||
|
Shown while asset batches are loading.
|
||||||
|
|
||||||
|
```c
|
||||||
|
extern uiloading_t UI_LOADING;
|
||||||
|
|
||||||
|
void uiLoadingInit(void);
|
||||||
|
void uiLoadingUpdate(float_t delta);
|
||||||
|
errorret_t uiLoadingDraw(void);
|
||||||
|
|
||||||
|
// Fade in; callback fires when fully visible:
|
||||||
|
void uiLoadingShow(eventcallback_t callback, void *user);
|
||||||
|
|
||||||
|
// Fade out; callback fires when fully hidden:
|
||||||
|
void uiLoadingHide(eventcallback_t callback, void *user);
|
||||||
|
```
|
||||||
|
|
||||||
|
`UI_LOADING_FADE_DURATION` is 0.5 seconds. `UI_LOADING_MARGIN` is 8px.
|
||||||
|
`uiLoadingDraw` is a no-op when the indicator is fully transparent.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### uifps_t -- FPS counter (`uifps.h`)
|
||||||
|
|
||||||
|
Debug FPS display. Measures real elapsed time between ticks and draws
|
||||||
|
the average frame rate as text in the corner of the screen.
|
||||||
|
|
||||||
|
```c
|
||||||
|
extern uifps_t UIFPS;
|
||||||
|
|
||||||
|
errorret_t uiFPSDraw(); // also performs the measurement update
|
||||||
|
```
|
||||||
|
|
||||||
|
Intended for debug builds only. Draw it explicitly from a JS `render`
|
||||||
|
hook or from a UI element -- it is not registered in `UI_ELEMENTS` by
|
||||||
|
default.
|
||||||
@@ -65,6 +65,12 @@ Detailed documentation on specific topics lives in `.claude/`:
|
|||||||
| Display -- screen, framebuffer, size modes | `.claude/display-core.md` |
|
| Display -- screen, framebuffer, size modes | `.claude/display-core.md` |
|
||||||
| Display -- texture, tileset, font | `.claude/display-texture.md` |
|
| Display -- texture, tileset, font | `.claude/display-texture.md` |
|
||||||
| Display -- shader, material, display state | `.claude/display-shader.md` |
|
| Display -- shader, material, display state | `.claude/display-shader.md` |
|
||||||
|
| Display -- mesh, vertex format, primitives | `.claude/display-mesh.md` |
|
||||||
|
| Display -- SpriteBatch (2D quad renderer) | `.claude/display-spritebatch.md` |
|
||||||
|
| Display -- text rendering, FONT_DEFAULT | `.claude/display-text.md` |
|
||||||
|
| Display -- color types, macros, constants | `.claude/display-color.md` |
|
||||||
|
| UI system (frame, textbox, fullbox, loading, FPS) | `.claude/ui.md` |
|
||||||
|
| Console (debug overlay, POSIX stdin mode) | `.claude/console.md` |
|
||||||
| Scene system (lifecycle, render pipeline, JS hooks) | `.claude/scene.md` |
|
| Scene system (lifecycle, render pipeline, JS hooks) | `.claude/scene.md` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
Reference in New Issue
Block a user