4f1e9a48d7
Stage-10 editor-UX follow-up. The Script component's inspector section now ends with a name field + "➕ New Script" button (Enter in the field also submits): it writes a fresh .rhai from the tested template into assets/scripts/ via create_script_file (sanitized stem, collisions suffixed), registers it in the asset database, saves the manifest, and assigns it to the component's `source` through the normal edit path — so the assignment is one undoable SetFieldCmd. Disabled with a hint while no project is open; failures land in the Console via the log. GUI piece — needs an eye-check before promotion to main. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1235 lines
79 KiB
Markdown
1235 lines
79 KiB
Markdown
# 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<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 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<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).
|
||
- **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.
|
||
|
||
---
|
||
|
||
## 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
|