/** * 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 }