The Karasu Game Engine
  • C++ 52.1%
  • JavaScript 22.9%
  • Python 9.3%
  • CMake 7.8%
  • Shell 5.1%
  • Other 2.7%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-23 02:35:46 +00:00
.claude SCR-00012: Update engine-level math, editor submodule, docs 2026-08-23 20:09:34 -07:00
3rdparty SCR-00016: Switch to glaze and TOML for karasu 2026-08-28 17:10:20 -07:00
apps SCR-00016: Switch to glaze and TOML for karasu 2026-08-28 17:10:20 -07:00
assets/shaders SCR-00020: Extend the engine for tengu's quad viewport and Shear tool, and bump the editor submodule 2026-09-04 22:49:38 -07:00
cmake SCR-00016: Switch to glaze and TOML for karasu 2026-08-28 17:10:20 -07:00
docs SCR-00020: Fast-path already-convex surfaces in math3d_decompose 2026-09-22 19:25:07 -07:00
editor@0dd7f7df89 SCR-00020: Point editor submodule at master instead of its merged branch 2026-09-22 19:32:06 -07:00
engine SCR-00020: Fast-path already-convex surfaces in math3d_decompose 2026-09-22 19:25:07 -07:00
plugins SCR-00016: Switch to glaze and TOML for karasu 2026-08-28 17:10:20 -07:00
tools SCR-00016: Switch to glaze and TOML for karasu 2026-08-28 17:10:20 -07:00
.gitignore SCR-00020: Document tengu's mesh brushes, face subdivide and interior dissolve, and bump the editor submodule 2026-09-07 18:40:42 -07:00
.gitmodules SCR-00016: Switch to glaze and TOML for karasu 2026-08-28 17:10:20 -07:00
AGENTS.md SCR-00002: Add untracked karasu codebase 2026-08-08 18:52:42 -07:00
CLAUDE.md SCR-00020: Add math3d_boolean set operations over convex solids for tengu's CSG tool 2026-09-17 19:22:45 -07:00
CMakeLists.txt SCR-00020: Extend the engine for tengu's quad viewport and Shear tool, and bump the editor submodule 2026-09-04 22:49:38 -07:00
LICENSE Initial commit 2026-08-09 01:37:23 +00:00
PLAN.md SCR-00020: Add the editor's Flip, Duplicate, Copy and Paste commands and merge coincident brush vertices on every accepted mesh edit 2026-09-05 12:13:34 -07:00
README.md SCR-00020: Add the editor's Flip, Duplicate, Copy and Paste commands and merge coincident brush vertices on every accepted mesh edit 2026-09-05 12:13:34 -07:00

Karasu

A C23/C++26 3D game engine on SDL3 (window/input), SDL3_ttf (font rasterization + HarfBuzz shaping), Vulkan (GPU graphics), Box3D (physics), PhysicsFS (ZIP / VFS), glaze (TOML/JSON), Umka (plugins/scripts), log.c (logging), Dear ImGui (dev UI), and argparse (CLI argument parsing). Math goes through the engine's own library over GLM, GTE, Highway and FastNoise2. Ships with tengu, a native (wxWidgets) game editor sharing the same Vulkan rendering code as the games.

Doc Contents
PLAN.md Architecture, phases, status, next steps
docs/PLUGINS.md Plugin packages, ZIP archives, overwrites, load order
docs/ENGINE.md Public modules, APIs, the math library, build notes, gotchas
docs/EDITOR.md tengu, the brush editor: tools, geometry model, roadmap

Quick start

# Arch Linux deps (Vulkan-capable driver also required)
sudo pacman -S sdl3 sdl3_ttf harfbuzz freetype2 vulkan-headers vulkan-icd-loader \
  vulkan-validation-layers shaderc glslang cmake ninja base-devel fontforge

cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug
cmake --build build

./build/dist/hello_world/hello_world    # engine smoke test
./build/dist/tri/tri                    # colored triangle + Dear ImGui
./build/dist/plugin_host/plugin_host    # plugins, resource index, Umka inits
./build/dist/tengu/tengu                # the game editor: wxWidgets-owned native window, same Gfx code as the games

Building tengu needs gtk3 (sudo pacman -S gtk3); everything else above builds without it. wxWidgets itself is vendored under 3rdparty/ and built from source as a shared library (see "Shipping a game" below for why shared), so no wxwidgets-gtk3 package is required — but that first build compiles wx from source, so expect a wx-enabled tree to take noticeably longer to configure and build the first time.

plugin_host copies plugins/ next to the binary on build; it also finds the repo-root plugins/ when run from the project tree.


What’s in the tree

3rdparty/     Vendored (all git submodules): argparse, box3d, logc, imgui,
              imguizmo, physfs, glaze, umka, lz4 + zstd (.kba codecs),
              glm + gte + highway + FastNoise2 (the math stack),
              wxWidgets (tengu's window backend; built shared),
              liberation-fonts + tabler-icons (content, staged into .kba),
              ggml + llama.cpp / stable-diffusion.cpp / transcribe.cpp (opt-in AI)
              <lib>_compat/ holds local patches; the trees stay pristine
assets/       GLSL sources (compiled to build/shaders/*.spv)
tools/        Build helpers (fonts, license collector)
              kbx/ is a submodule: the .kba archive tool, and the only
              thing that writes one (the build calls it to pack assets.kba)
engine/       Shared library karasu_<branch>.so, one per engine.branch
              (core, math, gfx, window backends, ui, vfs, plugins, script host)
apps/         hello_world, tri, plugin_host, sandbox, widget_demo, flycam_demo
editor/       tengu: wxWidgets-backed native app, same engine::Gfx as the games
plugins/      Sample mods (hello, demo_mod, patch_mod, scene_api_test)
docs/         PLUGINS.md, ENGINE.md, EDITOR.md
PLAN.md       Roadmap and architecture

Apps

Binary Purpose
hello_world Init engine, probe deps, log hello, exit
tri Vulkan triangle; per-vertex colors; Dear ImGui blend controls
plugin_host Discover plugins, auto load order, index resources, run plugin_init*
sandbox Black-screen stub; manifest + CLI demo; ships a dist folder (see below)
widget_demo Nested Scene2D trees, WidgetScene, Labels, nested Layouts; --frames N runs headlessly and logs resolved layout
flycam_demo Scene3D + Camera3D/CameraController; infinite ground plane, WASD/mouse-look fly camera; --frames N runs headlessly
tengu The game editor. Native (wxWidgets) window + shared Vulkan viewport via the same engine::Gfx. Brushes as convex solids, the Brush/Select/Move/Rotate/Grab/Scale tools, Mesh Edit (subdivide, extrude, clip, bevel, proportional editing), flip/duplicate and clipboard copy/paste, layers, undo/redo — see docs/EDITOR.md

Shipping a game

cmake --build build also assembles build/dist/sandbox/ — a self-contained folder to copy anywhere:

sandbox             the executable (RUNPATH $ORIGIN)
karasu_sandbox.so   the engine, named by the manifest's engine.branch
assets.kba          PhysicsFS asset archive

For a game: three files and no lib/. Everything vendored under 3rdparty/ is statically linked into the engine library, and everything else (SDL3, SDL3_ttf, the Vulkan loader) is a system package the machine already provides.

tengu is the exception, and gets a fourth entry — a lib/ holding the wxWidgets shared libraries, found through an added $ORIGIN/lib RUNPATH. wx is copyleft, and its licence entitles whoever receives the binary to drop in their own build of the library; static linking would deny them that without shipping the editor's object files. Nothing else ever goes in lib/ — see cmake/WxRuntimeDist.cmake.

Every app is built into its own folder under build/dist/ — there is no build/bin, so what you run out of the build tree is exactly what ships. The engine itself is a shared library whose file name each game picks with engine.branch in its manifest, and every dist folder carries its own copy, so patching one game's engine cannot disturb another's.

assets.kba is a ZIP under a game extension — PhysFS mounts it by content, not by name. Engine content is namespaced under engine/:

engine/fonts/liberation/*.ttf   default engine fonts (+ upstream LICENSE)

The engine's own SPIR-V is compiled into the executable (#embed), not shipped in the archive, so the folder runs with no build tree present.

The Liberation family is the engine default (LiberationSans-Regular.ttf). It is built from the vendored FontForge .sfd sources at build time, so FontForge is a build dependency (pacman -S fontforge), like glslc.

Add the same folder to another game with engine_add_game_dist(<target>).

CMake options

Option Default Description
ENGINE_BUILD_APPS ON Build demo apps
ENGINE_ENABLE_VALIDATION ON Vulkan validation layers (Debug)
ENGINE_ENABLE_DEVUI ON Dear ImGui support
ENGINE_BUILD_DOCS ON Make the docs Doxygen target available
ENGINE_ENABLE_LLM AUTO Build llama.cpp (AUTO = whatever the game manifests ask for)
ENGINE_ENABLE_DIFFUSION AUTO Build stable-diffusion.cpp
ENGINE_ENABLE_TRANSCRIBE AUTO Build transcribe.cpp speech-to-text
ENGINE_GGML_VULKAN ON Compile ggml's Vulkan backend alongside CPU
ENGINE_SIMD_TIER AUTO CPU baseline for the whole build (AUTO = manifest cpu.simd_tier, else avx2)
ENGINE_GGML_NATIVE OFF Shorthand for ENGINE_SIMD_TIER=native (not portable)
ENGINE_RELEASE_OPT_LEVEL O2 Optimization level for Release (O2/O3/Os/Oz)
ENGINE_LTO AUTO Link-time optimization in Release/MinSizeRel
ENGINE_LINKER AUTO Linker for Release/MinSizeRel (AUTO prefers mold, then lld)
ENGINE_RELEASE_STRIP ON Strip symbol tables from shipping binaries
ENGINE_RELEASE_ICF ON Fold identical functions at link time (needs mold/lld/gold)

Release builds

cmake -S . -B build-release -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build-release

Release and MinSizeRel are optimized for size and speed together (cmake/ReleaseOptimization.cmake): LTO across the engine sources and the vendored archives absorbed into karasu_<branch>.so, -ffunction-sections plus --gc-sections, -fno-semantic-interposition to recover intra-library inlining, identical-code folding where the linker supports it, and stripped symbol tables. The two configs differ in one flag — Release is -O2, MinSizeRel is -Os. Debug is untouched by any of it, so the day-to-day loop keeps fast links and working backtraces.

What that is worth, measured on this tree (GCC 16.1, mold, avx2 tier):

Debug Release MinSizeRel
karasu_sandbox.so 105.8 MB 5.6 MB 4.3 MB
sandbox 1.46 MB 92 KB 45 KB
karasu_tengu.so 105.8 MB 5.6 MB —
tengu 14.4 MB 700 KB —
sandbox dist folder 105 MB 7.8 MB —
tengu dist folder 117 MB 8.4 MB —

assets.kba is 2.4 MB of every dist folder, so after this the Liberation fonts are the largest single thing that ships. The tengu executable and tengu dist folder rows predate the switch to shared wxWidgets and have not been re-measured — wx no longer sits inside that executable, so the binary is smaller and its dist folder now also carries lib/. karasu_tengu.so is unaffected (no karasu_<branch>.so ever linked wx), and neither are the game numbers.

-ffast-math and its constituents are deliberately excluded and should stay excluded: GTE is linked here specifically for geometry predicates whose value is correct behaviour in degenerate cases, and relaxed floating point is exactly what breaks them.

LTO makes the per-branch link genuinely expensive — one whole-program link per distinct engine.branch, over every vendored archive. -DENGINE_LTO=OFF is the lever when iterating on a Release tree.

SIMD tier. Everything — engine, apps, and every vendored library including ggml — is compiled for one x86-64 SIMD baseline, avx2 (x86-64-v3) by default. Set it per game via cpu.simd_tier in the manifest, or per build with -DENGINE_SIMD_TIER=. It is deliberately not -march=native: that tunes to the machine that compiled it and SIGILLs on anything older, which is invisible until a player hits it.

Vendored libraries are never edited in place. Local fixes live as patches in 3rdparty/<lib>_compat/ and are applied at configure time. If you sync a library and a patch stops applying, the configure fails and tells you which _compat folder to refresh. Every build also reverts them again at the end (cmake/ThirdPartyCompatRevert.cmake), so a patched submodule never shows up as "modified content" in git status; the next configure re-applies it.

The three AI backends are normally selected per game rather than with these flags: apps/<game>/game_manifest.toml carries features.enable_llm, enable_diffusion and enable_transcribe, and AUTO builds the union of what the manifests ask for. All of them run on the single shared 3rdparty/ggml, with CPU and Vulkan backends chosen at runtime. With no game opting in (the default), none of it — ggml included — is configured or compiled.

Language

  • C23 for C units (third-party often C99 with extensions where required)
  • C++26 for engine and apps

Third-party (summary)

Library Role CMake target
SDL3, SDL3_ttf, Vulkan System packages SDL3::SDL3, SDL3_ttf::SDL3_ttf, Vulkan::Vulkan
argparse CLI argument parsing argparse::argparse
Box3D Physics box3d::box3d
PhysicsFS VFS + ZIP archives physfs::physfs
glaze TOML/JSON serialization; compile-time manifest parsing (consteval TOML) and runtime plugin.toml glaze::glaze
Umka Scripting / plugins umka::umka
log.c Logging logc::logc
Dear ImGui Dev UI imgui::imgui
ImGuizmo 3D gizmos + the curve editor tengu's falloff uses imguizmo::imguizmo
GLM Linear algebra (mirrors GLSL) glm::glm
GTE Robust geometric predicates and queries gte::gte
Highway Portable SIMD over point arrays hwy, hwy_contrib
FastNoise2 Procedural noise (linked, no facade yet) FastNoise
wxWidgets tengu's window backend; built shared, shipped in lib/ engine::window_wx
liberation-fonts, tabler-icons Content — built/staged into assets.kba engine_fonts

Note: rxi's log_error/log_warn/log_info macros collide textually with any library declaring identifiers of those names, so include engine/log.h late in a translation unit. (simdjson was the library that actually collided; glaze, which replaced it, has no such identifier.)


Status

Working: build system and per-branch shared engine library, Vulkan frame loop with a per-frame-in-flight depth buffer, Dear ImGui dev UI, the math library over GLM/GTE/Highway, Renderer2D + SDL3_ttf shaped text, the Scene/SceneGraph stack with Scene2D/Scene3D trees and the widget/layout toolkit, Camera3D + CameraController (perspective and orthographic), PhysicsFS VFS and resource catalog (index + lazy load), the Umka plugin host with explicit imports, automatic load order from depends + overwrites, compile-time game manifests via #embed + C++26 static reflection, and both window backends (SDL for games, wxWidgets for tengu).

Not done yet: a generic mesh/material renderer (Scene3D::on_draw_3d() is still a raw hook), physics demos beyond linking Box3D, entity system / map serialization / texturing in tengu, audio, networking, tests and CI.