Capsule.h
#pragma once #include "glm/vec3.hpp" #include "glm/vec2.hpp" #include "glm/geometric.hpp" #include <vector> using namespace glm; struct Capsule { std::vector<int> index; std::vector<vec3> pos; std::vector<vec2> uvs; }; Capsule MakeCapsule(int resolution, float height, float radius);
Capsule.cpp
#include "capsule.h" static const float pi = 3.1415927f; Capsule MakeCapsule(int resolution, float height, float radius) { if (resolution < 2) { return {}; } // Almost same generation as UV sphere but we force the // lat count to be odd so it can be split evenly int latCount = resolution; int lonCount = resolution; if (latCount % 2 == 0) { latCount++; } // Each longitudinal count makes two triangles (6 indices) for every // lateral count except for the top and bottom poles, which only make // one triangle per longitudinal count. // UV maps require two vertices with the same position, but different UVs // so we need counts + 1. const int totalIndexCount = 6 * (latCount - 1) * lonCount; const int totalVertexCount = (latCount + 1) * (lonCount + 1); const float latStep = pi / latCount; const float lonStep = 2 * pi / lonCount; const float zOffset = clamp(height / 2.0f - radius, 0.0f, FLT_MAX); Capsule capsule; capsule.index.resize(totalIndexCount); capsule.pos.resize(totalVertexCount); capsule.uvs.resize(totalVertexCount); int currentVertex = 0; int currentIndex = 0; for (int lat = 0; lat <= latCount; lat++) { float offset = lat > latCount / 2 ? zOffset : -zOffset; for (int lon = 0; lon <= lonCount; lon++) { capsule.pos[currentVertex] = vec3( cos(lon * lonStep) * sin(lat * latStep) * radius, sin(lon * lonStep) * sin(lat * latStep) * radius, cos(lat * latStep - pi) * radius + offset ); // UVs are almost the same as the UV sphere, but V needs // to be scaled to fit the height capsule.uvs[currentVertex] = vec2( (float)lon / lonCount, capsule.pos[currentVertex].z / (radius * 2 + height) + 0.5f ); currentVertex += 1; } } // Top cap // // One triangle connects the first lateral layer to the second per longitudinal count. // Even though the top points all have the same position, their UVs are different, // so each triangle uses a different point. // // -------- lonCount --------> // lon // | * // | / | \ // 1 / | \ // | / | \ // | / \ // \ / *------------*------------* // v v + 1 (v + 1) + 1 int v = lonCount + 1; for (int lon = 0; lon < lonCount; lon++) { capsule.index[currentIndex++] = lon; capsule.index[currentIndex++] = v; capsule.index[currentIndex++] = v + 1; v += 1; } // Middle // // Each lateral layer has 2 triangles for every longitudinal count. // // -------- lonCount --------> // v v + 1 (v + 1) + 1 // | *------------*------------* // | | / | / | // latCount | / | / | // | | / | / | // | | / | / | // \ / *------------*------------* // v + lc + 1 v + lc + 2 (v + 1) + lc + 2 v = lonCount + 1; for (int lat = 1; lat < latCount - 1; lat++) { for (int lon = 0; lon < lonCount; lon++) { capsule.index[currentIndex++] = v; capsule.index[currentIndex++] = v + lonCount + 1; capsule.index[currentIndex++] = v + 1; capsule.index[currentIndex++] = v + 1; capsule.index[currentIndex++] = v + lonCount + 1; capsule.index[currentIndex++] = v + lonCount + 2; v += 1; } v += 1; } // Bottom cap // Same as top cap, but reversed. // // -------- lonCount --------> // v v + 1 (v + 1) + 1 // | *------------*------------* // | \ | / // 1 \ | / // | \ | / // | \ | / // \ / * // v + lc + 1 for (int lon = 0; lon < lonCount; lon++) { capsule.index[currentIndex++] = v; capsule.index[currentIndex++] = v + lonCount + 1; capsule.index[currentIndex++] = v + 1; v += 1; } return capsule; }