This commit is contained in:
2026-06-18 20:25:54 -05:00
parent 730a5b2b10
commit 57b2cdb9d1
111 changed files with 865 additions and 3328 deletions
@@ -0,0 +1,16 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
mesh.c
quad.c
cube.c
sphere.c
plane.c
capsule.c
triprism.c
)
@@ -0,0 +1,192 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "capsule.h"
#include "assert/assert.h"
mesh_t CAPSULE_MESH_SIMPLE;
meshvertex_t CAPSULE_MESH_SIMPLE_VERTICES[CAPSULE_VERTEX_COUNT];
errorret_t capsuleInit() {
vec3 center = { 0.0f, 0.0f, 0.0f };
capsuleBuffer(
CAPSULE_MESH_SIMPLE_VERTICES,
center,
0.5f,
0.5f,
CAPSULE_CAP_RINGS,
CAPSULE_SECTORS
#if MESH_ENABLE_COLOR
, COLOR_WHITE_4B
#endif
);
errorChain(meshInit(
&CAPSULE_MESH_SIMPLE,
CAPSULE_PRIMITIVE_TYPE,
CAPSULE_VERTEX_COUNT,
CAPSULE_MESH_SIMPLE_VERTICES
));
errorOk();
}
void capsuleBuffer(
meshvertex_t *vertices,
const vec3 center,
const float_t radius,
const float_t halfHeight,
const int32_t capRings,
const int32_t sectors
#if MESH_ENABLE_COLOR
, const color_t color
#endif
) {
assertNotNull(vertices, "Vertices cannot be NULL");
assertNotNull(center, "Center vector cannot be NULL");
const float_t cx = center[0];
const float_t cy = center[1];
const float_t cz = center[2];
const float_t sectorStep = 2.0f * (float_t)GLM_PI / (float_t)sectors;
int32_t vi = 0;
/* Helper macro: write one vertex. */
#if MESH_ENABLE_COLOR
#define CAP_VERT(px, py, pz, u, v) \
vertices[vi].color = color; \
vertices[vi].pos[0] = (px); \
vertices[vi].pos[1] = (py); \
vertices[vi].pos[2] = (pz); \
vertices[vi].uv[0] = (u); \
vertices[vi].uv[1] = (v); \
vi++;
#else
#define CAP_VERT(px, py, pz, u, v) \
vertices[vi].pos[0] = (px); \
vertices[vi].pos[1] = (py); \
vertices[vi].pos[2] = (pz); \
vertices[vi].uv[0] = (u); \
vertices[vi].uv[1] = (v); \
vi++;
#endif
/* ---- Top hemisphere ---- */
/* phi ranges from PI/2 (top pole) down to 0 (equator). */
const float_t capStep = (float_t)GLM_PI_2 / (float_t)capRings;
for(int32_t i = 0; i < capRings; i++) {
const float_t phi1 = (float_t)GLM_PI_2 - (float_t)i * capStep;
const float_t phi2 = (float_t)GLM_PI_2 - (float_t)(i + 1) * capStep;
const float_t ly1 = radius * sinf(phi1);
const float_t ly2 = radius * sinf(phi2);
const float_t lxz1 = radius * cosf(phi1);
const float_t lxz2 = radius * cosf(phi2);
/* UV: top cap occupies v in [0.5 + halfHeightFrac .. 1.0]: we use a
* simple per-band normalisation against the full height. */
const float_t v1 = 1.0f - (float_t)i / (float_t)(2 * capRings + 1);
const float_t v2 = 1.0f - (float_t)(i + 1) / (float_t)(2 * capRings + 1);
for(int32_t j = 0; j < sectors; j++) {
const float_t t1 = (float_t)j * sectorStep;
const float_t t2 = (float_t)(j + 1) * sectorStep;
const float_t u1 = (float_t)j / (float_t)sectors;
const float_t u2 = (float_t)(j + 1) / (float_t)sectors;
const float_t x11 = lxz1 * cosf(t1), z11 = lxz1 * sinf(t1);
const float_t x12 = lxz1 * cosf(t2), z12 = lxz1 * sinf(t2);
const float_t x21 = lxz2 * cosf(t1), z21 = lxz2 * sinf(t1);
const float_t x22 = lxz2 * cosf(t2), z22 = lxz2 * sinf(t2);
const float_t y1off = cy + halfHeight + ly1;
const float_t y2off = cy + halfHeight + ly2;
CAP_VERT(cx+x11, y1off, cz+z11, u1, v1)
CAP_VERT(cx+x21, y2off, cz+z21, u1, v2)
CAP_VERT(cx+x12, y1off, cz+z12, u2, v1)
CAP_VERT(cx+x12, y1off, cz+z12, u2, v1)
CAP_VERT(cx+x21, y2off, cz+z21, u1, v2)
CAP_VERT(cx+x22, y2off, cz+z22, u2, v2)
}
}
/* ---- Cylindrical body ---- */
{
const float_t yTop = cy + halfHeight;
const float_t yBot = cy - halfHeight;
const float_t vTop = (
1.0f - (float_t)capRings / (float_t)(2 * capRings + 1)
);
const float_t vBot = (
1.0f - (float_t)(capRings + 1) / (float_t)(2 * capRings + 1)
);
for(int32_t j = 0; j < sectors; j++) {
const float_t t1 = (float_t)j * sectorStep;
const float_t t2 = (float_t)(j + 1) * sectorStep;
const float_t u1 = (float_t)j / (float_t)sectors;
const float_t u2 = (float_t)(j + 1) / (float_t)sectors;
const float_t x1 = radius * cosf(t1), z1 = radius * sinf(t1);
const float_t x2 = radius * cosf(t2), z2 = radius * sinf(t2);
CAP_VERT(cx+x1, yTop, cz+z1, u1, vTop)
CAP_VERT(cx+x1, yBot, cz+z1, u1, vBot)
CAP_VERT(cx+x2, yTop, cz+z2, u2, vTop)
CAP_VERT(cx+x2, yTop, cz+z2, u2, vTop)
CAP_VERT(cx+x1, yBot, cz+z1, u1, vBot)
CAP_VERT(cx+x2, yBot, cz+z2, u2, vBot)
}
}
// Bottom hemisphere
for(int32_t i = 0; i < capRings; i++) {
const float_t phi1 = -(float_t)i * capStep;
const float_t phi2 = -(float_t)(i + 1) * capStep;
const float_t ly1 = radius * sinf(phi1);
const float_t ly2 = radius * sinf(phi2);
const float_t lxz1 = radius * cosf(phi1);
const float_t lxz2 = radius * cosf(phi2);
const float_t v1 = (
1.0f - (float_t)(capRings + 1 + i) / (float_t)(2 * capRings + 1)
);
const float_t v2 = (
1.0f - (float_t)(capRings + 1 + i + 1) / (float_t)(2 * capRings + 1)
);
for(int32_t j = 0; j < sectors; j++) {
const float_t t1 = (float_t)j * sectorStep;
const float_t t2 = (float_t)(j + 1) * sectorStep;
const float_t u1 = (float_t)j / (float_t)sectors;
const float_t u2 = (float_t)(j + 1) / (float_t)sectors;
const float_t x11 = lxz1 * cosf(t1), z11 = lxz1 * sinf(t1);
const float_t x12 = lxz1 * cosf(t2), z12 = lxz1 * sinf(t2);
const float_t x21 = lxz2 * cosf(t1), z21 = lxz2 * sinf(t1);
const float_t x22 = lxz2 * cosf(t2), z22 = lxz2 * sinf(t2);
const float_t y1off = cy - halfHeight + ly1;
const float_t y2off = cy - halfHeight + ly2;
CAP_VERT(cx+x11, y1off, cz+z11, u1, v1)
CAP_VERT(cx+x21, y2off, cz+z21, u1, v2)
CAP_VERT(cx+x12, y1off, cz+z12, u2, v1)
CAP_VERT(cx+x12, y1off, cz+z12, u2, v1)
CAP_VERT(cx+x21, y2off, cz+z21, u1, v2)
CAP_VERT(cx+x22, y2off, cz+z22, u2, v2)
}
}
#undef CAP_VERT
}
@@ -0,0 +1,52 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/mesh/mesh.h"
#include "display/color.h"
#define CAPSULE_CAP_RINGS 4
#define CAPSULE_SECTORS 16
#define CAPSULE_VERTEX_COUNT ((2 * CAPSULE_CAP_RINGS + 1) * CAPSULE_SECTORS * 6)
#define CAPSULE_PRIMITIVE_TYPE MESH_PRIMITIVE_TYPE_TRIANGLES
extern mesh_t CAPSULE_MESH_SIMPLE;
extern meshvertex_t CAPSULE_MESH_SIMPLE_VERTICES[CAPSULE_VERTEX_COUNT];
/**
* Initializes the simple unit capsule mesh centered at (0,0,0) with radius 0.5
* and a cylindrical half-height of 0.5 (total height 2.0).
*
* @return Error for initialization of the capsule mesh.
*/
errorret_t capsuleInit();
/**
* Buffers a capsule (cylinder + two hemisphere caps) into the provided vertex
* array. The capsule's long axis is Y. Total vertex count is
* (2*capRings + 1) * sectors * 6.
*
* @param vertices Vertex array to write into.
* @param center Center position of the capsule.
* @param radius Radius of the cylinder and hemisphere caps.
* @param halfHeight Half the height of the cylindrical section only (caps
* extend an additional radius above/below).
* @param capRings Number of latitude rings per hemisphere cap.
* @param sectors Number of longitude segments around the circumference.
* @param color Color applied to all vertices.
*/
void capsuleBuffer(
meshvertex_t *vertices,
const vec3 center,
const float_t radius,
const float_t halfHeight,
const int32_t capRings,
const int32_t sectors
#if MESH_ENABLE_COLOR
, const color_t color
#endif
);
@@ -0,0 +1,114 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "cube.h"
#include "assert/assert.h"
mesh_t CUBE_MESH_SIMPLE;
meshvertex_t CUBE_MESH_SIMPLE_VERTICES[CUBE_VERTEX_COUNT];
errorret_t cubeInit() {
vec3 min = { 0.0f, 0.0f, 0.0f };
vec3 max = { 1.0f, 1.0f, 1.0f };
cubeBuffer(
CUBE_MESH_SIMPLE_VERTICES, min, max
#if MESH_ENABLE_COLOR
, COLOR_WHITE_4B
#endif
);
errorChain(meshInit(
&CUBE_MESH_SIMPLE,
CUBE_PRIMITIVE_TYPE,
CUBE_VERTEX_COUNT,
CUBE_MESH_SIMPLE_VERTICES
));
errorOk();
}
// Helper macro: set one vertex position, UV and color.
#if MESH_ENABLE_COLOR
#define CUBE_VERT(i, px, py, pz, u, v) \
vertices[i].color = color; \
vertices[i].pos[0] = (px); \
vertices[i].pos[1] = (py); \
vertices[i].pos[2] = (pz); \
vertices[i].uv[0] = (u); \
vertices[i].uv[1] = (v);
#else
#define CUBE_VERT(i, px, py, pz, u, v) \
vertices[i].pos[0] = (px); \
vertices[i].pos[1] = (py); \
vertices[i].pos[2] = (pz); \
vertices[i].uv[0] = (u); \
vertices[i].uv[1] = (v);
#endif
void cubeBuffer(
meshvertex_t *vertices,
const vec3 min,
const vec3 max
#if MESH_ENABLE_COLOR
, const color_t color
#endif
) {
assertNotNull(vertices, "Vertices cannot be NULL");
assertNotNull(min, "Min vector cannot be NULL");
assertNotNull(max, "Max vector cannot be NULL");
const float_t x0 = min[0], y0 = min[1], z0 = min[2];
const float_t x1 = max[0], y1 = max[1], z1 = max[2];
// Front face (+Z normal): CCW when viewed from +Z
CUBE_VERT( 0, x0, y0, z1, 0.0f, 0.0f);
CUBE_VERT( 1, x1, y0, z1, 1.0f, 0.0f);
CUBE_VERT( 2, x1, y1, z1, 1.0f, 1.0f);
CUBE_VERT( 3, x0, y0, z1, 0.0f, 0.0f);
CUBE_VERT( 4, x1, y1, z1, 1.0f, 1.0f);
CUBE_VERT( 5, x0, y1, z1, 0.0f, 1.0f);
// Back face (-Z normal): CCW when viewed from -Z
CUBE_VERT( 6, x1, y0, z0, 0.0f, 0.0f);
CUBE_VERT( 7, x0, y0, z0, 1.0f, 0.0f);
CUBE_VERT( 8, x0, y1, z0, 1.0f, 1.0f);
CUBE_VERT( 9, x1, y0, z0, 0.0f, 0.0f);
CUBE_VERT(10, x0, y1, z0, 1.0f, 1.0f);
CUBE_VERT(11, x1, y1, z0, 0.0f, 1.0f);
// Right face (+X normal): CCW when viewed from +X
CUBE_VERT(12, x1, y0, z1, 0.0f, 0.0f);
CUBE_VERT(13, x1, y0, z0, 1.0f, 0.0f);
CUBE_VERT(14, x1, y1, z0, 1.0f, 1.0f);
CUBE_VERT(15, x1, y0, z1, 0.0f, 0.0f);
CUBE_VERT(16, x1, y1, z0, 1.0f, 1.0f);
CUBE_VERT(17, x1, y1, z1, 0.0f, 1.0f);
// Left face (-X normal): CCW when viewed from -X
CUBE_VERT(18, x0, y0, z0, 0.0f, 0.0f);
CUBE_VERT(19, x0, y0, z1, 1.0f, 0.0f);
CUBE_VERT(20, x0, y1, z1, 1.0f, 1.0f);
CUBE_VERT(21, x0, y0, z0, 0.0f, 0.0f);
CUBE_VERT(22, x0, y1, z1, 1.0f, 1.0f);
CUBE_VERT(23, x0, y1, z0, 0.0f, 1.0f);
// Top face (+Y normal): CCW when viewed from +Y
CUBE_VERT(24, x0, y1, z1, 0.0f, 0.0f);
CUBE_VERT(25, x1, y1, z1, 1.0f, 0.0f);
CUBE_VERT(26, x1, y1, z0, 1.0f, 1.0f);
CUBE_VERT(27, x0, y1, z1, 0.0f, 0.0f);
CUBE_VERT(28, x1, y1, z0, 1.0f, 1.0f);
CUBE_VERT(29, x0, y1, z0, 0.0f, 1.0f);
// Bottom face (-Y normal): CCW when viewed from -Y
CUBE_VERT(30, x0, y0, z0, 0.0f, 0.0f);
CUBE_VERT(31, x1, y0, z0, 1.0f, 0.0f);
CUBE_VERT(32, x1, y0, z1, 1.0f, 1.0f);
CUBE_VERT(33, x0, y0, z0, 0.0f, 0.0f);
CUBE_VERT(34, x1, y0, z1, 1.0f, 1.0f);
CUBE_VERT(35, x0, y0, z1, 0.0f, 1.0f);
#undef CUBE_VERT
}
@@ -0,0 +1,43 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/mesh/mesh.h"
#include "display/color.h"
#define CUBE_FACE_COUNT 6
#define CUBE_VERTICES_PER_FACE 6
#define CUBE_VERTEX_COUNT (CUBE_FACE_COUNT * CUBE_VERTICES_PER_FACE)
#define CUBE_PRIMITIVE_TYPE MESH_PRIMITIVE_TYPE_TRIANGLES
extern mesh_t CUBE_MESH_SIMPLE;
extern meshvertex_t CUBE_MESH_SIMPLE_VERTICES[CUBE_VERTEX_COUNT];
/**
* Initializes the simple unit cube mesh (0,0,0) to (1,1,1).
*
* @return Error for initialization of the cube mesh.
*/
errorret_t cubeInit();
/**
* Buffers a 3D axis-aligned cube into the provided vertex array.
* Writes CUBE_VERTEX_COUNT vertices (6 faces x 6 vertices, CCW winding).
*
* @param vertices The vertex array to buffer into.
* @param min The minimum XYZ corner of the cube.
* @param max The maximum XYZ corner of the cube.
* @param color The color applied to all vertices.
*/
void cubeBuffer(
meshvertex_t *vertices,
const vec3 min,
const vec3 max
#if MESH_ENABLE_COLOR
, const color_t color
#endif
);
@@ -0,0 +1,116 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "mesh.h"
#include "util/memory.h"
#include "assert/assert.h"
#include "util/math.h"
errorret_t meshInit(
mesh_t *mesh,
const meshprimitivetype_t primitiveType,
const int32_t vertexCount,
const meshvertex_t *vertices
) {
assertNotNull(mesh, "Mesh cannot be NULL");
assertNotNull(vertices, "Vertices cannot be NULL");
assertTrue(vertexCount > 0, "Vertex count must be greater than 0");
memoryZero(mesh, sizeof(mesh_t));
errorChain(meshInitPlatform(mesh, primitiveType, vertexCount, vertices));
errorOk();
}
errorret_t meshFlush(
mesh_t *mesh,
const int32_t vertexOffset,
const int32_t vertexCount
) {
#ifdef meshFlushPlatform
assertNotNull(mesh, "Mesh cannot be NULL");
assertTrue(vertexOffset >= 0, "Vertex offset must be non-negative.");
assertTrue(
vertexCount == -1 || vertexCount > 0, "Vertex count incorrect."
);
int32_t vertCount = meshGetVertexCount(mesh);
assertTrue(
vertexOffset < (vertCount - 1), "Need at least one vert to draw"
);
int32_t drawCount = vertexCount;
if(vertexCount == -1) {
drawCount = vertCount - vertexOffset;
}
errorChain(meshFlushPlatform(mesh, vertexOffset, vertexCount));
#endif
errorOk();
}
errorret_t meshDraw(
const mesh_t *mesh,
const int32_t vertexOffset,
const int32_t vertexCount
) {
assertNotNull(mesh, "Mesh cannot be NULL");
assertTrue(vertexOffset >= 0, "Vertex offset must be non-negative");
assertTrue(vertexCount == -1 || vertexCount > 0, "Incorrect vert count");
int32_t vertDrawCount = vertexCount;
if(vertexCount == -1) {
const int32_t totalVertices = meshGetVertexCount(mesh);
vertDrawCount = totalVertices - vertexOffset;
}
if(vertDrawCount == 0) {
errorOk();
}
assertTrue(
vertexOffset + vertDrawCount <= meshGetVertexCount(mesh),
"Vertex offset and count must be within vertex count bounds"
);
errorChain(meshDrawPlatform(mesh, vertexOffset, vertDrawCount));
errorOk();
}
void meshGetBounds(
const mesh_t *mesh,
vec3 outMin,
vec3 outMax
) {
assertNotNull(mesh, "Mesh cannot be NULL");
assertNotNull(outMin, "Output min cannot be NULL");
assertNotNull(outMax, "Output max cannot be NULL");
for(int i = 0; i < 3; i++) {
outMin[i] = FLT_MAX;
outMax[i] = -FLT_MAX;
}
for(uint32_t i = 0; i < mesh->vertexCount; i++) {
meshvertex_t vert = mesh->vertices[i];
for(int j = 0; j < 3; j++) {
outMin[j] = mathMin(outMin[j], vert.pos[j]);
outMax[j] = mathMax(outMax[j], vert.pos[j]);
}
}
}
int32_t meshGetVertexCount(const mesh_t *mesh) {
assertNotNull(mesh, "Mesh cannot be NULL");
return meshGetVertexCountPlatform(mesh);
}
errorret_t meshDispose(mesh_t *mesh) {
assertNotNull(mesh, "Mesh cannot be NULL");
errorChain(meshDisposePlatform(mesh));
memoryZero(mesh, sizeof(mesh_t));
errorOk();
}
@@ -0,0 +1,100 @@
// Copyright (c) 2026 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/display.h"
#include "display/color.h"
#include "display/mesh/meshplatform.h"
#ifndef meshInitPlatform
#error "meshInitPlatform must be defined"
#endif
#ifndef meshDrawPlatform
#error "meshDrawPlatform must be defined"
#endif
#ifndef meshGetVertexCountPlatform
#error "meshGetVertexCountPlatform must be defined"
#endif
#ifndef meshDisposePlatform
#error "meshDisposePlatform must be defined"
#endif
typedef meshprimitivetypeplatform_t meshprimitivetype_t;
typedef meshplatform_t mesh_t;
/**
* Initializes a mesh.
*
* @param mesh The mesh to initialize.
* @param primitiveType The OpenGL primitive type (e.g., GL_TRIANGLES).
* @param vertexCount The number of vertices in the mesh.
* @param vertices The vertex data for the mesh.
* @return An error indicating success or failure.
*/
errorret_t meshInit(
mesh_t *mesh,
const meshprimitivetype_t primitiveType,
const int32_t vertexCount,
const meshvertex_t *vertices
);
/**
* Instructs the mesh to flush the vertices to the GPU. This is surprisingly
* only really necessary on modern devices, as we tend to let older devices
* read the vertices from the main memory directly.
*
* @param mesh Mesh to flush the vertices for.
* @param vertexOffset Start vertex to flush.
* @param vertexCount Count of vertices to flush, set to -1 for all.
* @return Error state.
*/
errorret_t meshFlush(
mesh_t *mesh,
const int32_t vertexOffset,
const int32_t vertexCount
);
/**
* Draws a mesh.
*
* @param mesh The mesh to draw.
* @param vertexOffset The offset in the vertex array to start drawing from.
* @param vertexCount The number of vertices to draw. If -1, draws all vertices.
* @return An error indicating success or failure.
*/
errorret_t meshDraw(
const mesh_t *mesh,
const int32_t vertexOffset,
const int32_t vertexCount
);
/**
* Gets the axis-aligned bounding box of a mesh.
*
* @param mesh The mesh to get the bounds of.
* @param outMin Output parameter for the minimum corner of the bounding box.
* @param outMax Output parameter for the maximum corner of the bounding box.
*/
void meshGetBounds(
const mesh_t *mesh,
vec3 outMin,
vec3 outMax
);
/**
* Gets the vertex count of a mesh.
*
* @param mesh The mesh to get the vertex count from.
* @return The vertex count of the mesh.
*/
int32_t meshGetVertexCount(const mesh_t *mesh);
/**
* Disposes a mesh.
*
* @param mesh The mesh to dispose.
* @return An error indicating success or failure.
*/
errorret_t meshDispose(mesh_t *mesh);
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#include "display/color.h"
#ifndef MESH_ENABLE_COLOR
#define MESH_ENABLE_COLOR 0
#endif
#define MESH_VERTEX_UV_SIZE 2
#define MESH_VERTEX_POS_SIZE 3
typedef struct {
#if MESH_ENABLE_COLOR
color_t color;
#endif
float_t uv[MESH_VERTEX_UV_SIZE];
float_t pos[MESH_VERTEX_POS_SIZE];
} meshvertex_t;
@@ -0,0 +1,116 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "plane.h"
#include "assert/assert.h"
mesh_t PLANE_MESH_SIMPLE;
meshvertex_t PLANE_MESH_SIMPLE_VERTICES[PLANE_VERTEX_COUNT];
errorret_t planeInit() {
vec3 min = { 0.0f, 0.0f, 0.0f };
vec3 max = { 1.0f, 0.0f, 1.0f };
vec2 uvMin = { 0.0f, 0.0f };
vec2 uvMax = { 1.0f, 1.0f };
planeBuffer(
PLANE_MESH_SIMPLE_VERTICES,
PLANE_AXIS_XZ,
min,
max
#if MESH_ENABLE_COLOR
, COLOR_WHITE_4B
#endif
, uvMin,
uvMax
);
errorChain(meshInit(
&PLANE_MESH_SIMPLE,
PLANE_PRIMITIVE_TYPE,
PLANE_VERTEX_COUNT,
PLANE_MESH_SIMPLE_VERTICES
));
errorOk();
}
/* Helper macro to write one vertex. */
#if MESH_ENABLE_COLOR
#define PLANE_VERT(i, px, py, pz, u, v) \
vertices[i].color = color; \
vertices[i].pos[0] = (px); \
vertices[i].pos[1] = (py); \
vertices[i].pos[2] = (pz); \
vertices[i].uv[0] = (u); \
vertices[i].uv[1] = (v);
#else
#define PLANE_VERT(i, px, py, pz, u, v) \
vertices[i].pos[0] = (px); \
vertices[i].pos[1] = (py); \
vertices[i].pos[2] = (pz); \
vertices[i].uv[0] = (u); \
vertices[i].uv[1] = (v);
#endif
void planeBuffer(
meshvertex_t *vertices,
const planeaxis_t axis,
const vec3 min,
const vec3 max
#if MESH_ENABLE_COLOR
, const color_t color
#endif
, const vec2 uvMin,
const vec2 uvMax
) {
assertNotNull(vertices, "Vertices cannot be NULL");
assertNotNull(min, "Min vector cannot be NULL");
assertNotNull(max, "Max vector cannot be NULL");
assertNotNull(uvMin, "uvMin cannot be NULL");
assertNotNull(uvMax, "uvMax cannot be NULL");
const float_t u0 = uvMin[0], u1 = uvMax[0];
const float_t v0 = uvMin[1], v1 = uvMax[1];
switch(axis) {
case PLANE_AXIS_XY: {
/* Flat in XY at z = min[2]; spans X and Y. */
const float_t z = min[2];
PLANE_VERT(0, min[0], min[1], z, u0, v0)
PLANE_VERT(1, max[0], min[1], z, u1, v0)
PLANE_VERT(2, max[0], max[1], z, u1, v1)
PLANE_VERT(3, min[0], min[1], z, u0, v0)
PLANE_VERT(4, max[0], max[1], z, u1, v1)
PLANE_VERT(5, min[0], max[1], z, u0, v1)
break;
}
case PLANE_AXIS_XZ: {
/* Flat in XZ at y = min[1]; spans X and Z. */
const float_t y = min[1];
PLANE_VERT(0, min[0], y, min[2], u0, v0)
PLANE_VERT(1, max[0], y, min[2], u1, v0)
PLANE_VERT(2, max[0], y, max[2], u1, v1)
PLANE_VERT(3, min[0], y, min[2], u0, v0)
PLANE_VERT(4, max[0], y, max[2], u1, v1)
PLANE_VERT(5, min[0], y, max[2], u0, v1)
break;
}
case PLANE_AXIS_YZ: {
/* Flat in YZ at x = min[0]; spans Y and Z. */
const float_t x = min[0];
PLANE_VERT(0, x, min[1], min[2], u0, v0)
PLANE_VERT(1, x, max[1], min[2], u1, v0)
PLANE_VERT(2, x, max[1], max[2], u1, v1)
PLANE_VERT(3, x, min[1], min[2], u0, v0)
PLANE_VERT(4, x, max[1], max[2], u1, v1)
PLANE_VERT(5, x, min[1], max[2], u0, v1)
break;
}
}
#undef PLANE_VERT
}
@@ -0,0 +1,61 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/mesh/mesh.h"
#include "display/color.h"
#define PLANE_VERTEX_COUNT 6
#define PLANE_PRIMITIVE_TYPE MESH_PRIMITIVE_TYPE_TRIANGLES
/** Which axis the plane's normal points along. */
typedef enum {
PLANE_AXIS_XY = 0, /**< Flat in XY, normal along +Z (billboard/wall face). */
PLANE_AXIS_XZ = 1, /**< Flat in XZ, normal along +Y (ground/floor plane). */
PLANE_AXIS_YZ = 2, /**< Flat in YZ, normal along +X (side wall). */
} planeaxis_t;
extern mesh_t PLANE_MESH_SIMPLE;
extern meshvertex_t PLANE_MESH_SIMPLE_VERTICES[PLANE_VERTEX_COUNT];
/**
* Initializes the simple unit XZ plane mesh (ground plane) from (0,0,0) to
* (1,0,1).
*
* @return Error for initialization of the plane mesh.
*/
errorret_t planeInit();
/**
* Buffers an axis-aligned plane into the provided vertex array.
* Writes PLANE_VERTEX_COUNT (6) vertices (two triangles, CCW winding).
*
* The min/max corners fully describe the plane in 3D space. The axis enum
* controls which dimension is treated as the "depth" (normal) axis:
* PLANE_AXIS_XY: spans X and Y, depth from min[2]/max[2] (uses min[2])
* PLANE_AXIS_XZ: spans X and Z, depth from min[1]/max[1] (uses min[1])
* PLANE_AXIS_YZ: spans Y and Z, depth from min[0]/max[0] (uses min[0])
*
* @param vertices Vertex array to write into (must hold PLANE_VERTEX_COUNT).
* @param axis Which axis the plane's normal points along.
* @param min Minimum XYZ corner.
* @param max Maximum XYZ corner.
* @param color Color applied to all vertices.
* @param uvMin Minimum UV coordinates.
* @param uvMax Maximum UV coordinates.
*/
void planeBuffer(
meshvertex_t *vertices,
const planeaxis_t axis,
const vec3 min,
const vec3 max
#if MESH_ENABLE_COLOR
, const color_t color
#endif
, const vec2 uvMin,
const vec2 uvMax
);
@@ -0,0 +1,219 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "quad.h"
#include "assert/assert.h"
mesh_t QUAD_MESH_SIMPLE;
meshvertex_t QUAD_MESH_SIMPLE_VERTICES[QUAD_VERTEX_COUNT] = {
{
#if MESH_ENABLE_COLOR
.color = COLOR_WHITE_4B,
#endif
.uv = { 0.0f, 0.0f },
.pos = { 0.0f, 0.0f, 0.0f }
},
{
#if MESH_ENABLE_COLOR
.color = COLOR_WHITE_4B,
#endif
.uv = { 1.0f, 0.0f },
.pos = { 1.0f, 0.0f, 0.0f }
},
{
#if MESH_ENABLE_COLOR
.color = COLOR_WHITE_4B,
#endif
.uv = { 1.0f, 1.0f },
.pos = { 1.0f, 1.0f, 0.0f }
},
{
#if MESH_ENABLE_COLOR
.color = COLOR_WHITE_4B,
#endif
.uv = { 0.0f, 0.0f },
.pos = { 0.0f, 0.0f, 0.0f }
},
{
#if MESH_ENABLE_COLOR
.color = COLOR_WHITE_4B,
#endif
.uv = { 1.0f, 1.0f },
.pos = { 1.0f, 1.0f, 0.0f }
},
{
#if MESH_ENABLE_COLOR
.color = COLOR_WHITE_4B,
#endif
.uv = { 0.0f, 1.0f },
.pos = { 0.0f, 1.0f, 0.0f }
}
};
errorret_t quadInit() {
errorChain(meshInit(
&QUAD_MESH_SIMPLE,
QUAD_PRIMITIVE_TYPE,
QUAD_VERTEX_COUNT,
QUAD_MESH_SIMPLE_VERTICES
));
errorOk();
}
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
#if MESH_ENABLE_COLOR
, const color_t color
#endif
) {
const float_t z = 0.0f; // Z coordinate for 2D rendering
assertNotNull(vertices, "Vertices cannot be NULL");
// First triangle
#if MESH_ENABLE_COLOR
vertices[0].color = color;
#endif
vertices[0].uv[0] = u0;
vertices[0].uv[1] = v1;
vertices[0].pos[0] = minX;
vertices[0].pos[1] = maxY;
vertices[0].pos[2] = z;
#if MESH_ENABLE_COLOR
vertices[1].color = color;
#endif
vertices[1].uv[0] = u1;
vertices[1].uv[1] = v0;
vertices[1].pos[0] = maxX;
vertices[1].pos[1] = minY;
vertices[1].pos[2] = z;
#if MESH_ENABLE_COLOR
vertices[2].color = color;
#endif
vertices[2].uv[0] = u0;
vertices[2].uv[1] = v0;
vertices[2].pos[0] = minX;
vertices[2].pos[1] = minY;
vertices[2].pos[2] = z;
// Second triangle
#if MESH_ENABLE_COLOR
vertices[3].color = color;
#endif
vertices[3].uv[0] = u0;
vertices[3].uv[1] = v1;
vertices[3].pos[0] = minX;
vertices[3].pos[1] = maxY;
vertices[3].pos[2] = z;
#if MESH_ENABLE_COLOR
vertices[4].color = color;
#endif
vertices[4].uv[0] = u1;
vertices[4].uv[1] = v1;
vertices[4].pos[0] = maxX;
vertices[4].pos[1] = maxY;
vertices[4].pos[2] = z;
#if MESH_ENABLE_COLOR
vertices[5].color = color;
#endif
vertices[5].uv[0] = u1;
vertices[5].uv[1] = v0;
vertices[5].pos[0] = maxX;
vertices[5].pos[1] = minY;
vertices[5].pos[2] = z;
}
void quadBuffer3D(
meshvertex_t *vertices,
const vec3 min,
const vec3 max,
const vec2 uvMin,
const vec2 uvMax
#if MESH_ENABLE_COLOR
, const color_t color
#endif
) {
assertNotNull(vertices, "Vertices cannot be NULL");
assertNotNull(min, "Min vector cannot be NULL");
assertNotNull(max, "Max vector cannot be NULL");
assertNotNull(uvMin, "UV Min vector cannot be NULL");
assertNotNull(uvMax, "UV Max vector cannot be NULL");
// First triangle
#if MESH_ENABLE_COLOR
vertices[0].color = color;
#endif
vertices[0].uv[0] = uvMin[0];
vertices[0].uv[1] = uvMin[1];
vertices[0].pos[0] = min[0];
vertices[0].pos[1] = min[1];
vertices[0].pos[2] = min[2];
#if MESH_ENABLE_COLOR
vertices[1].color = color;
#endif
vertices[1].uv[0] = uvMax[0];
vertices[1].uv[1] = uvMin[1];
vertices[1].pos[0] = max[0];
vertices[1].pos[1] = min[1];
vertices[1].pos[2] = min[2];
#if MESH_ENABLE_COLOR
vertices[2].color = color;
#endif
vertices[2].uv[0] = uvMax[0];
vertices[2].uv[1] = uvMax[1];
vertices[2].pos[0] = max[0];
vertices[2].pos[1] = max[1];
vertices[2].pos[2] = min[2];
// Second triangle
#if MESH_ENABLE_COLOR
vertices[3].color = color;
#endif
vertices[3].uv[0] = uvMin[0];
vertices[3].uv[1] = uvMin[1];
vertices[3].pos[0] = min[0];
vertices[3].pos[1] = min[1];
vertices[3].pos[2] = min[2];
#if MESH_ENABLE_COLOR
vertices[4].color = color;
#endif
vertices[4].uv[0] = uvMax[0];
vertices[4].uv[1] = uvMax[1];
vertices[4].pos[0] = max[0];
vertices[4].pos[1] = max[1];
vertices[4].pos[2] = min[2];
#if MESH_ENABLE_COLOR
vertices[5].color = color;
#endif
vertices[5].uv[0] = uvMin[0];
vertices[5].uv[1] = uvMax[1];
vertices[5].pos[0] = min[0];
vertices[5].pos[1] = max[1];
vertices[5].pos[2] = min[2];
}
@@ -0,0 +1,73 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "mesh.h"
#include "display/color.h"
#define QUAD_VERTEX_COUNT 6
#define QUAD_PRIMITIVE_TYPE MESH_PRIMITIVE_TYPE_TRIANGLES
extern mesh_t QUAD_MESH_SIMPLE;
extern meshvertex_t QUAD_MESH_SIMPLE_VERTICES[QUAD_VERTEX_COUNT];
/**
* Initializes the quad mesh.
*
* @return Error for initialization of the quad mesh.
*/
errorret_t quadInit();
/**
* Buffers a quad into the provided vertex array.
*
* @param vertices The vertex array to buffer into.
* @param minX The minimum X coordinate of the quad.
* @param minY The minimum Y coordinate of the quad.
* @param maxX The maximum X coordinate of the quad.
* @param maxY The maximum Y coordinate of the quad.
* @param color The color of the quad.
* @param u0 The U texture coordinate for the first vertex.
* @param v0 The V texture coordinate for the first vertex.
* @param u1 The U texture coordinate for the second vertex.
* @param v1 The V texture coordinate for the second vertex.
*/
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
#if MESH_ENABLE_COLOR
, const color_t color
#endif
);
/**
* Buffers a 3D quad into the provided vertex array.
*
* @param vertices The vertex array to buffer into.
* @param min The minimum XYZ coordinates of the quad.
* @param max The maximum XYZ coordinates of the quad.
* @param color The color of the quad.
* @param uvMin The minimum UV coordinates of the quad.
* @param uvMax The maximum UV coordinates of the quad.
*/
void quadBuffer3D(
meshvertex_t *vertices,
const vec3 min,
const vec3 max,
const vec2 uvMin,
const vec2 uvMax
#if MESH_ENABLE_COLOR
, const color_t color
#endif
);
@@ -0,0 +1,145 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "sphere.h"
#include "assert/assert.h"
mesh_t SPHERE_MESH_SIMPLE;
meshvertex_t SPHERE_MESH_SIMPLE_VERTICES[SPHERE_VERTEX_COUNT];
errorret_t sphereInit() {
vec3 center = { 0.0f, 0.0f, 0.0f };
sphereBuffer(
SPHERE_MESH_SIMPLE_VERTICES,
center,
0.5f,
SPHERE_STACKS,
SPHERE_SECTORS
#if MESH_ENABLE_COLOR
, COLOR_WHITE_4B
#endif
);
errorChain(meshInit(
&SPHERE_MESH_SIMPLE,
SPHERE_PRIMITIVE_TYPE,
SPHERE_VERTEX_COUNT,
SPHERE_MESH_SIMPLE_VERTICES
));
errorOk();
}
void sphereBuffer(
meshvertex_t *vertices,
const vec3 center,
const float_t radius,
const int32_t stacks,
const int32_t sectors
#if MESH_ENABLE_COLOR
, const color_t color
#endif
) {
assertNotNull(vertices, "Vertices cannot be NULL");
assertNotNull(center, "Center vector cannot be NULL");
const float_t stackStep = (float_t)GLM_PI / (float_t)stacks;
const float_t sectorStep = 2.0f * (float_t)GLM_PI / (float_t)sectors;
int32_t vi = 0;
for(int32_t i = 0; i < stacks; i++) {
/* Latitude angles: top of band -> bottom of band */
const float_t phi1 = (float_t)GLM_PI_2 - (float_t)i * stackStep;
const float_t phi2 = (float_t)GLM_PI_2 - (float_t)(i + 1) * stackStep;
const float_t y1 = radius * sinf(phi1);
const float_t y2 = radius * sinf(phi2);
const float_t xz1 = radius * cosf(phi1);
const float_t xz2 = radius * cosf(phi2);
const float_t v1 = 1.0f - (float_t)i / (float_t)stacks;
const float_t v2 = 1.0f - (float_t)(i + 1) / (float_t)stacks;
for(int32_t j = 0; j < sectors; j++) {
const float_t theta1 = (float_t)j * sectorStep;
const float_t theta2 = (float_t)(j + 1) * sectorStep;
const float_t x11 = xz1 * cosf(theta1);
const float_t z11 = xz1 * sinf(theta1);
const float_t x12 = xz1 * cosf(theta2);
const float_t z12 = xz1 * sinf(theta2);
const float_t x21 = xz2 * cosf(theta1);
const float_t z21 = xz2 * sinf(theta1);
const float_t x22 = xz2 * cosf(theta2);
const float_t z22 = xz2 * sinf(theta2);
const float_t u1 = (float_t)j / (float_t)sectors;
const float_t u2 = (float_t)(j + 1) / (float_t)sectors;
/* Triangle 1: top-left, bottom-left, top-right */
#if MESH_ENABLE_COLOR
vertices[vi].color = color;
#endif
vertices[vi].pos[0] = center[0] + x11;
vertices[vi].pos[1] = center[1] + y1;
vertices[vi].pos[2] = center[2] + z11;
vertices[vi].uv[0] = u1;
vertices[vi].uv[1] = v1;
vi++;
#if MESH_ENABLE_COLOR
vertices[vi].color = color;
#endif
vertices[vi].pos[0] = center[0] + x21;
vertices[vi].pos[1] = center[1] + y2;
vertices[vi].pos[2] = center[2] + z21;
vertices[vi].uv[0] = u1;
vertices[vi].uv[1] = v2;
vi++;
#if MESH_ENABLE_COLOR
vertices[vi].color = color;
#endif
vertices[vi].pos[0] = center[0] + x12;
vertices[vi].pos[1] = center[1] + y1;
vertices[vi].pos[2] = center[2] + z12;
vertices[vi].uv[0] = u2;
vertices[vi].uv[1] = v1;
vi++;
/* Triangle 2: top-right, bottom-left, bottom-right */
#if MESH_ENABLE_COLOR
vertices[vi].color = color;
#endif
vertices[vi].pos[0] = center[0] + x12;
vertices[vi].pos[1] = center[1] + y1;
vertices[vi].pos[2] = center[2] + z12;
vertices[vi].uv[0] = u2;
vertices[vi].uv[1] = v1;
vi++;
#if MESH_ENABLE_COLOR
vertices[vi].color = color;
#endif
vertices[vi].pos[0] = center[0] + x21;
vertices[vi].pos[1] = center[1] + y2;
vertices[vi].pos[2] = center[2] + z21;
vertices[vi].uv[0] = u1;
vertices[vi].uv[1] = v2;
vi++;
#if MESH_ENABLE_COLOR
vertices[vi].color = color;
#endif
vertices[vi].pos[0] = center[0] + x22;
vertices[vi].pos[1] = center[1] + y2;
vertices[vi].pos[2] = center[2] + z22;
vertices[vi].uv[0] = u2;
vertices[vi].uv[1] = v2;
vi++;
}
}
}
@@ -0,0 +1,47 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/mesh/mesh.h"
#include "display/color.h"
#define SPHERE_STACKS 8
#define SPHERE_SECTORS 16
#define SPHERE_VERTEX_COUNT (SPHERE_STACKS * SPHERE_SECTORS * 6)
#define SPHERE_PRIMITIVE_TYPE MESH_PRIMITIVE_TYPE_TRIANGLES
extern mesh_t SPHERE_MESH_SIMPLE;
extern meshvertex_t SPHERE_MESH_SIMPLE_VERTICES[SPHERE_VERTEX_COUNT];
/**
* Initializes the simple unit sphere mesh centered at (0,0,0) with radius 0.5.
*
* @return Error for initialization of the sphere mesh.
*/
errorret_t sphereInit();
/**
* Buffers a UV sphere into the provided vertex array.
* Writes stacks * sectors * 6 vertices (CCW winding).
*
* @param vertices Vertex array to write into (must hold stacks*sectors*6).
* @param center Center position of the sphere.
* @param radius Radius of the sphere.
* @param stacks Number of horizontal rings (latitude bands).
* @param sectors Number of vertical segments (longitude slices).
* @param color Color applied to all vertices.
*/
void sphereBuffer(
meshvertex_t *vertices,
const vec3 center,
const float_t radius,
const int32_t stacks,
const int32_t sectors
#if MESH_ENABLE_COLOR
, const color_t color
#endif
);
@@ -0,0 +1,106 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "triprism.h"
#include "assert/assert.h"
mesh_t TRIPRISM_MESH_SIMPLE;
meshvertex_t TRIPRISM_MESH_SIMPLE_VERTICES[TRIPRISM_VERTEX_COUNT];
errorret_t triPrismInit() {
triPrismBuffer(
TRIPRISM_MESH_SIMPLE_VERTICES,
0.0f, 0.0f, /* p0: bottom-left */
1.0f, 0.0f, /* p1: bottom-right */
0.5f, 1.0f, /* p2: apex */
0.0f, 1.0f /* minZ, maxZ */
#if MESH_ENABLE_COLOR
, COLOR_WHITE_4B
#endif
);
errorChain(meshInit(
&TRIPRISM_MESH_SIMPLE,
TRIPRISM_PRIMITIVE_TYPE,
TRIPRISM_VERTEX_COUNT,
TRIPRISM_MESH_SIMPLE_VERTICES
));
errorOk();
}
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
#if MESH_ENABLE_COLOR
, const color_t color
#endif
) {
assertNotNull(vertices, "Vertices cannot be NULL");
/* Helper macro: write one vertex then advance index. */
int32_t vi = 0;
#if MESH_ENABLE_COLOR
#define PRISM_VERT(px, py, pz, u, v) \
vertices[vi].color = color; \
vertices[vi].pos[0] = (px); \
vertices[vi].pos[1] = (py); \
vertices[vi].pos[2] = (pz); \
vertices[vi].uv[0] = (u); \
vertices[vi].uv[1] = (v); \
vi++;
#else
#define PRISM_VERT(px, py, pz, u, v) \
vertices[vi].pos[0] = (px); \
vertices[vi].pos[1] = (py); \
vertices[vi].pos[2] = (pz); \
vertices[vi].uv[0] = (u); \
vertices[vi].uv[1] = (v); \
vi++;
#endif
/* --- Front face (z = maxZ), CCW from +Z --- */
PRISM_VERT(x0, y0, maxZ, 0.0f, 0.0f)
PRISM_VERT(x1, y1, maxZ, 1.0f, 0.0f)
PRISM_VERT(x2, y2, maxZ, 0.5f, 1.0f)
/* --- Back face (z = minZ), CCW from -Z (reverse winding) --- */
PRISM_VERT(x2, y2, minZ, 0.5f, 1.0f)
PRISM_VERT(x1, y1, minZ, 1.0f, 0.0f)
PRISM_VERT(x0, y0, minZ, 0.0f, 0.0f)
/* --- Side face 0: edge p0->p1 --- */
PRISM_VERT(x0, y0, minZ, 0.0f, 0.0f)
PRISM_VERT(x1, y1, minZ, 1.0f, 0.0f)
PRISM_VERT(x1, y1, maxZ, 1.0f, 1.0f)
PRISM_VERT(x0, y0, minZ, 0.0f, 0.0f)
PRISM_VERT(x1, y1, maxZ, 1.0f, 1.0f)
PRISM_VERT(x0, y0, maxZ, 0.0f, 1.0f)
/* --- Side face 1: edge p1->p2 --- */
PRISM_VERT(x1, y1, minZ, 0.0f, 0.0f)
PRISM_VERT(x2, y2, minZ, 1.0f, 0.0f)
PRISM_VERT(x2, y2, maxZ, 1.0f, 1.0f)
PRISM_VERT(x1, y1, minZ, 0.0f, 0.0f)
PRISM_VERT(x2, y2, maxZ, 1.0f, 1.0f)
PRISM_VERT(x1, y1, maxZ, 0.0f, 1.0f)
/* --- Side face 2: edge p2->p0 --- */
PRISM_VERT(x2, y2, minZ, 0.0f, 0.0f)
PRISM_VERT(x0, y0, minZ, 1.0f, 0.0f)
PRISM_VERT(x0, y0, maxZ, 1.0f, 1.0f)
PRISM_VERT(x2, y2, minZ, 0.0f, 0.0f)
PRISM_VERT(x0, y0, maxZ, 1.0f, 1.0f)
PRISM_VERT(x2, y2, maxZ, 0.0f, 1.0f)
#undef PRISM_VERT
}
@@ -0,0 +1,50 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/mesh/mesh.h"
#include "display/color.h"
#define TRIPRISM_VERTEX_COUNT 24
#define TRIPRISM_PRIMITIVE_TYPE MESH_PRIMITIVE_TYPE_TRIANGLES
extern mesh_t TRIPRISM_MESH_SIMPLE;
extern meshvertex_t TRIPRISM_MESH_SIMPLE_VERTICES[TRIPRISM_VERTEX_COUNT];
/**
* Initializes the simple unit triangular prism. The cross-section triangle has
* vertices (0,0), (1,0), (0.5,1) in XY, extruded from z=0 to z=1.
*
* @return Error for initialization of the triangular prism mesh.
*/
errorret_t triPrismInit();
/**
* Buffers a triangular prism into the provided vertex array.
* The triangular cross-section is defined by three 2D points in the XY plane;
* the prism is extruded along the Z axis between minZ and maxZ.
* Writes TRIPRISM_VERTEX_COUNT (24) vertices (CCW winding).
*
* @param vertices Vertex array to write into.
* @param x0,y0 First triangle vertex (XY).
* @param x1,y1 Second triangle vertex (XY).
* @param x2,y2 Third triangle vertex (XY).
* @param minZ Near Z extent of the prism.
* @param maxZ Far Z extent of the prism.
* @param color Color applied to all vertices.
*/
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
#if MESH_ENABLE_COLOR
, const color_t color
#endif
);