-
Notifications
You must be signed in to change notification settings - Fork 0
Guides Capsule
A capsule mesh — two hemispheres joined by a cylinder — to use as a character proxy. Same data shape as Primitives::Sphere: positions, normals, uvs, indices. Drop into a SceneObject and draw with the same pipeline as everything else.
-
Guides · Scene — you have a
SceneObjectpattern. - Guides · Flat Plane — ground exists.
Primitives::Sphere ships with VCK; capsule doesn't, because most users want it sized to their character's collision profile. A capsule generator is ~40 lines of simple trig, written here with the same output shape so it pairs with VulkanMesh::Upload exactly like the built-in primitives.
radius controls the cylindrical waist + the hemisphere caps. halfHeight is the cylinder section's half-extent — the total capsule is 2*halfHeight + 2*radius tall.
#include "VCK.h"
#include <cmath>
using namespace VCK;
Primitives::Mesh BuildCapsule(float radius, float halfHeight,
int rings = 8, int sectors = 16)
{
Primitives::Mesh out;
const float pi = 3.1415926535f;
const int ringsPerCap = rings;
const int ringCount = 2 * ringsPerCap; // top + bottom hemispheres
out.positions.reserve((ringCount + 1) * (sectors + 1));
out.normals .reserve((ringCount + 1) * (sectors + 1));
out.uvs .reserve((ringCount + 1) * (sectors + 1));
for (int r = 0; r <= ringCount; ++r)
{
// Latitude: top hemisphere [0..pi/2], bottom hemisphere [pi/2..pi].
const float lat = (pi * 0.5f) * static_cast<float>(r)
/ static_cast<float>(ringsPerCap);
const float y = std::cos(lat) * radius;
const float ring = std::sin(lat) * radius;
// Push the centre of the cylindrical section between the two caps.
const float cylY = (r <= ringsPerCap) ? halfHeight : -halfHeight;
for (int s = 0; s <= sectors; ++s)
{
const float lon = (2.0f * pi) * static_cast<float>(s)
/ static_cast<float>(sectors);
const float x = std::cos(lon) * ring;
const float z = std::sin(lon) * ring;
const Vec3 p { x, y + cylY, z };
const Vec3 n = Normalize({ x, y, z }); // hemisphere normal, ignore cyl offset
const Vec2 uv{ static_cast<float>(s) / sectors,
static_cast<float>(r) / ringCount };
out.positions.push_back(p);
out.normals .push_back(n);
out.uvs .push_back(uv);
}
}
out.indices.reserve(ringCount * sectors * 6);
for (int r = 0; r < ringCount; ++r)
{
for (int s = 0; s < sectors; ++s)
{
const uint32_t a = static_cast<uint32_t>( r * (sectors + 1) + s );
const uint32_t b = static_cast<uint32_t>( r * (sectors + 1) + s + 1);
const uint32_t c = static_cast<uint32_t>((r + 1) * (sectors + 1) + s );
const uint32_t d = static_cast<uint32_t>((r + 1) * (sectors + 1) + s + 1);
out.indices.push_back(a); out.indices.push_back(c); out.indices.push_back(b);
out.indices.push_back(b); out.indices.push_back(c); out.indices.push_back(d);
}
}
return out;
}Same output struct as Primitives::Cube / Sphere. The capsule is just a sphere whose two hemispheres have been pulled apart by 2*halfHeight along Y; the hemisphere normals stay correct because we only translate the positions, not the normal data.
rings × sectors controls tessellation. 8 × 16 is plenty for a character proxy; bump them for a screen-filling capsule.
Primitives::Mesh capsule = BuildCapsule(/*radius*/0.4f, /*halfHeight*/0.8f);
struct V { Vec3 p; Vec3 n; };
static_assert(sizeof(V) == 24, "tightly packed");
std::vector<V> verts(capsule.positions.size());
for (std::size_t i = 0; i < verts.size(); ++i)
verts[i] = { capsule.positions[i], capsule.normals[i] };
SceneObject character;
character.transform = Translate({0, 1.2f, 0}); // capsule centred 1.2m above ground
character.mesh.Upload(device, command,
verts.data(),
static_cast<VkDeviceSize>(sizeof(V) * verts.size()),
static_cast<uint32_t>(verts.size()),
capsule.indices.data(),
static_cast<uint32_t>(capsule.indices.size()));
scene.push_back(std::move(character));A 0.4m-radius / 0.8m-half-height capsule is 2.4m tall. Adjust to whatever your character controller calls "tall".
- Guides · Step & Jump — give the capsule velocity, gravity, and a jump
- Cookbook — recipe 7 (instanced primitives), recipe 8 (skinned meshes)
- Home
- Quick Start
- Your First App
- Understanding cfg
- Build — Windows / Linux / macOS
Single source of truth for the full API surface is the doc block at the top of VCK.h.