Skip to content
sindneyPublic

About

A WIP vibe friendly game engine.

Topics

Resources

Stars

92 stars

Watchers

13 watching

Forks

Repository files navigation

Fury3D

中文简体

Introduction

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.

Highlights

  • RHI layer - an RHI::Device / CommandList abstraction 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 leak GL_BLEND into unrelated passes. Backend selected per launch via --rhi= / FURY_RHI / Engine.run{ rhi } / editor ini (see docs/CLI.md).
  • C++17 smart pointers, sol2 + Lua 5.4 scripting bridge.
  • JSON-configurable pipelines — every pass, every shader variant, every uniform lives in a .json next 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|scene translates assets to the runtime form (json or LZ4-compressed bin) with --auto-scale for centimetre-native meshes.
  • glTF 2.0 importer — vendored tinygltf, with alpha-mode (OPAQUE/MASK/BLEND) and KHR_materials_transmission mapping. FBX input is chained through FBX2glTF + the glTF importer.
  • HDR rendering pipeline — RGBA16F intermediate 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; Animator component 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; .json available 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 by FURY_WITH_TRACY[_GPU].
  • Render thread — a dedicated GL thread pulls FramePacket snapshots 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 .r16 height 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.

Screenshots

Tank fleet in the editor viewport with a translate gizmo on the selected tank; Node Properties shows the MeshRender material slots, Scene Inspector and Content Browser docked around it.

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.

Ocean island jungle: instanced palms and a grass field under the atmospheric sky; the Sky node is selected and the SkyAtmosphere inspector (Time of Day, Day Length, Auto Advance, Sun from TOD) is on the right.

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.

Sponza in the editor viewport with the Content Browser showing the scene's texture tiles below and Node Properties on the right.

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.

James skinned character with the Show Joints skeleton overlay (joints labelled) and the Animator component inspector on the right (states, Bind Clip, animate physics).

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.

In-game pause menu (RmlUi) over the running scene: CONTINUE / OPTIONS / EXIT TO MAIN MENU / EXIT TO DESKTOP, with the world dimmed and blurred behind it.

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.

In-game OPTIONS screen (RmlUi): resolution, FPS cap, vsync, field of view, per-effect post-processing toggles, shadow quality, ocean SSR, HDR, and control options.

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.

Compatibility

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.

Building

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 -j

The 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.lua

A 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

Build options

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).

Adding a vendored dependency

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.

Run the demo / editor

cd examples && ./furye Editor.lua

Editor.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 its Projects/, Resource/Scene/ prefixed variants).
  • ./furye Editor.lua --gpu-profiler /tmp/dump -> opens the GPU Capture Profiler on a --gpu-dump /tmp/dump capture 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.

Controls

  • 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-emitted Camera menu.
  • Settings → Editor — Snap Enabled, snap-step sizes, Auto-Focus on Open (default ON; toggled via the auto_focus editor setting in imgui.ini). Camera controls (Move Speed, Mouse Sensitivity).

Editor windows

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).

CLI

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 thumbnail

Cook + 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.lua

cook 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.

Special thanks

About

A WIP vibe friendly game engine.

Topics

Resources

Stars

92 stars

Watchers

13 watching

Forks

Releases

Contributors

Languages