Files
Oxide/PLAN.md
T
Homer c47efa876f Editor: Unity-style file explorer in the Project panel
The last item of the Stage-10 editor-UX batch. The Project panel's
fixed typed-folder listing becomes a real file explorer over assets/:

- breadcrumbs + double-click folder navigation, " New Folder", inline
  rename rows, context menus (Open / Rename / Delete), drag a row onto
  a folder (or "..") to move it, drag files in from the OS to import
  into the current folder, double-click to open (scripts via the
  external-editor flow, others via xdg-open).
- All behavior lives egui-free in editor/src/explorer.rs (listing,
  breadcrumbs, name validation/uniquing, create/rename/move/delete/
  import) and is unit-tested; the shell only renders it. Renames and
  moves ride the uid-preserving AssetDatabase ops so saved AssetRefs
  keep resolving; unregistered files fall back to fs::rename. Folders
  delete only when empty — no recursive asset deletion.
- Engine: AssetDatabase::scan now walks the WHOLE assets/ tree instead
  of just the typed folders, so assets organised into custom folders
  register and survive rescans (covered by updated unit tests).

File operations act immediately and bypass the undo stack, like the
hierarchy's structural edits. GUI piece — needs an eye-check before
promotion to main.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 21:01:05 +02:00

1248 lines
80 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 016).** 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 18 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 15/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<Mesh>`, `Handle<Texture>`, …) 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<T>`, 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 17 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 (17) 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
916.
> **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<T>`→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<T>`** 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<T>` 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::<T: Reflect>`
(`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<T>` 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<T>` 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<T>`/`Handle<T>`
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<T>` reference type (the form a
component stores — `Option<AssetUid>` that `resolve`s to a `Handle<T>`). 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<T>`/`Handle<T>` 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<AssetRef<Font>>` (the engine's
first `AssetRef<T>` 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<T>` 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<T>` 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).
**🚧 On `dev` (2026-07-10), awaiting eye-check**: breadcrumbs + folder
navigation, New Folder, rename/delete context menus (folders delete only when
empty), drag-to-move rows, OS drag-in import, double-click-to-open. Behavior
layer is unit-tested (`editor/src/explorer.rs`); renames/moves ride the new
uid-preserving `AssetDatabase` file ops, and `scan` now walks the whole
`assets/` tree so custom folders survive rescans.
- **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.
**🚧 On `dev` (2026-07-10), awaiting eye-check**: Browse… buttons on both
dialogs → native folder picker (`rfd`/xdg-portal, Wayland + X11) on a helper
thread; the typed field remains as fallback.
- **"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