Fury3D is a cross-platform rendering engine written in C++17 and OpenGL, with a Lua-driven editor, JSON-configurable rendering pipelines, and a data-driven post-process chain.
Picked up the development with the help of Kimi K3, MiniMax M3, GLM 5.2 with Claude Code.
Start with M3 and GLM, then mainly K3, since it's multimodal and easier to buy than GLM and is really powerful.
Works on Windows and macOS. Designed for study — the editor, importers, and shaders are all small enough to read end-to-end.
- RHI layer - an
RHI::Device / CommandListabstraction with two working backends: OpenGL 3.3+ core profile and Metal (macOS; GLSL cross-compiled to MSL through Slang + SPIRV-Cross, cached in the shader DDC). Vulkan is compiled in but the device is a stub - cook can emit Vulkan shader artifacts, the runtime cannot run them yet. Explicit per-draw state (PSO-shaped) so chain + debug draws never leakGL_BLENDinto unrelated passes. Backend selected per launch via--rhi=/FURY_RHI/Engine.run{ rhi }/ editor ini (seedocs/CLI.md). - C++17 smart pointers,
sol2 + Lua 5.4scripting bridge. - JSON-configurable pipelines — every pass, every shader variant, every uniform lives in a
.jsonnext to the runtime. - Editor (ImGui + ImGuizmo) — Scene Inspector, Content Browser, Node Properties, viewport gizmo, native file dialogs, configurable dock layout, persisted across launches.
- Game UI (RmlUi) — the in-game runtime UI is RmlUi: RML documents (an HTML subset) styled with RCSS (flexbox, absolute positioning, pseudo-classes, transitions, font effects, ninepatch decorators) and driven from Lua via event listeners and data binding. Rendering rides the render thread's frame packet. See docs/GAME_UI.md.
- CLI —
fury convert gltf|fbx|scenetranslates assets to the runtime form (json or LZ4-compressed bin) with--auto-scalefor centimetre-native meshes. - glTF 2.0 importer — vendored
tinygltf, with alpha-mode (OPAQUE/MASK/BLEND) and KHR_materials_transmission mapping. FBX input is chained throughFBX2glTF+ the glTF importer. - HDR rendering pipeline —
RGBA16Fintermediate composite, ACES tonemap, automatic chain ordering. - Data-driven post-process chain — SSAO, SSR, FXAA, CRT, ACES. Effects declare a
stage(pre_tonemap/tonemap/post_tonemap); chain runs in canonical order with auto-tonemap injection. Per-effect uniform overrides via the Settings panel. - Transparent rendering — back-to-front alpha-blended base + per-light additive draws, depth-write off, premultiplied-alpha forward shader.
- Skinned meshes + animations — glTF skinning, joint TRS-driven bone matrices;
Animatorcomponent with state weights / speeds / wraps / cross-fades; Console overlay for joint debugging. - Cascaded shadow maps for the sun, plus standard spot/point shadow maps.
- OcTree spatial with default-cube auto-wrap and Profiler Spatial section.
- LZ4-compressed scene format (
.bin) — the editor's default save form;.jsonavailable for human-readable debugging. - Tracy profiler (CPU zones, optional OpenGL GPU zones via timer queries) — sample live or via
tracy-capture; integrates with the engine's per-frame packet. Vendored; gated byFURY_WITH_TRACY[_GPU]. - Render thread — a dedicated GL thread pulls
FramePacketsnapshots off the game thread; draw-command cache deduplicates identical packets to skip the entire frame. - Kraut vegetation — vendor of
kraut(pinned fork) for billboard-atlas trees + ISM/HISM instancing; grass layer generated from heightmap/splat gates; editor clipper instance list + LOD bucket overlay. - Atmospheric sky — Rayleigh + Mie single-scattering with sun direction; depth fog and horizon blend driven by the sky shader.
- Volumetric clouds — raymarched, wind-scrolled Perlin-Worley noise (generated at load through fragment passes, cached to disk) shaped by a 2D weather map into stratus / cumulus / cumulonimbus profiles, with detail erosion. Marched on a curved-earth shell between the cloud base and top so clouds sink toward the horizon, with a two-LOD step schedule and an early exit on transmittance. Lighting is Beer + Powder in-scatter over 6 cone-sampled sun-transmittance taps and a two-lobe Henyey-Greenstein phase for the silver lining; ambient reuses the march's coarse sample and is tinted by the sky, so clouds follow the time of day. Rendered at half resolution, clipped against scene depth, composited over the sky, and skipped entirely when disabled.
- Heightmap terrain — 16-bit
.r16height fields with chunk LOD + splat-blend shader; CPU-driven physics heightfield collider. - FFT ocean — tiled ring-LOD infinite ocean + CPU-driven FFT wave sampling (Gerstner bands), sky-coupled foam, reflection-via-SSR.
Tank fleet under HDR PBR + cascaded shadow maps, inside the editor. The Scene Inspector tree, the translate gizmo, the MeshRender material slots in Node Properties.
Heightmap terrain with Kraut vegetation - instanced palm groves and a heightmap/splat-gated grass layer - lit by the atmospheric sky. Selecting the Sky node edits its SkyAtmosphere component live: time of day, day length, auto-advance, and whether the sun node follows the TOD; Open Sky Editor... opens the full curve editor.
Khronos Sponza under the HDR prelight pipeline, open in the editor. The canonical chain (SSAO, SSR, ACES, FXAA) runs across its large texture variety, and the Content Browser lists the imported textures as thumbnails.
Skinned mesh + Animator component: skin deformation driven by joint TRS. Ticking Show Joints draws the selected mesh's joint hierarchy as labelled bones over the mesh, which is the fastest way to see which part of the deformation is wrong.
The runtime UI is RmlUi, not the editor's ImGui: documents in RML (an HTML subset) styled with RCSS (a CSS subset with flexbox, pseudo-classes, transitions, and font effects), driven from Lua. See docs/GAME_UI.md.
Every control is RCSS-styled and data-bound to the live engine state - toggling HDR or the shadow-quality preset takes effect on the running scene, no restart.
Tested compilers:
- AppleClang 16 (macOS Darwin 24.x)
- GCC 9+ / Clang 10+ on Linux
- MSVC 16+ (Visual Studio 2019+) on Windows
Should work with any GPU that supports OpenGL 3.3+. The Metal backend (macOS) needs a Metal-capable Mac and builds against the vendored header-only metal-cpp. The Vulkan backend compiles but device creation is not implemented; a --rhi=vulkan launch falls back to OpenGL.
Vendored dependencies (git submodules under engine/ThirdParty/):
- SFML 3.1.0 — window/input/context.
- rapidjson 1.1.0 — JSON serialisation.
- Lua 5.4.7 + sol2 v3.5.0 — scripting bridge.
- tinygltf v2.9.7 — glTF 2.0 loader.
- meshoptimizer — mesh optimisation / simplification / LOD chain.
- nativefiledialog-extended (nfd) — platform-native file dialogs (editor-only).
- STB image, ImGui, ImGuizmo, ImReflect - bundled in-tree.
- metal-cpp, SPIRV-Cross, Slang - Metal RHI bindings and the GLSL -> MSL / SPIR-V cross-compile chain (shader DDC + pak artifacts).
No FBX SDK requirement.
Fury3D uses CMake (≥ 3.22) and vendors every third-party dependency as a git submodule under engine/ThirdParty/. After cloning, initialise the submodules once:
git clone https://github.com/sindney/fury3d
cd fury3d
git submodule update --init --recursive--recursive is required because some submodules have their own submodules (nfd vendors wayland-protocols for its Linux path, which is inert on macOS/Windows). For an existing clone, the same command updates submodules to the pinned commits.
Then configure and build:
# you may need this on windows
git config --global core.longpaths true
cmake -S engine -B build-engine
cmake --build build-engine -jThe fury (runtime) and furye (runtime + editor) executables are emitted directly into examples/ next to Editor.lua and Resource/, so launching is just:
cd examples && ./furye Editor.luaA non-editor build (smaller binary, no ImGui editor windows, no nfd dependency) builds fury only:
cmake -S engine -B build-engine -DWITH_EDITOR=OFF
cmake --build build-engine -j| Option | Default | Effect |
|---|---|---|
WITH_EDITOR |
ON |
Builds the C++ editor shell (Scene Inspector, Content Browser, gizmo, native file dialogs). Requires GUI_IMP=ON. With OFF, only fury is built. |
GUI_IMP |
ON |
Compiles the ImGui overlay (HUD, debug widgets). Required by WITH_EDITOR. |
BUILD_SHARED_LIBS |
OFF |
Builds fury / furye as executables (default). ON produces libfury.{dylib,so,dll} for embedding — the Lua launcher main is then excluded. |
EXPORT_DLL (Windows) |
ON |
Defines FURY_API_EXPORT so the shared lib exports symbols. Ignored for static builds. |
FURY_WITH_TRACY |
ON for Debug / RelWithDebInfo / multi-config, OFF for Release / MinSizeRel |
Instruments the engine with the Tracy profiler (vendored at engine/ThirdParty/tracy). Also builds tracy-profiler + tracy-capture into examples/. See docs/TRACY.md. |
FURY_WITH_TRACY_GPU |
ON when Tracy is on |
Adds OpenGL timer-query GPU zones. Unreliable on Apple/TBDR drivers — turn off per machine if so. |
FURY_RHI_VULKAN |
ON |
Compiles the Vulkan RHI backend. It is a stub: device creation logs "not implemented yet" and the launch falls back to opengl. |
FURY_RHI_METAL |
ON (Apple only, forced OFF elsewhere) |
Compiles the Metal RHI backend (macOS). |
Each submodule under engine/ThirdParty/ is wired the same way: a configure-time presence check that fails with the right git command, then either add_subdirectory(...) (if upstream ships CMake) or a flat file(GLOB) static lib (if it doesn't). Editor-only deps sit behind if(WITH_EDITOR). nfd's macOS backend compiles nfd_cocoa.m, so engine/CMakeLists.txt declares LANGUAGES C CXX OBJC on Apple.
cd examples && ./furye Editor.luaEditor.lua picks the startup scene by argv:
- No argv -> starts with an empty scene; open one via File -> Open... or pass a path.
./furye Editor.lua Projects/ocean/ocean_island.bin-> loads that scene at startup (the literal path, or itsProjects/,Resource/Scene/prefixed variants)../furye Editor.lua --gpu-profiler /tmp/dump-> opens the GPU Capture Profiler on a--gpu-dump /tmp/dumpcapture at startup.
If you only built fury, the launch is the same (./fury Editor.lua) but the ImGui editor windows are absent — drop the .lua to run your own script.
- WASD / arrows — move along the camera's forward / strafe.
- Hold left mouse button + drag — yaw and pitch.
- Mouse wheel — adjust move speed.
- LShift — 5× speed multiplier.
- F2 or right-click → Rename — rename a selected node.
- Double-click a node in the Scene Inspector — frame the camera on that node (matches the framed whole-scene pose used for auto-focus on File→Open).
- File → Open... / Ctrl+O — native single-select open dialog (any engine-loadable format:
.json,.bin,.gltf,.glb,.fbx). On open, the editor auto-scales the import (cm→m), runs auto-focus, and the camera is at the framed pose before the next input event fires. - File → Import... / Ctrl+Shift+I — native multi-select open dialog (merges into the active scene).
- File → Save As... / Ctrl+Shift+S — native OS save dialog.
- Menu bar —
File,Edit,Window, plus the project-emittedCameramenu. - Settings → Editor —
Snap Enabled, snap-step sizes,Auto-Focus on Open(default ON; toggled via theauto_focuseditor setting inimgui.ini). Camera controls (Move Speed, Mouse Sensitivity).
| Window | What it does |
|---|---|
| Scene Inspector | Tree of nodes, drag-drop reparent, leaf double-click frames camera. |
| Viewport | Render surface + toolbar (gizmo mode, debug-view, overlay toggles). |
| Node Properties | Mode switch (Local/World), transform (the root node is locked to identity — non-identity there shrinks the gizmo), MeshRender material slots, Animator state inspector. |
| Content Browser | In-scene tiles (Mesh, Material, Texture, AnimationClip), type filter, fuzzy search, F2 / right-click rename, double-click opens the asset editor. |
| Settings | RenderSettings (HDR toggle, CSM, postprocess chain per-effect enabled + Edit dialog for uniform overrides), Camera controls, import flags (auto-scale, normal gen, mesh weld). |
| Profiler | FPS sparkline, draw-call / triangle counts, cascaded-light shadow map viewer, GBuffer texture previews. |
| GPU Capture Profiler | Views a --gpu-dump <dir> capture: every render-target texture of one frame with min/max/mean stats, PNG previews, and the pass/texture render graph. Open via Window menu or --gpu-profiler <dir>. |
| Console | FURY_INFO/WARN/ERROR log buffer + ad-hoc Lua eval (errors caught inline). |
The same fury binary hosts an offline asset CLI when argv[1] is a known subcommand; no window, no GL context, no Lua VM unless fury exec is used. furye-cli is the headless editor-side sibling (fury/furye/furye-cli roles: see docs/CLI.md).
cd examples
./fury help
./fury convert gltf character.glb character.bin # engine runtime form
./fury convert fbx tank.fbx tank.bin --auto-scale # FBX -> glTF -> engine; auto-scales cm-native meshes
./fury convert scene tank.bin tank.json # round-trip bin <-> json
./fury info tank.bin # CPU-side summary (nodes, meshes, AABB...)
./fury exec tank.bin some-script.lua # headless Lua over a scene
./fury render-mesh tank.bin Tank_005 /tmp/tank.png # single-mesh thumbnailCook + package a scene into a deployable pak (textures -> BCn KTX2 via the vendored ktx CLI, DDC-cached):
./furye-cli cook Projects/ocean/ocean_island.bin # textures -> DDC + cook manifest
./furye-cli package Projects/ocean/ocean_island.bin # -> ocean_island.pak (boot scene + assets)
./furye-cli package Projects/ocean/ocean_island.bin \
--rhi opengl,metal # also cross-compile + pack shader artifacts for those targets
./fury ocean_island.pak # mount + play, or fury exec ocean_island.pak script.luacook also cross-compiles every shader the pipeline can hit (pipeline JSONs, runtime-generated terrain/particle/atmosphere shaders, every postprocess effect's shader) for the --rhi targets (default: host backends) into the same DDC (FURY_DDC env or <exe dir>/DDC; TEX_ texture keys, SHADER_ shader keys). The pak stores the per-backend blobs + reflection sidecars under DDC/ keys, so a Metal launch loads its MSL straight from the pak (Vulkan artifacts cook too, but the Vulkan runtime device is not implemented). tests/pak_pipeline.sh verifies the whole pipeline, including an independent pak unpack/repack/corruption round trip.
Run ./fury help <subcommand> for full flags; see docs/CLI.md for the reference.
- Rapidjson — loading pipeline / scene JSON
- Plog — log implementation
- ThreadPool — threadpool implementation
- Stb_image — image loading
- LZ4 — scene file compression/decompression
- SFML — OS window/input/context
- tinygltf — glTF 2.0 loader
- FBX2glTF — FBX → glTF (precompiled binaries vendored per platform)
- meshoptimizer — mesh optimization + LOD chain
- Lua + sol2 — scripting bridge
- Ogre3d — octree implementation reference
- ImGui + ImGuizmo + ImReflect — editor UI / gizmo / reflected inspector
- nativefiledialog-extended — cross-platform file dialogs
- RenderDoc — OpenGL debugging
- glTF-Sample-Assets - Sample assets
- Kraut — billboard-atlas vegetation pipeline (Kraut-CLI fork)
- Tracy — real-time CPU/GPU profiler
- RmlUi — runtime game UI (RML + RCSS, Lua bindings)





