# Oxide Engine — Long-Term Development Plan Each stage must be completed and signed off (manual + automated testing) before the next begins. Stages are designed so that each one produces a usable, standalone artifact that later stages build upon. The **in-engine editor** (`oxide-editor`) is a first-class deliverable. It is developed in parallel with the engine: each stage adds editor support for the systems introduced in that stage. The editor crate lives at `editor/` and is always buildable alongside the engine. The project must remain installable as a Linux package at all times. `install.sh` builds a release binary and installs it to the system. Keep it current whenever new binaries or assets are added. --- ## Development Phases Oxide is built in two phases: - **Phase 1 — The general-purpose engine (Stages 0–16).** Everything needed to build *and ship* any 3D game: core framework, editor + project system, input, UI, physics, scripting, animation, particles, shaders, audio, a content kit, and game export to Linux + Windows. Stage 16 is the **milestone**: a complete, general-purpose engine that can build and export real games. - **Phase 2 — Built-in modules (Stages 17+).** Optional, self-contained capabilities — ray-traced audio, a developer console, a procedural toolkit, terrain, open-world streaming, pathfinding/AI, water — each shipped as a feature-gated **module** built entirely on Phase-1 systems. Phase 2 is also the proof the module API holds: adding a capability should mean *writing a module*, not surgery on the core. The headline framing is simply **a general-purpose 3D engine that scales from stylized low-poly to realistic, shipping only what each game uses.** Simulation, open world, and procedural generation are *capabilities the engine supports through modules*, not design drivers — the goal is to **build tools, not games**. --- ## Platform & Target Strategy These are project-wide constraints that span every stage, not a single deliverable: - **Primary platform is Linux**, and Linux means **both Wayland and Xorg (X11)** — the editor and any engine app must run on either without code changes. This is the default and must stay working at every stage (verified via `winit`'s `wayland` + `x11` backends, both enabled). - **Windows support comes later** — once enough of the engine exists to be worth porting, a compiled Windows build of the editor/engine is added. Code should avoid Linux-only assumptions so this port stays cheap; `wgpu`/`winit` already abstract the platform layer. - **Game export targets both Linux and Windows binaries.** A project built in the editor must be exportable to standalone runnable binaries for both platforms (see Stage 16). The editor and the exported game share the same engine runtime. ## Cross-Cutting Principles (apply to every stage) - **Build tools, not games.** The engine ships composable building blocks (e.g. noise + modifiers, not a "make me a world" button). Genre-specific behavior belongs in game code or optional modules. - **Ship only what's used.** Subsystems are **feature-gated modules**; an exported game compiles in only the modules it registers. The same engine produces a tiny stylized game and a heavy realistic one, differing only in which modules are present. - **Scalable fidelity.** The renderer is a **data-driven pass pipeline** so a project can run a flat unlit/low-poly look (or a stylized post effect like a VCR filter) or a full realistic stack with shadows, SSAO, and bloom — paying only for the passes it enables. - **Modules are the primary extension point.** A module registers engine logic (systems, components, asset loaders) **and** editor integration (panels, menu items, tools, inspectors) **and** its own settings, through one documented API. Anyone — including AI agents — can write a module. Native modules are Rust crates implementing the `Module` trait; lightweight modules can be `rhai` scripts. - **Editor- and script-editable types.** Engine component types (e.g. `Transform`, `Node`, `Script`, materials, colliders) must be editable **both** from the editor UI **and** from scripts/code, through the same reflected/serializable representation. From Stage 5 this is backed by a reflection/ type registry, so any editor — or an **AI agent in the editor terminal** — can create and edit game code and scene data in real time. New component types are added with this dual-editability built in. - **Live reload by default.** Game scripts/assets are watched on disk; changes are picked up and applied to the running editor without a restart (watcher foundation in Stage 6, full script reload in Stage 10). Assets added earlier keep their loaders reload-friendly. - **The editor grows every stage.** Each stage adds editor support for its systems through the Stage-6 editor framework: a top menu, dockable panels, an undo/redo command stack, a module-driven extension API, and a **settings/preferences framework** that every system contributes pages to (engine prefs, editor prefs, input bindings, per-module settings, enable/disable modules). - **Choice of subsystems.** Where the engine offers an advanced system it also offers a standard one so each project can choose its trade-off — most concretely **audio**: a standard mixer/spatial system (Stage 14, core) *and* a ray-traced propagation module (Stage 17), sharing components. - **Comprehensive, not minimal, gameplay systems.** Physics, input, and the UI are first-class, full systems (Stages 7, 8 & 9), not thin wrappers — they must cover the cases real games need. - **Document as you build, including how to extend.** A stage is not complete until its systems are documented under `docs/`. Every module ships documentation for **both** how to *use* it **and** how to *create* one like it (an authoring guide), so the module ecosystem is approachable. --- ## Status | Stage | Title | State | |-------|-------|-------| | 0 | Project Foundation | ✅ Complete | | 1 | Math & Core Primitives | ✅ Complete | | 2 | Window & Render Context | ✅ Complete | | 3 | Scene Graph & Entity System | ✅ Complete | | 4 | Basic 3D Rendering | ✅ Complete | | **Phase 1 — General-purpose engine** | | | | 5 | Engine Core Framework (modules, layers, assets, reflection, render graph) | ✅ Complete | | 6 | Editor Framework & Project System | ✅ Complete | | 7 | Input System (mapping + remappable actions) + gizmos | ✅ Complete | | 8 | Comprehensive UI System | ✅ Complete (pieces 1–8 on `main`; the editor UI canvas — piece 9 — shipped as Stage 8.5 piece 7) | | 8.5 | Reflection v2, Editor Redesign & Asset Database (foundation) | ✅ Complete — every component type is editor- and script-editable with no per-type code; asset database + UI canvas on `main` | | 8.7 | Editor Play Mode (Play/Pause/Step/Stop) | ✅ Complete on `main` — snapshot/restore + play-state model, `App::step`/`tick_for`, toolbar + Ctrl+P/Ctrl+. + viewport tint, scene-swap host runner (eye-checked & approved 2026-06-16) | | 9 | Physics Integration (comprehensive) | ✅ Complete on `main` — full sim/events/queries/joints/character controller + 2 examples + editor types/play-loop/collider gizmos + raycast debug probe (freeze-on-click; eye-checked & approved 2026-06-17) | | 10 | Scripting, Live Reload & Editor Terminal | 🚧 Core done on `main` (eye-checked & approved 2026-06-17): `oxide-script` crate (`Script`, `.rhai` loader, sandboxed `ScriptEngine`, `ScriptModule`) + lifecycle (`init`/`update(dt)` via `ScriptHost`) + `Vec3`/transform engine API + **live reload** (`examples/script_spin`) + **editor integration** (`Script` addable; play-loop shares asset server/db → live `.rhai` edits update a *playing* scene) + **Console** (captures the `log` stream — script `print`/errors) + **command terminal** ($ → `sh -c`) + **interactive PTY terminal** (`portable-pty`+`vt100`, tabbed, auto-close on exit, Tab/arrows/Esc routed; runs shells/TUIs/`claude`). **Remaining:** richer script API (spawn/despawn + component add/edit beyond `Transform`); then the editor-UX batch (file explorer, native New/Open dialog, New-Script button, open-script-in-editor) | | 11 | Animation System | ⬜ Planned | | 12 | Particle System | ⬜ Planned | | 13 | Shader System & Advanced Graphics | ⬜ Planned | | 14 | Standard Audio System | ⬜ Planned | | 15 | Engine Content & Prototyping Kit | ⬜ Planned | | 16 | Windows Support & Game Export | ⬜ Planned — **Phase 1 milestone** | | **Phase 2 — Built-in modules** | | | | 17 | Ray-Traced Spatial Audio (module) | ⬜ Planned | | 18 | Developer Console & Cheats (module) | ⬜ Planned | | 19 | Procedural Toolkit (module) | ⬜ Planned | | 20 | Terrain System (module) | ⬜ Planned | | 21 | Open World Support (module) | ⬜ Planned | | 22 | Pathfinding & NPC AI (module) | ⬜ Planned | | 23 | Water System (module) | ⬜ Planned | > Backlog (post-roadmap capabilities and additional modules) is at the end of this file. --- ## Stage 0 — Project Foundation ✅ **Goal:** A buildable, testable Rust project with core structure in place. ### Deliverables - Cargo workspace layout (`engine/`, `editor/`, `examples/`, `tests/`) - Basic logging and error handling infrastructure - CI-ready: `cargo build`, `cargo test`, `cargo clippy`, `cargo fmt` all pass - Empty integration test harness - `README.md` with engine description, build instructions, and install instructions - `install.sh` — builds release binaries and installs to `/usr/local` (or `$PREFIX`) - Editor stub: `oxide-editor` binary that starts, logs, and exits cleanly ### Test Criteria - `cargo build` succeeds from clean checkout - `cargo test` runs (even with zero tests) without error - Clippy reports zero warnings with `deny(warnings)` - `install.sh` completes without errors on a clean Linux system - `oxide-editor` binary runs after installation --- ## Stage 1 — Math & Core Primitives ✅ **Goal:** Solid math foundation every other system will depend on. ### Deliverables - Choose and integrate math library (`glam` preferred) — done (`glam` with `serde`) - `Transform` type: position, rotation (quaternion), scale — `engine/src/math/transform.rs` - `AABB`, `Ray`, `Plane`, `Frustum` types — `engine/src/math/{aabb,ray,plane,frustum}.rs` - Common utility types: `Color`, `Rect`, `Range3` — `engine/src/math/{color,rect,range3}.rs` - Unit tests for all types (edge cases: zero scale, gimbal lock paths, etc.) — 69 unit tests - **Editor:** no visible UI yet; math types available via `oxide_engine::math` / `prelude` ### Test Criteria - 100% unit test coverage on all math types — ✅ every type has a `tests` module - Fuzz transform composition (random chains should remain stable) — ✅ `tests/` `stage1::fuzz_transform_chains_stay_stable` - Benchmark: 1M transform multiplications under 10ms — ✅ ~5.1ms (`cargo bench -p oxide-engine`) ### Notes - `Transform` stores decomposed TRS; composition takes an exact fast path for uniform scale and re-decomposes from the matrix for the non-uniform case. - `Range3` is a *value* range (clamp/lerp/remap), distinct from `Aabb` (geometry). - Runnable example: `cargo run -p oxide-examples --bin math_demo`. --- ## Stage 2 — Window & Render Context ✅ **Goal:** A window opens, GPU context is acquired, a colored screen is displayed. ### Deliverables - `winit` integration for window creation and event loop — `engine/src/window/` (`App` trait, `AppCtx`, `WindowConfig`, `run`) - `wgpu` device/queue/surface setup — `engine/src/render/` (`Gpu`, `RenderContext`) - Clear-color render loop (configurable clear color) — `render::clear_view`, `AppCtx::set_clear_color` - Resize handling — `RenderContext::resize`, driven by the event loop; minimized windows clamp to 1×1 - Input event plumbing (keyboard, mouse — raw events only, no abstraction yet) — every `WindowEvent` forwarded to `App::event`; types re-exported via `window::event` - **Editor:** opens its own window using this infrastructure; displays a placeholder viewport (dark clear, raw input logged, `Ctrl+Q` quits) ### Test Criteria - Window opens on Linux without errors — ✅ manual sign-off (2026-06-11, Wayland, RADV/Vulkan) - Resize does not crash or produce artifacts — ✅ manual sign-off - Clear color changes are reflected immediately — ✅ manual sign-off (`hello_window` keys 1–5/Space) - Manual: run the `hello_window` example and confirm stable 60+ FPS on minimal GPU — ✅ ~3700 FPS (RX 9070 XT, Mailbox present mode) - Automated: headless GPU clear verified by pixel readback — ✅ `tests/` `stage2::headless_clear_fills_texture_with_clear_color` ### Notes - Docs: `docs/windowing.md`, `docs/render-context.md`. - `wgpu`/`winit` are re-exported (`oxide_engine::wgpu` / `::winit`) so consumers don't need to version-match their own copies. - egui editor panels intentionally deferred to Stage 3 (Stage 2 editor scope is the placeholder viewport only). --- ## Stage 3 — Scene Graph & Entity System ✅ **Goal:** Entities with transforms exist in a hierarchy; scene can be queried and mutated. ### Deliverables - Choose and integrate ECS — done (`hecs`, re-exported as `oxide_engine::hecs`) - `Scene` type: add/remove/query entities — `engine/src/scene/graph.rs` - `Transform` hierarchy (parent-child with world-space resolution) — `Scene::world_transform` / `world_transforms` - `Node` component: name, enabled flag — `engine/src/scene/node.rs` - Scene serialization/deserialization (RON) — `Scene::to_ron` / `from_ron`, stable index-based format - `examples/scene_basic`: create a hierarchy, print world transforms — done - **Editor:** scene hierarchy panel; select, rename, enable/disable, reparent nodes — done (egui Hierarchy + Inspector panels) ### Test Criteria - Parent transform propagates correctly to children — ✅ unit + `tests/` `stage3` - Removing a parent removes or detaches children (configurable) — ✅ `DespawnPolicy::{Recursive, DetachChildren}` - Serialize → deserialize round-trip produces identical scene — ✅ byte-identical re-serialization - 10,000 entity scene with 5-level deep hierarchy resolves in under 1ms — ✅ ~0.6ms (`cargo bench -p oxide-engine --bench scene`) - Manual: editor hierarchy/inspector panel verified by maintainer (2026-06-11, Wayland, RADV/Vulkan) ### Notes - Docs: `docs/scene.md`. - The hierarchy is owned by `Scene` (ordered `roots`/`children`), not stored as components, for deterministic ordering and cheap reparenting; the ECS still owns all entity data, so later stages attach their own components. - egui is an **editor-only** dependency: the engine exposes a generic post-clear draw hook (`App::render(&RenderCtx)`) and the editor's `EguiLayer` draws through it, keeping egui out of the engine's dependency tree. --- ## Stage 4 — Basic 3D Rendering ✅ **Goal:** Meshes are loaded and rendered with a simple material system. ### Deliverables - `Mesh` type: vertex buffer (position, normal, UV), index buffer - GLTF loader (static meshes only, no animation yet) - Forward renderer: depth buffer, basic lit pass - `Material` type: albedo color/texture, roughness, metallic (PBR-lite) - Camera component with perspective projection - `examples/hello_mesh`: load a GLTF cube/sphere and render it - **Editor:** 3D viewport renders the active scene; camera orbit/pan/zoom controls; material inspector panel ### Test Criteria - GLTF primitives (cube, sphere, plane) render correctly - Depth ordering is correct (no z-fighting on simple scenes) - Camera movement updates the view matrix correctly - Manual: inspect rendered output for correct lighting on a sphere — ✅ maintainer sign-off (2026-06-12) ### Notes - Docs: `docs/rendering.md`. - The forward renderer has automatic render-backend fallback (Vulkan/Metal/DX12 → GL → software) so it runs on a wide range of GPUs. - The hardcoded forward pass from this stage is generalized into a data-driven **render pass pipeline** in Stage 5 — later passes (shadows, post-process, overlay UI) compose into it rather than rewriting the renderer. --- ## Stage 5 — Engine Core Framework **Goal:** The structural "spine" every later system plugs into: a module system, scene/object **layers & tags**, a central **asset server**, a minimal **reflection/type registry**, and a **data-driven render pass pipeline**. Built now, while the engine is small, so later stages add capabilities without rewriting the core. ### Deliverables - **Module system + app builder.** An `App`/`Engine` assembled by registering `Module`s. A `Module::build(&mut App)` hook registers: update systems (with an explicit ordering/schedule), component types, asset loaders, and (via Stage 6) editor integration. `DefaultModules` bundles the built-in set. Math/window/render/scene are wrapped as built-in modules; subsystems from Stage 9 on are built as **their own feature-gated crates** (`oxide-physics`, `oxide-audio`, …) so an exported game compiles in only what it registers. - **System scheduling.** A simple, deterministic ordered schedule (e.g. `Input → PreUpdate → Update → PostUpdate → Render`) that modules attach systems to, with a fixed-timestep slot reserved for Stage 9 physics. - **Layer & Tags.** - **`LayerMask`** — a 32-slot bitmask with a **project-level named-layer registry**. One shared definition consumed by physics collision groups + sensor filtering (Stage 9), **camera visibility masks** (which layers a camera renders — first-person view models, editor-only gizmo layers, minimap cameras), and **scene/raycast query filtering**, plus editor per-layer show/hide/lock. - **Groups/tags** — the *multi-valued* counterpart to the *single-valued* `Layer` (Unity model: an entity is on one layer but in any number of groups). Membership lives in the `Tags` component; the project's valid group names live in a `GroupRegistry` so the editor offers a fixed vocabulary to pick from. Separate from the hot-path `LayerMask`. - **Asset server + handles.** A central registry returning typed, ref-counted **handles** (`Handle`, `Handle`, …) with async loading and a pluggable loader registry. Stage 4's GLTF/texture loading is migrated onto it. This one system underpins live reload (Stage 10), streaming (Stage 21), and export packing (Stage 16), so it lands before more loaders accrue. - **Reflection / type registry (minimal).** Register a component type once and get serialization (serde/RON), a generic editor inspector (Stage 6), and script access (Stage 10) — the backbone of the *dual-editable types* principle. Existing hand-written inspectors are migrated as the registry matures; every component type from Stage 7 on is dual-editable by construction. - **Data-driven render pass pipeline.** Generalize Stage 4's forward pass into an ordered, composable list of passes (depth, lit, transparent, post-process, overlay) with shared render targets. A project/module enables only the passes it needs — this is the mechanism behind *scalable fidelity* (a flat low-poly look vs. a full realistic stack) and the insertion point for Stage 13's post-process effects and Stage 8's UI overlay. - **Editor:** no new dedicated panel beyond wiring — the hierarchy/inspector now read the reflection registry, the viewport gains a layer visibility toggle, and the groundwork is laid for Stage 6's module/extension framework. ### Delivered (all on `main`) - ✅ **Layers, groups & tags** (`oxide_engine::layer`) — `LayerMask`, `LayerRegistry`, single-valued `Layer`, multi-valued `Tags` + `GroupRegistry`. - ✅ **Reflection/type registry** (`oxide_engine::reflect`) — generic name-keyed component access. - ✅ **Asset server + handles** (`oxide_engine::asset`) — ref-counted `Handle`, dedup, loaders, background load, in-place reload; glTF migrated onto it. - ✅ **Module system + scheduling** (`oxide_engine::app`) — `App` + `Module` + `Schedule` (phases incl. fixed-timestep), enable/disable/remove; `CoreModule`/`RenderModule`/`DefaultModules`. - ✅ **Data-driven render pass pipeline** (`oxide_engine::render::RenderPipeline`) — composable `RenderPass` list, `ClearPass`/`ForwardPass`, camera `LayerMask` visibility; editor viewport + `hello_mesh` retrofitted. Maintainer manual sign-off 2026-06-12 (viewport + `hello_mesh` render identically). > **Known tech-debt (tracked):** there are now two `App` types — the engine container > `oxide_engine::app::App` and the windowing `oxide_engine::window::App` trait (the per-window event > handler). To avoid the clash the core `App` is intentionally **not** in the prelude (import it as > `oxide_engine::app::App`); `window::App` stays in the prelude. Resolve by renaming the windowing > trait (e.g. `WindowApp`/`AppHandler`) when the editor adopts the core `App` loop in Stage 6/7 — a > GUI-affecting change best done with a manual-test pass, not in isolation. ### Test Criteria - An app composed purely by registering modules runs; removing a module removes its systems, components, and assets with no dangling references - A feature-gated module excluded at compile time produces a binary with none of its code - `LayerMask` filtering is correct and shared: the same named layer drives a render visibility mask and (stubbed) a query filter identically - The asset server returns ref-counted handles; loading the same asset twice yields one resource; dropping all handles frees it - A component registered with the reflection registry round-trips through RON and is read/written generically (proving the inspector/script path) without type-specific code - The render pipeline runs Stage 4's scene through the new pass list with identical output, and a pass can be added/removed without touching the renderer core - Benchmark: module/system scheduling overhead is negligible vs. the Stage 4 hardcoded loop --- ## Stage 6 — Editor Framework & Project System ✅ **Goal:** Turn the editor from a fixed set of panels into a proper, extensible application shell with a **project system** — so every later stage (and every third-party module) plugs UI, tools, and settings into a consistent host, and real projects can be created, opened, and saved. ### Deliverables - **Editor shell.** A top **menu bar** (File / Edit / View / Project / Window / Help / module menus), a **dockable/resizable panel system**, status bar, and a registry of panels modules can add to. - **Command / undo-redo stack.** A central, editor-wide command stack so every tool (transform gizmos, inspector edits, and later sculpt/paint/scatter) is undoable/redoable through one mechanism — landed here so all subsequent editor tools are undoable for free. - **Module → editor extension API.** The Stage-5 `Module` gains an editor hook to contribute: menu items, dockable panels, viewport tools/gizmos, component inspectors (via the reflection registry), and settings pages. This is *the* mechanism by which "anyone can write a module" that extends both engine logic and the editor. - **Settings / preferences framework.** A unified, serialized settings system with pages contributed by the engine, the editor, and each module: - **Editor preferences** (theme, layout, viewport, shortcuts) and **engine preferences** (render/quality defaults) — grown incrementally as later stages add their options. - **Module management:** list installed modules, **enable/disable** them, and edit each module's own settings page. - Input bindings get their settings page here once Stage 7 lands. - **Project system.** Create / open / save a **project**: a project file (RON) plus a defined asset/ scene/script folder layout, a list of enabled modules and their settings, and per-project preferences. A recent-projects list and a "new project" flow. Scenes load/save within a project. - **File-watcher foundation.** Watch the project's asset/scene folders (`notify`) and reload changed assets live — the groundwork for Stage 10's full script hot-reload and the "edit with any external editor or AI agent" workflow. - **Editor:** the existing hierarchy/inspector/viewport are reparented into the new docking shell; a Project panel/asset browser; a Preferences window with the settings pages above. - **Resolve the Stage-5 `App` naming clash.** As the editor adopts the engine's core `oxide_engine::app::App` loop here, rename the windowing `window::App` trait (e.g. `WindowApp`) so the core `App` can live in the prelude unambiguously (see the Stage-5 tech-debt note). ### Test Criteria - Panels can be docked, undocked, resized, and restored across restarts (layout persists) - A trivial test module adds a menu item, a panel, and a settings page purely through the extension API — with no edits to editor core - Undo/redo correctly reverses and re-applies a sequence of edits (e.g. rename, reparent) through the single command stack - Creating, saving, closing, and reopening a project restores its scenes, enabled modules, and settings unchanged (RON round-trip) - Enabling/disabling a module in settings adds/removes its systems and editor contributions live - Editing a watched asset file on disk updates the running editor without a restart - Manual: maintainer creates a project, rearranges the layout, toggles a module, and reopens it --- ## Stage 7 — Input System (mapping + remappable actions) + gizmos ✅ **Goal:** A comprehensive input abstraction used by all higher-level systems, with full per-key edge detection and a remappable named-action layer — and the editor's viewport transform gizmos, built on the Stage-6 command stack. **Status (2026-06-14):** ✅ Complete. Shipped in six pieces — `InputState` (1), `ActionMap` / `Binding` (2), axis + 2D-axis actions (3), editor flythrough camera (4), input-bindings preferences page with disk persistence (5), gizmo math + viewport tab sizing + viewport gizmos (6a/6b/6c). Final maintainer test surfaced three issues (snap mid-drag, uniform-scale sensitivity, sphere winding) — all fixed in the same stage. Gamepad bindings are deferred ("where available" in the deliverables); the rest is in. ### Deliverables - `InputState`: keyboard, mouse button, mouse delta, scroll, gamepad (where available) - **Per-key edge + state queries** for any physical key/button: `pressed` (this frame), `released` (this frame), and `held`/`down` (current state) — i.e. map any key to its press, up, and down events. - **Named-action mapping with defaults and remapping.** Bind named actions (e.g. `"Jump"`) to one or more physical inputs, each action carrying a **default** binding (e.g. `Space`). Game code queries only the action name (`input.action_pressed("Jump")`) and never the physical key, so a project's settings screen can remap `"Jump"` to any other key without touching game code. - **Bindings persistence**: action maps load/save (RON) so game settings can store user remaps, surfaced through the Stage-6 settings framework. - Input consumed/propagated model (UI/editor can consume before game) - Axis/2D-axis actions (e.g. movement) composed from keys and/or gamepad sticks - `examples/input_debug`: print all actions and raw input each frame; `examples/input_remap`: rebind an action at runtime and show game code unaffected - **Editor:** input bindings editor page (assign/clear/remap action bindings, restore defaults) in the settings framework; live input-state debug overlay; **viewport flythrough camera** (WASD/QE move, mouse-look, with Shift to accelerate) layered on top of the existing orbit/pan/zoom controls - **Editor transform gizmos:** on-screen manipulators for the selected entity — **translate** (axis arrows + plane handles), **rotate** (axis circles), and **scale** (axis/uniform handles) — dragged directly in the viewport, every edit recorded on the Stage-6 undo stack. Includes a **snap system** with configurable increments (grid distance, angle step, scale step) and a modifier key to toggle snapping; switch active tool with hotkeys (e.g. W/E/R). With gizmos in place, the inspector's transform fields become **directly type-editable** (click to type an exact value), not drag-only. ### Test Criteria - `pressed`/`released` fire exactly once on the correct frame; `held` reflects current state - Dragging each gizmo axis moves/rotates/scales the selection on exactly that axis; snapping constrains the result to the configured increment; typed inspector values apply exactly; each is undoable/redoable through the command stack - Action bindings can be changed at runtime and persisted/restored via RON - Multiple keys can be bound to one action; one key can drive multiple actions - Remapping an action changes behavior while game code (querying the action name) is unchanged - Mouse delta is correct across frame boundaries (no jump on first frame) --- ## Stage 8 — Comprehensive UI System **Status (2026-06-15): 🟡 In progress.** Pieces 1–7 complete and on `main`: 1. ✅ Widget tree + layout (`oxide_engine::ui::{widget, style, layout}`). 2. ✅ Styling & theming (`visual`, `theme`; cascade default → named → per-instance). 3. ✅ Text shaping + glyph atlas (`text/{font, atlas, shape}` via `ab_glyph`). 4. ✅ 2D overlay render pass — screen-space (4a) + world-space `UiPanel` (4b). 5. ✅ Input routing (`routing::Router`; hit-test + hover / press / focus state machine). 6. ✅ Events + data binding (immediate-mode `RouterFrame::clicked_left(...)` etc. + typed `WidgetValue`). 7. ✅ `examples/ui_menu` — themed main menu + draggable slider + checkbox settings. Remaining for sign-off: 8. ✅ `examples/ui_hud` (health / ammo / minimap overlay + centre crosshair, composited over the Stage-4 forward pass) — signed off on `main` 2026-06-15 after maintainer eye-check. The atlas cache-hit claim it demonstrates is also covered by an automated GPU test (`render::ui_pass::atlas_caches_glyphs_and_reaches_steady_state`). 9. ⬜ Editor **UI canvas** — visual document builder. **Goal:** A first-class, **in-game** UI system — widgets, layout, styling, text, and input routing — that ships inside exported games. This is distinct from the editor's `egui` (which stays editor-only): this UI is an engine system a project uses to build its menus, HUDs, and tools, and it consumes input through the Stage 7 model and renders through the Stage 5 pass pipeline. ### Deliverables - **Widget tree** owned by the engine and renderable in shipped games: containers/panels, label/ text, button, image, checkbox, radio, slider, drop-down, text input, progress bar, and a scrollable list/view - **Layout system**: stack (row/column), grid, and anchor/dock layouts; padding/margin, alignment, and sizing modes (fixed / grow / fit-content); resolution- and **DPI-aware** scaling - **Styling & theming**: per-widget styles plus reusable themes (colors, fonts, spacing, borders), overridable per instance - **Text rendering**: TTF font loading, a glyph atlas, alignment, wrapping, and multi-font support - **Input integration**: UI consumes input *before* the game using Stage 7's consume/propagate model; hover/focus/press states; keyboard/gamepad focus navigation. (The Stage 7 bindings/remap settings screen is itself buildable with this UI system.) - **Events & data binding**: widget callbacks/events and binding widget values to game data - **2D overlay pass** integrated as a Stage-5 render pass (batched draws), plus optional **world-space UI** (a UI panel rendered on a quad in 3D) - **Serializable, dual-editable UI documents** (RON): layouts round-trip through the engine's representation so they are authored in the editor *and* editable from scripts/AI agents - `examples/ui_menu` (main menu + settings, including action remapping) and `examples/ui_hud` (health/ammo/minimap overlay) - **Editor:** a visual **UI canvas** to build and arrange UI documents — widget palette, drag/resize, property inspector, and live preview — saving the same RON the runtime loads ### Test Criteria - Widgets render and lay out correctly across window sizes and DPI scale factors - Input routing is correct: UI consumes clicks/keys over its widgets, and the game receives input only where the UI does not consume it - Hover/focus/press states and keyboard/gamepad focus navigation behave correctly - Text renders crisply with correct wrapping and alignment; the atlas handles large glyph sets - A UI document round-trips through RON unchanged (proving dual-editability) - Manual: build a menu in the editor's UI canvas, run an example, and interact with it (including remapping an action from the settings screen) --- ## Stage 8.5 — Reflection v2, Editor Redesign & Asset Database (foundation) **Status: ✅ Complete (2026-06-16).** All pieces (1–7) on `main`; piece 7c (UI canvas) eye-checked & approved. Inserted before Stage 9 (and before Stage 8's deferred piece 9) because every later stage adds component types (colliders, animation clips, particle emitters, audio sources, …) and each one would otherwise need a hand-written editor inspector and hand-wired add/remove plumbing. Doing this **now** was the cheapest it would ever be — it stops that per-type cost from accruing across Stages 9–16. > **Known minor follow-up (UI canvas):** egui logs `Widget rect … changed id between passes` while > interacting with the canvas — id instability from dynamically-sized property widgets across egui's > two-pass layout. Harmless (egui recovers each frame) but noisy; quiet it by scoping the canvas's > per-widget sections under stable `ui.push_id(...)`s. Low priority. **Why:** Stage 5 shipped a *whole-value* reflection registry (`oxide_engine::reflect` round-trips a whole component as RON). The editor inspector, however, is still **hand-coded per type** (`transform_inspector`, `mesh_inspector` in `editor/src/shell.rs`), and component add/remove is bespoke per type (`PendingAction::AddMesh/RemoveMesh`). This finishes the project's **dual-editable types** principle: a component's public fields should surface automatically in the inspector and over scripts (Stage 10), the Unity/Godot model — *public mutable fields appear as editable inspector fields with no per-type code.* ### Component model — node-baked vs modular (2026-06-15 decision) The maintainer surfaced a distinction the editor must honor explicitly: - **Node-baked (essential):** every entity inherently carries them — they're part of *being a node*, not a feature the user adds. Today: `Node`, `Transform`, `Layer` (auto-attached on `Scene::spawn`). Always present, single-instance, rendered in a fixed canonical order, **no** enable checkbox, **no** remove button, **no** drag-reorder handle, **not** offered by the Add Component menu. - **Modular:** the user's choice — `MeshRenderer`, `Camera`, `DirectionalLight`, future `RigidBody`, particle emitters, audio sources, custom scripts. Addable from the registry, removable, disable-able, reorderable. The editor enforces this with `ESSENTIAL_COMPONENTS = ["Node", "Transform", "Layer"]` (see `editor/src/shell.rs::is_essential_component`). Future node-baked additions extend that list; modular ones just register through `register_addable`. **Layers & groups in the inspector (2026-06-16 decision — Unity model "A").** The single-valued `Layer` renders as a single-select dropdown (with a Layer Names editor); gameplay **Groups** (multi-valued, backed by `Tags` + a `GroupRegistry`) render as a multi-select dropdown (with a Groups editor). Both sit in the node-baked section above the modular list. An archetypal ECS allows only **one component of a given type per entity**, so a second mesh/collider/etc. lives on a **child** entity (the Add Component menu's "as child" path); several *distinct* addable types (`MeshRenderer`, `Camera`, `DirectionalLight`) is what makes multi-component nodes and drag-reorder exercisable. **Multi-component-of-same-type** (e.g. two colliders, two meshes) is not supported by archetypal ECS (`hecs` allows one per type per entity) and **won't be lifted in Oxide**. The two principled patterns the engine offers: - **Child entities** (Bevy-style): each "additional" piece is its own entity, parented to the owner. Composes with the existing hierarchy + drag-drop reparent + per-child enable/disable. - **Submeshes / multi-slot components** (Unity-style): a single component wraps a list internally (e.g. a `Mesh` asset can have multiple submeshes with their own material slots). Stage 9 (Physics) picks for colliders; Stage 13 (Shaders) may revisit for meshes. The Add Component menu surfaces this clearly when a user tries to add a duplicate ("Add Child with …" affordance — TODO in piece 4 polish). ### Deliverables - **Reflection v2 — per-field reflection + a `#[derive(Reflect)]` macro.** A new `oxide-engine-derive` proc-macro crate generates, for a struct's public fields, a field table (name + type + typed get/set) re-exported from `oxide_engine`. `reflect.rs` grows from whole-value to field-level access while keeping the existing name-keyed registry API. Pure logic, fully unit-testable. - **Generic reflection-driven inspector.** One inspector that walks a selected entity's registered components and renders a widget per field by type (`f32`→drag, `bool`→checkbox, `Color`→picker, `Vec3`→vec3 drag, enum→combo, `Handle`→asset picker), falling back to a RON text field for unknown types. The hand-written `transform_inspector` / `mesh_inspector` are **deleted** and proven equivalent. All edits route through the existing undo/redo command stack. - **Editor redesign items.** Hierarchy **right-click context menu** (Add Child / Add prefab / Rename / Duplicate / Delete); inspector **"Add Component ▾"** menu enumerated from the type registry; remove-component control per component. (The deferred Stage-8 piece 9 — the visual UI canvas — lands on top of this redesign so its property inspector is reflection-driven, not bespoke.) - **Prefabs / archetypes (lightweight).** A named "spawn a thing that already carries these components" entry so the hierarchy add-menu is data-driven (an entity is its component set; this is not a parallel "object type" system). - **Asset database + typed project folders.** `assets/` gains typed subfolders (`fonts/`, `textures/`, `models/`, `audio/`, `ui/`); an asset database maps **project-relative paths ↔ stable ids ↔ `Handle`** so scenes and UI documents reference `"fonts/Inter-Regular.ttf"` rather than absolute system paths (a prerequisite for clean game export at Stage 16). The Project panel becomes an asset browser; "import" = drop a file into the right folder and let the watcher register it. A bundled default UI font lands here (resolves the Stage-8 follow-up). - **`Handle` as a reflected field type** so the generic inspector renders an asset-picker filtered by type — selecting a UI element then choosing its font is exactly this. - **Docs:** `docs/reflection.md` (per-field model + derive), `docs/assets.md` (database + folders + picker), and an editor-redesign note; update `architecture.md`. ### Piece breakdown (each = one green commit) 1. ✅ `oxide-engine-derive` crate + `Reflect` trait (per-field name/type/get/set) + derive — pure logic, unit-tested, on `main`. Public-only fields + `#[reflect(skip)]`; per-field RON get/set. 2. ✅ `reflect.rs` per-field get/set through the registry via `register_reflected::` (`field_infos` / `get_field` / `set_field`); Transform + Node derive `Reflect`. Whole-value API unchanged. Pure logic, on `main`. 3. ✅ Generic reflection-driven inspector (signed off on `main` 2026-06-15 after eye-check). Split: - **3a**: `EditorState.registry` (Transform + Node reflected) + generic `SetFieldCmd{entity,type,field,before,after}` with merge-coalescing. Pure logic. - **3b**: the inspector UI — walks the registry, one heading per component, one typed widget per field (f32→drag, bool→checkbox, Vec3→3 drags, String→text, `Quat`→Euler degrees, unknown→RON fallback). Deletes the hand-written `transform_inspector`; `mesh_inspector` stays until piece 4. `Node` skipped (name/enabled shown by the header). Enum→combo deferred (needs variant reflection). 4. Hierarchy redesign + component lifecycle (GUI) — → `dev`, eye-check: - Hierarchy **right-click** context menu (Add Child / Add prefab / Rename / Duplicate / Delete). - **Drag-and-drop reparenting in the hierarchy**, and **remove the inspector's Parent dropdown** (maintainer request 2026-06-15: the dropdown lists every entity, so it doesn't scale to large scenes — reparenting belongs in the tree via drag-and-drop). - Inspector **"Add Component ▾"** (enumerated from the registry) + per-component remove control; make `MeshRenderer` reflected and retire the bespoke `mesh_inspector`. - Enum→combo widget (add variant reflection to `#[derive(Reflect)]` so enum fields like `PrimitiveShape` get a dropdown instead of the RON fallback). 5. Prefab/archetype spawn entries (logic + a little GUI) — split: - ✅ **5a** (engine logic, on `main`): `oxide_engine::prefab` — `ComponentSpec`/`Prefab`/ `PrefabRegistry`, data-driven (component name + RON applied via the `TypeRegistry`), `spawn`/ `spawn_child`/`unknown_specs`. Pure logic, unit-tested; `docs/prefabs.md`. - 🟡 **5b** (editor GUI, on `dev` — awaiting eye-check): `EditorState.prefab_registry` seeded with built-in prefabs (Empty/Cube/Sphere/Plane/Camera/Directional Light); hierarchy add-menus (toolbar Root/Child, row right-click Add Child, empty-area Add Root) are submenus listing them. Plain `AddRoot`/`AddChild` retired (the `Empty` prefab covers a bare node). 6. Asset database + typed folders + `Handle` reflected + asset-picker widget (logic → `main`; picker GUI → `dev`) — split: - ✅ **6a** (engine logic, on `main`): `oxide_engine::asset::AssetDatabase` — typed `assets/` subfolders (`AssetKind`: fonts/textures/models/audio/ui), stable `AssetUid` ↔ relative-path manifest (`assets.manifest`), `scan`/`register`/`open`/`save`, and uid→`Handle` resolution through the `AssetServer`. Unit-tested round-trip + moved-project case; `docs/assets.md`. - ✅ **6b** (engine logic, on `main`): `asset_ref_target` parses an `AssetRef`/`Handle` field's syntactic `type_name` to its target type, and `AssetKind::for_handle_target` maps that to the picker's filter kind. Added the serializable `AssetRef` reference type (the form a component stores — `Option` that `resolve`s to a `Handle`). Pure logic, unit-tested. - 🟡 **6c** (editor GUI, on `dev` — awaiting eye-check): `EditorState.asset_db` (opened + scanned on project open/create, rescanned on watcher events); Project panel is an asset browser (typed-folder sections from the database + Rescan); `field_widget` asset-picker for `AssetRef`/`Handle` fields (lists assets of the matching kind, stores the `AssetUid`); bundles **Inter** (SIL OFL) as the default UI font, seeded into new projects' `fonts/`. The first component *using* an `AssetRef` field arrives with piece 7 (UI canvas). 7. Migrate Stage-8 piece 9 (UI canvas) onto the reflection-driven inspector — split: - ✅ **7a** (engine logic, on `main`): widget-tree authoring primitives in `oxide_engine::ui` — `WidgetPath` (positional addressing) + `Widget::{get_path,insert_child,remove_path,move_subtree}` (the basis for the canvas's undoable add/remove/move). Unit-tested. - ✅ **7b** (engine logic, on `main`): `FontLoader` (default-registered, makes `.ttf`/`.otf` loadable via `AssetServer`) + `VisualStyle.font_asset: Option>` (the engine's first `AssetRef` field; overrides the `FontRef` descriptor when set). Unit-tested. - ✅ **7c** (editor GUI, on `main` — eye-checked & approved 2026-06-16): the UI canvas (`PanelKind::UiCanvas`, **centre tab beside the Viewport** — it needs the large central area), in a scroll area — open/new a `UiPanel` document; widget-tree panel (selectable, using 7a), Add ▾ palette (Leaf/Row/Column/Grid/Anchor), Remove, scaled canvas preview (egui painter), and a **type-aware property panel**: id/text, kind-specific (Leaf intrinsic; Stack direction/gap/main-align; Grid cols/rows/gap), visual (background/foreground/font size/**font asset picker**), and a Layout section (sizing, align x/y, padding, margin, **anchor preset**) so widgets are sized and positioned. Save to a `ui/` asset; edits route through `SetUiPanelCmd` (undoable). Follow-ups: engine-font-accurate preview via the real `UiOverlayPass`; change-kind; widget drag-reorder (the 7a `move_subtree` primitive is ready); anchor offset/handles for free drag-positioning. ### Test Criteria - A brand-new component (e.g. a `Timer { repeating: bool, duration: f32 }`) becomes fully editable in the inspector **and** over RON/scripts with only `#[derive(Reflect, Serialize, Deserialize)]` + one registration line — **no per-type editor code**. - The generic inspector reproduces the old Transform/Mesh inspectors' behavior (positions, rotation in degrees, scale, mesh shape) — verified by an automated round-trip plus a manual eye-check. - An asset referenced by project-relative path resolves to the same `Handle` across save/load and survives moving the project directory. - Selecting a UI element in the editor and picking a font from the asset browser updates the rendered text. - Manual: right-click the hierarchy to add/duplicate/delete; add a component from the inspector; pick an asset for a `Handle` field. ### Editor-UX follow-ups (deferred from piece 6c — maintainer feedback 2026-06-16) Piece 6c shipped a *functional* asset browser, but the maintainer flagged UX improvements to do **properly later** (not blockers; kept out of their stages to avoid feature creep mid-stage). **Reconfirmed + expanded 2026-06-17** (during Stage 10): do this batch **after Stage 10 pieces 4 (terminal panel) & 5 (error console)**. Each is GUI → `dev` + eye-check. - **Unity-style asset/file explorer in the Project panel.** Today the typed folders (`fonts/ textures/ …`) are fixed, so importing means creating/using a folder with that exact name. Wanted: a real file explorer — create/rename/move folders, rename/delete assets, **drag files in from the OS** to import, context menus, breadcrumb navigation. The `AssetDatabase` already tracks arbitrary relative paths, so this is a UI/interaction layer over it (re-classify by folder still applies; loose files fall back to extension). - **Proper file/path opener for New/Open Project.** Today both take a raw typed path, which the maintainer confirms "is very hard to use". Wanted: a native file/folder picker (a `rfd`-style dialog, working on Wayland + X11) and/or a better-designed in-editor chooser, instead of a free-typed path field. - **"New Script" button on the `Script` component inspector** (Stage 10 follow-up). Create a new `.rhai` from a template into `assets/scripts/` and auto-assign it to the component, without leaving the editor — today scripts must be authored as files outside the editor. The `.rhai` loader + `AssetKind::Script` already exist; this writes a template file + registers it. **🚧 On `dev` (2026-07-10), awaiting eye-check**: name field + ➕ button at the bottom of the Script section; template/sanitize/collision helpers are unit-tested on `main` (`editor/src/assets.rs`), assignment goes through `SetFieldCmd` (undoable). - **Open a script in an editor** (Stage 10 follow-up). Double-click / button to open a `.rhai` in an in-editor text view or launch `$EDITOR` / a configured external editor; edits flow back through the existing live reload. **🚧 On `dev` (2026-07-10), awaiting eye-check**: ✏ Edit button on the Script section + double-click in the Project panel; External Editor command preference (`editor.external_editor`), fallback $VISUAL/$EDITOR in a Terminal tab, else xdg-open. --- ## Stage 8.7 — Editor Play Mode **Status: ✅ Complete on `main`** (all three pieces; piece 3 GUI eye-checked & approved 2026-06-16). Inserted before Stage 9 because Stage 9 already assumes the editor can **play/pause/step** the simulation to test physics, yet no piece defined that loop. Decided with the maintainer 2026-06-16: a **hybrid** model — in-editor play as the primary loop now, a standalone launch later. Docs: [`docs/play-mode.md`](docs/play-mode.md). ### Piece breakdown 1. **Play-state model + snapshot/restore** — ✅ `main`. `PlayState {Editing,Playing,Paused}` + `play_snapshot` on `EditorState` (`enter_play`/`toggle_pause`/`stop`); registry-aware `oxide_engine::scene::SceneSnapshot` (captures reflected components + intrinsic `Tags`/ `DisabledComponents`, not just `to_ron`'s hierarchy), restoring the scene bit-for-bit. 2. **Schedule driver primitives** — ✅ `main`. `App::step()` (one fixed tick, accumulator bypassed) and `oxide_editor::play::tick_for` (the testable Playing→Frame / Paused+Step→FixedStep / else Idle decision). 3. **Toolbar + viewport tint + runner** — ✅ `main`. Play/Pause/Step/Stop toolbar (gated by state, badge; Resume routes through `play_or_resume`), Ctrl+P / Ctrl+. shortcuts, green/amber viewport border; host runner owns the play `App`, swaps the editor scene in/out per tick (`state.scene` stays the source of truth), undo cleared on Play/Stop. **Goal:** Drive the engine's update [`Schedule`](#stage-5--engine-core-framework) from the editor so the open scene can be run, paused, single-stepped, and stopped **in the viewport**, with edits made during play safely reverted on stop. The same fixed-timestep tick a shipped game uses is what the editor drives, so play behaves like the real runtime. ### Deliverables - **Play-mode toolbar** (Play / Pause / Step / Stop) in the editor's top bar, with matching shortcuts; a clear **"PLAYING" viewport tint / indicator** so edit vs play state is never ambiguous. - **Play-state model** on `EditorState` (`Editing` / `Playing` / `Paused`); the host runner ticks the `Schedule` (incl. the fixed-timestep slot) only while `Playing`, and advances exactly one fixed tick per **Step** while `Paused`. - **Snapshot / restore.** On **Play**, snapshot the scene (reuse the RON scene serialization); on **Stop**, restore it, so play-mode mutations (physics moving bodies, scripts spawning entities) never corrupt the authored scene — avoiding Unity's classic "edited in play mode, lost it" footgun. - **Live inspect during play.** The reflection inspector and gizmos keep working while `Paused` (and `Playing`), so a field can be tweaked and the result observed live — the payoff of the Stage-8.5 reflection work. (rhai scripts hot-reload with no compile step, so iteration is instant.) - **Deferred to Stage 16 (export):** a **"Launch standalone"** button that runs the *real* exported runtime in a separate window/process via the export builder — the truest-to-ship check, reusing the Stage-16 packer rather than the in-editor loop. ### Test Criteria - Press Play → the scene's update systems run and the viewport animates; Pause halts ticking; Step advances exactly one fixed tick; Stop returns the scene **bit-for-bit** to its pre-play state (automated: snapshot, mutate via a tick, stop, assert the scene RON matches the snapshot). - Entering/leaving play mode does not leak entities, handles, or undo-stack entries into edit mode. - Manual: play a scene, pause, edit a reflected field, observe the change; stop and confirm the scene reverts. --- ## Stage 9 — Physics Integration (comprehensive) **Goal:** A comprehensive rigid-body physics system — collision, queries, constraints, and a character controller — built as a **module** (`oxide-physics`) on `rapier3d`, not a minimal wrapper. ### Deliverables - Integrate `rapier3d` as a feature-gated module registered through the Stage-5 app builder - `RigidBody` component: static, kinematic, dynamic (mass, damping, gravity scale, CCD) - `Collider` component: box, sphere, capsule, cylinder, convex hull, trimesh; friction/restitution, **collision groups/masks driven by the Stage-5 `LayerMask`**, and **sensor (trigger) colliders** that report overlap without resolving (filtered by layer — e.g. a trigger that only fires for the Player or NPC layer) - Forces & control: apply force/impulse/torque, set velocities, sleep/wake - **Joints/constraints**: fixed, revolute, prismatic, spherical - **Scene queries**: raycast, shape-cast, point/overlap queries against the physics world, with `LayerMask` filtering - **Kinematic character controller** (capsule): move-and-slide, step offset, slope limit, grounded state — reused later by the Stage-15 prototyping kit's character controller - Collision/trigger **events** surfaced to game code (enter/stay/exit) - Physics step synchronized with game loop (**fixed timestep** with interpolation to render, using the Stage-5 fixed-timestep schedule slot) - `examples/physics_stack` (boxes fall and collide) and `examples/character_capsule` (walk/jump a capsule over terrain) - **Editor:** collider shape gizmos in viewport; RigidBody/Collider inspector panels; play/pause/step physics simulation; raycast debug visualization — all contributed via the Stage-6 extension API ### Test Criteria - A dropped sphere lands on a static plane and comes to rest - Stacked boxes maintain stable contact without jitter - Kinematic bodies move without being affected by forces - Raycast/shape-cast return correct hits; sensor colliders fire enter/exit without resolving contact; layer masks correctly include/exclude bodies from collisions, triggers, and queries - A joint (e.g. revolute) constrains motion to its expected degrees of freedom - Character controller climbs steps below the offset, is blocked by walls, and reports grounded - Physics and render transforms stay in sync (no visual lag/offset) under fixed-timestep interpolation ### Piece breakdown (status) The `oxide-physics` crate is a new workspace member; the ECS is the source of truth and the rapier world is a transient resource rebuilt from components (so play-mode snapshot/restore works for free). Pure-logic/headless pieces go to `main`; editor gizmos/visual behaviour stop at `dev` for eye-check. | # | Piece | Where | Status | |---|-------|-------|--------| | 1 | Component data model + module wiring: `RigidBody`/`RigidBodyKind`, `Collider`/`ColliderShape`, `PhysicsModule`, `PhysicsSettings` (gravity) | `oxide-physics` (new crate) | ✅ on `main` | | 2 | Rapier-backed simulation: build the world from components, step on `FixedUpdate`, write transforms back; by-entity forces/velocities/sleep | `oxide-physics` | ✅ on `main` | | 3 | Collision groups/masks via `LayerMask`, sensors, collision/trigger events (enter/stay/exit) | `oxide-physics` | ✅ on `main` | | 4 | Scene queries: raycast, shape-cast, point/overlap with `LayerMask` filtering | `oxide-physics` | ✅ on `main` | | 5 | Joints/constraints: fixed, revolute, prismatic, spherical (programmatic API; component authoring needs serializable entity refs — backlog) | `oxide-physics` | ✅ on `main` | | 6 | Kinematic character controller (capsule): move-and-slide, step offset, slope limit, grounded | `oxide-physics` | ✅ on `main` | | 7 | `examples/physics_stack` + `examples/character_capsule` (headless console demos) | `examples` | ✅ on `main` | | 8a | Editor integration: register RigidBody/Collider/CharacterController (addable, reflected) + enums; wire `PhysicsModule` into the play `App` (physics = first real consumer of Play; Stop reverts via snapshot) | `oxide-editor` | ✅ on `main` | | 8b | Editor viewport: collider shape wireframe gizmos (box/sphere/capsule/cylinder), green=solid / amber=sensor, View ▸ Show Colliders toggle; matches the sim (ignores `Transform::scale`) | `oxide-editor` | ✅ on `main` | | 8c | Editor viewport: raycast debug viz — `PhysicsWorld::sync_to_scene` (pure-logic) + a View ▸ Raycast Probe toggle: click freezes a camera→cursor ray into the world (orbit to view it as a 3D line) + hit point/normal | `oxide-physics` + `oxide-editor` | ✅ on `main` | Standalone "Launch" stays deferred to Stage 16. Convex-hull/trimesh colliders (need mesh data) land within piece 2/3 or as a follow-up. --- ## Stage 10 — Scripting, Live Reload & Editor Terminal **Goal:** Game logic lives in watched scripts that hot-reload while the editor runs, an integrated terminal hosts tools and AI agents, and every component type is editable from both the editor and scripts/code through the Stage-5 reflection registry. ### Deliverables - **Scripting layer** (`rhai` preferred — embeddable, sandboxed, Rust-friendly): a `Script` component attaching a script file to an entity; lifecycle hooks (`init`, `update(dt)`, events). Scripts can also register lightweight **script modules** through the Stage-5 module API. - **Component reflection / dual-editability**: scripts read/write engine component types through the Stage-5 reflection registry, so the **same** fields are editable from the editor inspector, from scripts, and from external tools/AI agents — backed by the serde/RON representation. Scripts can spawn entities and add/edit components. - **File watching + live reload**: extend the Stage-6 watcher to recompile/reload changed scripts into the running editor with no restart, preserving scene state where possible. This is what lets *any* external editor or AI agent edit game code and have it take effect live. - **Integrated editor terminal panel**: a real shell/terminal inside the editor able to run commands and host long-running processes — including **AI agents** that edit the watched scripts, whose edits flow back through live reload. - Safe error surfacing: a script error pauses that script and reports to the editor console/terminal without crashing the editor. - `examples/script_spin`: a script rotates an entity each frame; editing the script live changes the spin without restarting - **Editor:** terminal panel; script console (errors/logs/`print`); per-entity script attach/detach; generic reflection-driven inspector for component fields ### Test Criteria - Editing a watched script changes running behavior without an editor restart - A script reads and writes a component (e.g. `Transform`) and the editor inspector reflects the same value (and vice-versa) — proving dual-editability over one representation - A scripted entity spawn/despawn and component add/edit round-trips through RON serialization - A script runtime error is reported and isolated; the editor stays alive - A command run in the editor terminal executes and streams output back to the panel - Manual: run an AI agent / external editor against the watched script dir and confirm live updates --- ## Stage 11 — Animation System **Goal:** Skeletal animation plays on a skinned mesh. ### Deliverables - `Skeleton` type: joint hierarchy with bind pose - GLTF skinned mesh and animation clip loading (via the Stage-5 asset server) - `AnimationPlayer` component: play, pause, seek, loop - Animation blending (linear blend between two clips) - `examples/anim_character`: load a character GLTF and play walk/idle clips - **Editor:** animation timeline panel; clip browser; playback controls; blend weight sliders ### Test Criteria - Idle and walk clips play without visual artifacts - Blend between clips produces smooth interpolation - Seeking to frame 0 and frame N is deterministic - Manual: visually verify no mesh distortion at extreme joint angles --- ## Stage 12 — Particle System **Goal:** GPU-driven particle emitters with configurable behavior. ### Deliverables - `ParticleEmitter` component: spawn rate, lifetime, velocity, gravity, color-over-lifetime - GPU-side simulation (compute shader) - Billboard rendering pass (registered into the Stage-5 render pipeline) - Emitter types: point, cone, sphere surface - `examples/particles_fire`: a fire-like particle effect - **Editor:** particle emitter inspector with live preview; curve editor for color/size over lifetime ### Test Criteria - 100,000 particles maintain 60 FPS on mid-tier GPU - Lifetime and color curves produce correct visual output - Emitter enable/disable works without particle artifacts - Manual: fire effect looks plausible --- ## Stage 13 — Shader System & Advanced Graphics **Goal:** Custom shaders, post-processing, and advanced rendering effects — the realistic end of the *scalable fidelity* spectrum, all composed as passes in the Stage-5 render pipeline. ### Deliverables - Hot-reloadable WGSL shader pipeline (shaders watched via the Stage-6 file watcher) - **Post-process stack** built as composable render passes: bloom, SSAO, tone mapping, FXAA, plus a stylization slot (e.g. a **VCR/CRT-style filter**) — each pass opt-in, so a project ships only the effects it enables - Shadow mapping (directional light, single cascade) - Skybox rendering (HDR cubemap) - `examples/advanced_scene`: showcase scene with shadows, SSAO, bloom; `examples/stylized_scene`: the same scene with a flat/low-poly look and a VCR post filter (demonstrating fidelity scaling) - **Editor:** shader asset editor with hot-reload button; post-process stack configuration panel (enable/reorder passes) ### Test Criteria - Shader hot-reload works without GPU crash or validation errors - Shadows appear on correct geometry, no peter-panning - SSAO adds visible depth cues on curved surfaces - Enabling/disabling individual post passes changes only that effect; a project with no post passes pays no post-process cost - Manual: realistic scene looks polished; stylized scene shows the low-poly + VCR look --- ## Stage 14 — Standard Audio System **Goal:** A conventional, low-overhead audio system every project can use by default — the simple audio model, with the ray-traced model available later as an opt-in module (Stage 17). ### Deliverables - Audio backend/device + mixer (`kira` or `rodio`): load/decode clips (WAV/OGG via the asset server), play/stop/pause, volume, pitch, looping, per-bus mixing (master/music/sfx) - `AudioSource` / `AudioListener` components designed to be shared with Stage 17 (same component types, swappable backend) — **standard spatialization**: distance attenuation (inverse-square) + stereo panning from listener-relative position, optional low-pass with distance - One-shot and streaming playback; music crossfade - Per-source **audio-model selector** (standard now; "ray-traced" becomes selectable once the Stage-17 module is enabled) — both share components and the listener - `examples/audio_basic`: positional sound that pans and attenuates as the listener moves - **Editor:** audio source inspector (clip, volume, range, loop, bus); per-source model selector; range-sphere gizmo ### Test Criteria - Clips play/stop/loop with correct volume and pitch; buses mix independently - Panning and attenuation track listener-relative position (inverse-square within 5% error) - Selecting a non-standard audio model on a source requires no component-type change (forward-compat with Stage 17) - Manual: positional audio feels correct moving around a source --- ## Stage 15 — Engine Content & Prototyping Kit **Goal:** Ship the batteries — built-in primitives, controllers, shaders, and example scenes — so projects start building immediately instead of from an empty scene. The general-purpose capstone before export. ### Deliverables - **Prototyping primitives**: ready-to-use cube/sphere/capsule/plane/cylinder meshes and a grid/ checker "prototype" material, placeable from the editor (greybox blockout workflow) - **Built-in character controller** asset: a drop-in first/third-person controller built on the Stage-9 capsule controller + Stage-7 named actions (move/look/jump/crouch/sprint), remappable - **Built-in shader library**: curated, documented WGSL shaders/materials (unlit, PBR, prototype grid, simple sky) usable as-is or as templates for Stage-13's shader editor - **Example games/scenes**: a small set of runnable example projects exercising the engine end to end (e.g. a walk-around prototype level, a physics playground) - **Editor:** an "Add ▸" content menu/asset browser exposing the primitives, controller, materials, and example scenes - Everything here is built only from already-shipped engine systems (no new core subsystems) ### Test Criteria - Each prototyping primitive spawns and renders correctly from the editor - The built-in character controller walks/looks/jumps on a prototype level with remappable actions - Built-in shaders compile and render without validation errors - Example projects build and run via `cargo run` and from the editor - Manual: blockout a small level using only built-in content and play it with the controller --- ## Stage 16 — Windows Support & Game Export **Goal:** A Windows build of the editor/engine, and one-click export of a project to standalone Linux **and** Windows game binaries. **This completes Phase 1: a general-purpose engine that builds and ships real games.** ### Deliverables - **Windows build** of `oxide-engine` and `oxide-editor` (verified on Windows; `wgpu` DX12/Vulkan, `winit` Win32) alongside the existing Linux Wayland/Xorg build - **Game runtime/export pipeline**: package a project's scenes, scripts, and assets into a standalone runnable game that links the engine runtime (without the editor), compiling in **only the modules the project enables** (validating the Stage-5 feature-gating) - **Cross-target export**: from the editor (on Linux) export to both a Linux binary and a Windows binary (cross-compile, e.g. `x86_64-pc-windows-gnu`), bundling assets next to the executable - Export presets (target platform, asset packing, build profile) and a CLI equivalent for CI - Packaging: keep `install.sh` for the editor; add export output layout docs for both platforms - `examples/`: export one of the Stage-15 example projects to Linux and Windows binaries - **Editor:** "Export Project" dialog (choose Linux/Windows targets, output dir, profile) ### Test Criteria - The editor builds and runs on Windows (Wayland/Xorg Linux build remains unaffected) - A project exports to a Linux binary that runs standalone (no editor, no toolchain) with its assets - The same project cross-exports to a Windows binary that runs on Windows - Exported binaries contain no editor-only code paths and none of the modules the project didn't use - Manual: run an exported Linux build and an exported Windows build of an example project --- # Phase 2 — Built-in Modules Each Phase-2 stage is a **self-contained, feature-gated module** built on Phase-1 systems and wired in through the Stage-5 module API and Stage-6 editor extension API. Every module ships documentation for both *using* it and *authoring* one like it, and each automatically works with export (Stage 16) and the settings framework (Stage 6). The order below is a default; modules are largely independent and can be reprioritized to match the needs of the first real game built in Oxide. ## Stage 17 — Ray-Traced Spatial Audio (module) **Goal:** Sound propagation that simulates real wave behavior from scene geometry — the advanced audio model, built on the Stage-14 components so projects opt in per source. ### Deliverables - Reuses `AudioSource` / `AudioListener` from Stage 14 (ray-traced model selected per source) - Sound ray casting: shoot rays from source, accumulate energy at listener (built on the engine's own ray casts / `LayerMask` filtering) - Occlusion: blocked rays reduce volume - Reflection: rays bounce off surfaces and arrive with delay (reverb model) - Distance attenuation using inverse square law - `examples/sound_cave`: walk around a cave with echoing footstep sounds - **Editor:** audio source visualizer (range sphere, ray debug lines); reverb parameter inspector; the module's settings page ### Test Criteria - Sound behind a wall is measurably quieter than line-of-sight - Moving away from source follows inverse square law within 5% error - Reverb tail length correlates with room size - Switching a source between standard and ray-traced models requires no component-type change - Manual: walk through the cave example and confirm intuitive sound behavior --- ## Stage 18 — Developer Console & Cheats (module) **Goal:** A drop-in developer console any game can enable for commands, cheats, and debugging help — trivially added to a project and removed from release builds. ### Deliverables - In-game console overlay (built on the Stage-8 UI) with command input, history, autocomplete, and log output; toggled by a Stage-7 action (default backtick) - **Command registry**: register named commands with typed arguments from engine, game code, scripts, or other modules; built-in commands (spawn, teleport, set var, toggle layer, time scale, etc.) - **Cheats / dev variables (cvars)**: registerable toggles and values (godmode, noclip, give, fly) with a guard so they can be compiled out or locked in release/export builds - Bind console commands to keys via the Stage-7 input system for quick dev shortcuts - `examples/console_demo`: a scene with registered cheats and commands - **Editor:** command browser/inspector; console mirrored in the editor; the module's settings page (enable in build, default key, release lockout) ### Test Criteria - Registered commands execute with correct typed-argument parsing; unknown commands report cleanly - Cheats/cvars change game state live and can be disabled/compiled out for release - The console consumes input over the game via the Stage-8 routing model - Console state and command registry are unaffected by hot-reload of game scripts - Manual: enable the module in a game, open the console, run commands and toggle a cheat --- ## Stage 19 — Procedural Toolkit (module) **Goal:** Composable procedural *building blocks* — noise and a modifier/operation stack the user combines — not a fixed world generator. Consumed by the terrain module (Stage 20) and usable standalone. **Build tools, not the game.** ### Deliverables - Noise library: Perlin, Simplex, Worley, fractal octaves, domain warping (deterministic, seeded) - **Modifier/operation stack**: chainable operations over heightfields/value grids (add, multiply, terrace, erode, mask, remap, blend) authored as a data-driven, serializable graph/stack - Seeded RNG utilities and distribution helpers for scatter/placement - Simple L-system helper for vegetation/structure layout - Region/`Biome` helper: blend by temperature/humidity/height maps (a tool, not a built-in world) - `examples/procgen_playground`: build a noise + modifier stack and preview the result - **Editor:** noise preview panel (2D heatmap); modifier-stack editor with live re-evaluation; seed controls; the module's settings page ### Test Criteria - Same seed + same stack always produces identical output (determinism) - Each modifier transforms its input correctly and composes in a chain - A modifier stack round-trips through RON unchanged (dual-editable) - Benchmark: evaluating a representative stack over a 1024² grid completes within target time - Manual: build and preview a multi-stage noise stack in the editor --- ## Stage 20 — Terrain System (module) **Goal:** A comprehensive terrain module: generate terrain (via the Stage-19 toolkit), sculpt and paint it with editor brushes, and scatter vegetation/objects onto it. Designed to stream later under Stage 21. ### Deliverables - **Terrain representation**: chunked heightmap terrain with a meshing pass (normals, UVs) and collider generation (heightfield collider via Stage 9); LOD-ready chunk layout - **Generators**: drive terrain from the Stage-19 noise + modifier stack, from basic Perlin/Simplex up to layered erosion/domain-warp stacks and biome-driven generation - **Sculpt brush tools** (editor): raise/lower, smooth, flatten, noise — adjustable radius, strength, falloff; live edits, undoable through the Stage-6 command stack - **Texture/splat painting** (editor): paint multiple terrain material layers (e.g. grass/rock/sand) with a splat map and per-layer tiling; brush-based painting with falloff - **Foliage & object scatter** (editor): paint/spray grass, trees, rocks, or arbitrary prefabs with density/scale/rotation jitter and slope/height masks; instanced rendering for dense foliage, entity placement for larger objects - **Dual-editable & serializable**: terrain data (heightmap, splat layers, scatter sets) round-trips through RON so it is editable from editor and scripts/AI agents - `examples/terrain_playground`: generate a terrain, sculpt/paint it, scatter grass and trees - **Editor:** terrain tool panel (generate, sculpt, paint, scatter sub-tools); brush settings; layer/material list; foliage/prefab palette; the module's settings page ### Test Criteria - A generated terrain produces correct mesh normals and a matching heightfield collider - The same seed/generator stack produces identical terrain (determinism) - Sculpt and paint brush edits modify the heightmap/splat map correctly and are undoable - Scatter respects density and slope/height masks; instanced foliage renders without per-instance entity overhead - Terrain + splat + scatter data round-trips through serialization unchanged - Manual: sculpt, paint layers, and scatter grass/trees on a terrain and confirm it collides correctly --- ## Stage 21 — Open World Support (module) **Goal:** Streaming, chunked worlds that can exceed memory limits. ### Deliverables - World chunk system: load/unload chunks based on camera position (built on the Stage-5 asset server) - Async asset streaming (chunks load on background threads) - Level of Detail (LOD) system for meshes and terrain - Entity streaming: entities activate/deactivate with their chunk - `examples/open_world`: a large world that streams seamlessly as you move - **Editor:** world chunk map overlay; streaming debug view (loaded/unloaded chunks); LOD visualizer; the module's settings page ### Test Criteria - No frame drops during chunk load/unload transitions - Memory usage stays bounded regardless of world size - Entities in unloaded chunks do not tick (verified via counters) - Manual: drive across chunk boundaries and confirm invisible seams --- ## Stage 22 — Pathfinding & NPC AI (module) **Goal:** Built-in navigation and reusable NPC behavior building blocks, so games get agents that move and decide without writing it all from scratch. ### Deliverables - **NavMesh**: bake a navigation mesh from scene/terrain geometry (with `LayerMask`-driven walkable/obstacle filtering); runtime queries (path find, nearest point, path smoothing) - **Agent component**: follow a path with steering, avoidance, speed/acceleration, and grounded movement via the Stage-9 character controller - **NPC AI building blocks**: a behavior-tree and/or state-machine system, perception helpers (vision cones / hearing using scene queries and layers), and steering behaviors (seek/flee/wander/ patrol) — composable, not a fixed AI - Dual-editable, serializable behavior/agent definitions - `examples/npc_patrol`: agents navigate, patrol waypoints, chase the player, and avoid obstacles - **Editor:** navmesh bake/visualize panel; agent inspector; behavior-tree/state-machine editor; the module's settings page ### Test Criteria - A baked navmesh produces correct paths around obstacles; queries return valid points - Agents follow paths, avoid each other, and stop at goals without jitter - A behavior tree/state machine transitions correctly and is serializable (dual-editable) - Perception correctly gates on layers and line-of-sight - Manual: watch NPCs patrol, detect, chase, and give up --- ## Stage 23 — Water System (module) **Goal:** Built-in water — rendering and gameplay mechanics — that a project can drop in. ### Deliverables - **Water rendering**: animated water surface (waves/normals), reflection/refraction, depth-based color/fog, shoreline blending — composed as Stage-5 render passes and a Stage-13 shader material - **Water bodies**: planar/ocean and bounded volumes (lakes/pools) with configurable height/level - **Gameplay mechanics**: buoyancy/floating on Stage-9 rigid bodies, water-volume triggers (enter/exit, submerged state), simple flow/current forces, and a swim mode for the Stage-15 character controller - Optional underwater post-process (tint, distortion, muffled audio hook for Stages 14/17) - Dual-editable, serializable water-body definitions - `examples/water_scene`: a floating object, a swimmable lake, and an ocean horizon - **Editor:** water body inspector and placement gizmos; surface/material settings; the module's settings page ### Test Criteria - Water renders with plausible reflection/refraction and correct depth blending at shorelines - Rigid bodies float and settle at the correct waterline; flow forces push bodies as configured - Water-volume triggers fire enter/exit and report submerged state correctly - The character controller enters swim mode in water and exits on leaving - Manual: drop objects in water, swim the character, and confirm it looks and behaves correctly --- ## Backlog (additional modules & post-roadmap capabilities) ### Editor UX / inspector (raised 2026-06-16, maintainer GUI review) - **Editor visual overhaul** — overall styling, spacing, and panel layout need a dedicated polish pass (egui theme, inspector/panel density). Tracked as a standalone editor-styling piece, not folded into feature work. - **Component gizmos / viewport handles** — components with spatial meaning need editor-only viewport visualization: `DirectionalLight` direction arrow (+ cone for future spot lights), camera frustum, collider/audio shapes. Pairs with the multiple-light-types work below. - **Multiple light types** — `DirectionalLight` is the only light today; add point + spot (and the renderer support to gather light *entities*, which the reflected `DirectionalLight`/`Camera` components are scaffolding toward). - **Component multiplicity model (DECIDED 2026-06-16 — hybrid).** Archetypal ECS (`hecs`) allows one component of a given type per entity. Chosen approach, applied as each component type lands: - **Local-offset field** for things that are naturally *single per node but positioned* — a collider's `center`, a light/audio emitter offset — stored as a `Vec3`/`Transform` field on the component (one node, no extra hierarchy). This mirrors Unity (`BoxCollider.center`). - **Child entities** for *genuine multiples* (several meshes on one logical object): each is a real node with its own `Transform`, gizmo-movable and shown in the viewport — what Unity/Bevy do for multi-mesh. The "Add … (as child)" path stays; editor UX is polished so it doesn't feel like clutter. - Explicitly **not** doing internal multi-instance lists + sub-object gizmos (rejected as the biggest lift / least ECS-native). Concretely: colliders (Stage 9) and audio sources (later) get offset fields; multi-mesh stays child-based. - Networking (deterministic lockstep or rollback for simulation use) - Multi-threading and job system (rayon or custom) - **Dynamically loadable native plugins** (C-ABI/dylib) — currently modules are compile-time crates or `rhai` scripts; revisit once the static module API is proven - Save-format/asset versioning and migration (backward compatibility for shipped projects) - VR/XR support - Volumetric fog and clouds - GPU-driven rendering (indirect draw, bindless resources) - Terrain deformation at runtime - Fluid simulation (beyond the Stage-23 water surface model) - Vehicle physics module - Destruction / fracture module - **SDF (signed-distance-field) text rendering** for the Stage-8 UI — each glyph rasterized once at one canonical size as an SDF, then scaled arbitrarily in the shader. Decouples atlas memory from font-size combinatorics (HiDPI, animated scale, many sizes) and is how Unity TextMeshPro / Unreal Slate / Godot 4's font system all ship. Slots in next to the piece-3 `ab_glyph` rasterizer without rewriting the shaper. --- ## Testing Protocol (All Stages) 1. **Unit tests** — every module has tests for core behavior and edge cases 2. **Integration tests** — the `tests/` crate runs end-to-end scenarios 3. **Example review** — each stage ships at least one runnable example; both user and Claude review it 4. **Benchmarks** — performance-sensitive systems have `criterion` benchmarks; regressions block stage completion 5. **Clippy + fmt** — zero warnings, consistent formatting required before stage sign-off 6. **Module authoring docs** — every module (Stage 9 on) ships both a usage guide and an authoring guide under `docs/`; a stage is not done until its docs land