Lightweight game engine built from scratch in C++ (with Rust where it makes sense).
Kuma is designed for small, indie games β prioritizing simplicity, modularity, and ease of use over being everything to everyone.
π§ Early development β core systems being built bottom-up.
- Vulkan renderer β graphics pipeline, three pipelines (textured opaque / debug-normal opaque / transparent particle billboards), depth buffer (D32_SFLOAT, standard direct-Z), shader compilation, configurable present mode (vsync / mailbox / immediate). Per-frame instance ring buffer for particle uploads. 96-byte shared push constant range covering view-projection + camera right/up.
- SDL3 platform layer β windowing, event loop, resize handling, relative mouse mode for FPS-style camera look. 4K window (3840Γ2160) by default in the sandbox.
- Math β
Vec2,Vec3,Vec4,Quat,Mat4, perspective + look-at + MVP helpers, slerp, axis-angle and Euler quaternion factories. - Transform β position + quaternion rotation + scale, producing a model matrix.
- ECS β sparse-set Registry with generational EntityID handles, sparse-set component storage, and
view<T...>()queries with structured bindings. - Debug overlay β Dear ImGui integration with a custom Kuma Dark style and Cascadia Mono font; F3 toggles a default panel (FPS, frame time, 1% low, 60-frame sparkline). Game code calls
ImGui::*directly for custom panels. - Asset pipeline β
kuma-bake(Rust) converts source assets (.obj, .png, .jpg, .tga, .gltf, .glb, .wav, .ogg) into the engine's binary formats (.kmesh, .ktex, .ksound, .kmaterial, .kscene). Engine loads only baked binaries at runtime; no source-format parsing in the hot path. CMake invokeskuma-bakeduring build viakuma_bake_*helpers. - Camera β reusable perspective camera plus keyboard/RMB-look free-fly controller for debug inspection.
- Resource system β loads .ktex / .kmesh / .kmaterial binaries with path-and-usage-keyed caching. Texture cache distinguishes sRGB (color) from UNORM (data) uploads so normal maps don't get gamma-corrected.
- Physics β Jolt Physics 5.5.0 wired through an opaque
kuma::physicsAPI. Dynamic / Static / Kinematic bodies, sphere / box / capsule shapes, fixed-step accumulator with spiral-of-death clamp. Bodies plug into the ECS via aPhysicsBodycomponent. - Character controller β Kinematic FPS capsule on top of Jolt's
CharacterVirtual. Step-and-slide collision, slope detection, auto-step over short obstacles, ground state, pushes dynamic bodies.FpsCameraControllerreads input and writes both character and camera (mouse-look on yaw + pitch, WASD relative to character yaw, Space jump). T toggles between FPS mode and free-fly camera. - Audio β miniaudio 0.11 wired through an opaque
kuma::audioAPI. WAV / OGG sounds with 3D positional spatialization (distance attenuation + stereo panning). Dual API: fire-and-forgetplay_sound/play_sound_atreturning generation-checkedSoundHandle, and anAudioSourceECS component for long-running music / ambience.kuma-bake sounduses Symphonia to convert .wav to PCM.ksoundor pass .ogg bytes through. - Scenes β Multi-mesh asset loading via
kuma::scene::load_and_spawn.kuma-bake scenewalks a glTF scene tree, dedups primitive geometry into per-primitive.kmeshfiles, composes parent-chain world transforms, and writes a.kscenev2 index referencing meshes AND materials. Runtime spawns one ECS entity per node withTransform+MeshRef+MaterialRef. - Materials β Per-mesh diffuse textures with PBR-ready data layout.
kuma-bakeextracts glTF materials into.kmaterialfiles (108-byte header carrying base color, metallic, roughness, alpha mode, plus paths for five PBR texture slots) and dedups referenced textures into.ktex. Runtime allocates one descriptor set per material from a 256-set pool. 5-binding descriptor set layout (diffuse / normal / metallic-roughness / occlusion / emissive) with renderer-owned 1Γ1 defaults per glTF spec. Current shader samples only diffuse; the other slots wait for lighting. - Particles β CPU-driven billboard particles.
ParticleEmitterECS component owns a fixed 256-particle SoA pool. One-shot (burst) and continuous emitters share the same component shape.particles::simulateruns in UPDATE phase,particles::renderafter opaque draws (depth test on, depth write off, alpha blend on). Per-emitter back-to-front sort plus per-emitterdraw_orderenum (Index / Lifetime / ViewDepth) for within-emitter ordering. Five built-in presets:make_muzzle_flash,make_impact_spark,make_blood_spatter,make_death_poof,make_pickup_sparkle. Sandbox demo: F spawns an icosahedron + impact_spark burst; mouse-1 in FPS mode fires muzzle_flash. - Input β keyboard & mouse polling with edge detection (pressed/released this frame).
- Time β monotonic delta / total / frame count with anti-spiral clamp.
- Frame orchestration β engine-owned
begin_frame()/end_frame()wrapping a 5-phase contract (input β time β update β render β present). - Logging β severity levels, colored console output.
- Lighting β Forward / Forward+ / deferred decision pending. Will pay back the materials work β the 5-binding descriptor set finally starts sampling normal / metallic-roughness / occlusion / emissive once a lit shader exists. Render pass refactor (HDR offscreen + depth-prepass capability) likely lands as commit 1.
- Shadows β directional + point light shadow maps, layered on top of lighting.
- Renderer batching / instancing for static meshes when entity counts make per-entity draw calls a real cost.
- BC7 / BC5 texture compression, mipmap generation in
kuma-bake. - MP3 / FLAC support in the audio asset pipeline.
See CONTRIBUTING.md for commit conventions, comment style, and code style. TL;DR: Conventional Commits, clang-format on save, public headers don't leak SDL/Vulkan types.
- CMake 3.24+
- C++20 compiler (MSVC 2022, GCC 12+, or Clang 15+)
- Vulkan SDK
- Rust 1.85+ (for the
kuma-bakeasset baker - install via rustup)
cmake -B build -S .
cmake --build build --config Debug
./build/bin/Debug/sandboxOr in VS Code: press F5 (launch.json and tasks.json are included).
The sandbox loads Khronos's standard Sponza with all 25 diffuse textures (~60 MB total). Source files are gitignored; download them once with:
pwsh tools\download_sponza.ps1After that, cmake --build will bake Sponza.kscene + 103 sibling .kmesh files + 25 .kmaterial files + 25 .ktex files into build/assets/scenes/.
cmake --build invokes cargo automatically to build kuma-bake and bakes
the sandbox's source assets into the engine binary format (.kmesh, .ktex)
before linking the sandbox executable.
Kuma/
βββ engine/ Engine static library
β βββ include/kuma/ Public API headers
β β βββ kuma.h Main entry point
β β βββ audio.h Sound playback + listener pose
β β βββ camera.h Camera + free-fly controller
β β βββ character.h Character controller component
β β βββ debug.h ImGui debug overlay
β β βββ ecs.h Registry + view + EntityID
β β βββ fps_camera.h FPS character + camera controller
β β βββ input.h Keyboard and mouse polling
β β βββ material.h Material runtime struct + TextureUsage
β β βββ math.h Vec2/3/4, Quat, Mat4
β β βββ particles.h ParticleEmitter component + presets
β β βββ physics.h Jolt-backed physics module
β β βββ platform.h exe_relative path helper
β β βββ renderer.h Renderer interface
β β βββ resource_manager.h Asset loading + caching
β β βββ scene.h Multi-mesh scene loading
β β βββ time.h Frame delta / total time
β β βββ transform.h Position + rotation + scale
β β βββ window.h Window management
β β βββ log.h Logging system
β β βββ asset_format.h On-disk binary format definitions
β βββ src/
β β βββ core/ Engine init, frame loop, logging, ECS, debug
β β βββ platform/ SDL3 windowing, input, paths
β β βββ renderer/ Vulkan device, swapchain, pipelines, resources
β β βββ resources/ ResourceManager (.kmesh/.ktex/.kmaterial loading)
β β βββ physics/ Jolt integration
β β βββ character/ CharacterVirtual + FpsCameraController
β β βββ audio/ miniaudio integration
β β βββ scene/ .kscene parser + spawn
β β βββ particles/ Sim, render, presets
β βββ shaders/ GLSL shaders (compiled to SPIR-V at build time)
βββ sandbox/ Test application (Sponza walkaround + FX demo)
βββ tools/
β βββ kuma-bake/ Rust asset converter (mesh/tex/sound/scene/gltf)
β βββ download_sponza.ps1 Fetch Sponza glTF + textures
βββ assets/ Game-side baked assets (engine defaults)
βββ tests/ GoogleTest unit + integration tests
βββ cmake/ Helper modules (kuma-bake.cmake, etc)
βββ .vscode/ VS Code launch + build tasks
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Game / Application β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Scene / ECS (Registry, components, views) β
ββββββββββββ¬βββββββββββ¬βββββββββββ¬βββββββββββ¬ββββββββββββββββββ€
β Renderer β Audio β Physics βParticles β Character β
ββββββββββββ΄βββββββββββ΄βββββββββββ΄βββββββββββ΄ββββββββββββββββββ€
β Resources (ResourceManager + asset cache) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Platform (SDL3 window, input, paths) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Core (frame loop, time, log, math, debug) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Library | Purpose | Integration |
|---|---|---|
| SDL3 | Windowing, input | FetchContent (automatic) |
| Vulkan | GPU rendering | System install (Vulkan SDK) |
| Jolt Physics | Rigid body + character | FetchContent (automatic) |
| miniaudio | Audio playback | Single header, vendored |
| Dear ImGui | Debug overlay | FetchContent (automatic) |
| stb_image | Image loading (Rust side via image crate; engine takes baked .ktex only) |
Indirect |
| GoogleTest | Unit + integration tests | FetchContent (automatic) |
Rust-side (kuma-bake):
| Crate | Purpose |
|---|---|
gltf |
glTF / GLB parsing |
image |
PNG / JPG / TGA decoding |
tobj |
OBJ parsing |
symphonia |
WAV / OGG audio decoding |
bytemuck |
Safe POD struct β bytes conversion |
TBD