# Bevy FPS Foundation A modular first-person game foundation and in-process editor built on **Bevy 0.18** and **Avian 0.6** physics. The runtime game provides a high-fidelity PBR stack (HDR, procedural atmosphere + image-based lighting, cascaded shadows, SSAO, TAA, bloom, fog, ACES tonemapping) plus Hybrid Auto Solari raytraced indirect lighting where supported, and local input/presentation glue for a fixed-step kinematic FPS simulation. The editor provides dockable egui panels, a filtered scene hierarchy, click selection, transform gizmos, undo/redo, asset import/placement, native Bevy scene save/load, and BRP support for external tooling. ## Requirements - Rust (stable; pinned via `rust-toolchain.toml`) - Git LFS for binary game/source assets: run `git lfs install` once, then `git lfs pull` after clone. - A Vulkan-capable GPU + drivers (developed against an NVIDIA RTX 3080 Ti) - Linux build deps for `winit`/`wgpu` (ALSA, udev, etc.) if not already present ## Running ```bash # Check everything cargo fmt --check cargo check --workspace cargo clippy --workspace cargo clippy -p game -p shared -p protocol -p sim -- -D warnings cargo test -p game --lib cargo test -p sim # Runtime game cargo run -p game cargo run -p game --features dev # In-engine editor cargo run -p editor cargo run -p editor --features dev ``` ### Hot reload (gameplay iteration) `--features dev` turns on Bevy **dynamic linking** for faster rebuilds (recommended day-to-day). Add `--features hot-reload` for **in-process hot reload** of [`game_hot`](crates/game_hot) (sim movement, player input/camera, world bootstrap). The editor process stays open; only gameplay system bodies swap when the dylib rebuilds. **Two-terminal workflow (hot reload):** ```bash # Terminal 1 — rebuild the hot dylib on save cargo watch -w crates/game_hot -w crates/sim -x "build -p game_hot --features dylib" # Terminal 2 — run the editor once cargo run -p editor --features dev,hot-reload ``` Or use the VS Code task **watch game_hot (hot reload)** alongside **run editor (dev fast-link)** (enable the `hot-reload` feature on the editor run task when using dylib iteration). | Input | Action | |-------|--------| | Save `game_hot` / `sim` gameplay code | Dylib rebuilds; status bar shows **building…** then **reloaded** | | **Ctrl+Shift+R** | Manual refresh (`on_hot_reload` bootstrap: camera FX, ambient light) | **Safe to hot reload:** system logic, movement tuning in code, input mapping. **Requires full editor restart:** new/changed component fields, new components, plugin schedule changes, editor UI code. > Note: the workspace uses the `mold` linker via `clang` (see `.cargo/config.toml`) for fast link times. ### Target Cache Cleanup Cargo/Bevy debug artifacts can grow quickly. Use the workspace cleanup task before reaching for a full `cargo clean`: | Command | Effect | |---------|--------| | `cargo clean-target` | Dry run: prints reclaimable `target/` cache. | | `cargo clean-target --apply` | Safe cleanup: removes incremental and rust-analyzer flycheck cache. | | `cargo clean-target --include-artifacts --days 3 --apply` | Deeper cleanup: also removes stale hashed `deps`, `build`, `.fingerprint`, and example artifacts older than 3 days. Cargo will rebuild anything still needed. | VS Code tasks: **target cleanup (dry run)** and **target cleanup (safe apply)**. ### Launch Troubleshooting - The native game/editor windows force an opaque Wayland surface and opaque camera clears to avoid compositor alpha issues on mixed HDR/SDR desktops. - If the window maps but appears transparent on Hyprland or another Wayland compositor, launch with `BEVY_FPS_HDR=0` to force the SDR camera path while debugging monitor/compositor behavior. - The workspace patches `bevy_render` locally so transient Linux swapchain acquire timeouts skip one frame instead of panicking in `prepare_windows`. - Debug launch configs and run tasks set `WGPU_VALIDATION=0` to quiet the known wgpu/Vulkan validation-layer warning `VUID-StandaloneSpirv-MemorySemantics-10871`. This only disables the Vulkan validation layer for those launches; Rust panics and application errors still surface normally. - If **CodeLLDB / mold** fails with hundreds of `undefined symbol` linker errors, the incremental `target/` cache is stale. Run the VS Code task **clean build editor (dev)** or `cargo clean -p editor && cargo build -p editor --features dev`, then launch **Debug editor** again. Use `cargo run -p editor --features dev` from the terminal if you need `libbevy_dylib` on `LD_LIBRARY_PATH` automatically. ## Editor Controls | Input | Action | |-------|--------| | `F5` | Toggle Edit / Play in the same viewport | | `F6` | While playing: pause / resume simulation (stay in Play mode) | | `F8` | While playing: possess player / eject to editor camera | | `G` | Toggle clean game-view overlay (hide editor widgets/proxies/gizmos) | | `Ctrl+G` | Toggle viewport grid | | `F` | Focus editor camera on selection | | `Ctrl+Shift+1` / `Ctrl+Shift+2` | Save / recall viewport camera bookmark | | RMB + mouse | Editor camera look; cursor hides while held | | RMB + `W` `A` `S` `D` | Editor camera fly | | RMB + `Q` / `E` | Editor camera down / up | | Mouse wheel | Dolly editor camera | | MMB + mouse | Pan editor camera | | Click authored object, actor root icon, or visualizer in viewport | Select source entity | | Click empty viewport / `Esc` | Deselect | | `Delete` / `Backspace` | Delete selection | | `Ctrl+D` | Duplicate selection | | `Ctrl+Z` / `Ctrl+Shift+Z` / `Ctrl+Y` | Undo / redo | | `F2` in Hierarchy | Rename selection | | `W` / `E` / `R` | Translate / rotate / scale gizmo | | `X` | Toggle world/local gizmo orientation | | `B` | Enter Draw Brush mode | | Draw Brush: LMB / `Enter` / `Esc` / `Backspace` | Place floor points / create brush / cancel / remove last point | | Brush selected: `1` / `2` / `3` / `4` | Vertex / edge / face / clip edit modes | | Brush edit mode: LMB / `Shift+LMB` / `Esc` | Select element / toggle element selection / return to object mode | | Viewport toolbar (sun / brush / box icons) | Shading: Lit, Unlit (albedo), Colliders (mesh off) | | Viewport eye/options | Toggle actor root icon categories, adjust icon/gizmo size, and control colliders, lights, spawns, prefab/model anchors, and runtime player/camera visualizers | | `Tab` in viewport | Cycle selection through overlapping objects at last click | | Click Player visualizer in Edit mode | Select or create the authored `PlayerSpawn` (`Player Start`) | | Select Project Sun | Inspect project default lighting; create a scene sun override | | Asset Browser project/file views | Browse `assets/`, search/filter/sort, switch grid/list, expand model subassets with generated thumbnails, inspect staged import/material settings, drag assets/submeshes into the viewport | | Asset Browser context/details actions | Apply textures/materials, regenerate thumbnails, reimport models, place assets/submeshes, or move file assets to `assets/.trash/` | | `Ctrl+P` | Command palette; type to filter, use arrow keys to select, Enter to run (`scene.reset_lighting`, `selection.group`, `selection.focus`, `selection.reset_transform`, play commands) | | `F7` | While paused in Play: advance one sim tick | | Shift/Ctrl + click (Hierarchy) | Additive selection | | Hierarchy context | Reparent to other selection / Unparent | | File menu | New, Open, Save, Save As, Import Assets, Export Selection, Save Selection As Prefab, Recent Scenes | | Inspector component card | Collapse with caret, toggle active with status dot, or use triple-dot menu for reset/copy/paste/move/remove actions | | Inspector footer → Add Component | Expands an inline search shelf for registered authoring, rendering, physics, gameplay, and volume components with descriptions, availability hints, and undo | | Edit → Project Settings… | Edit `assets/project.ron` (rendering, physics, input) | Viewport shortcut keys require the pointer to be in the viewport and are suspended while typing in egui text fields or actively using camera navigation. `Delete`, `Backspace`, duplicate, and undo/redo also defer to text-field focus. ### Play Mode - Press **F5** (or Play menu) to run the **real game** in-process: same `GamePlugin`, player, fixed-step `sim`, and rendering stack as the standalone `game` binary. - The unified **Viewport** stays in place on play enter. The editor fly camera and player camera render to the same offscreen HDR texture depending on Edit/Play possession state. - While editing, gameplay simulation and input are disabled (`SimEnabled` / `GameInputEnabled`). Entering Play enables simulation; **F6** pauses/resumes the sim without leaving Play; **F8** toggles **possess** (player input + camera) vs **eject** (editor fly camera while sim keeps running). - Transport controls (Play, Pause, Stop, Eject) live on the **main toolbar** below the menu bar. - The viewport uses the full project rendering stack from **`assets/project.ron`** (Edit → Project Settings…). Only one 3D camera carries that stack at a time (editor fly cam while editing/ejected, player cam while possessed) so GPU bind limits are respected. - Add a `PlayerSpawn` marker component (via Inspector) on a level object to choose where Play starts; otherwise the default game spawn `(0, 1.5, 10)` is used. - Actor root icons draw over scene meshes, stay screen-sized while zooming, and are prioritized when clicked. - Clean game-view overlay (`G`) hides editor widgets, actor root icons, visualizers, selectable proxies, gizmos, selection outlines, and grid without changing the active camera. - **F5** stops Play and restores **player sim state** only; authored scene edits made during PIE are kept. **Esc** frees the cursor mid-play. ## Game Controls | Input | Action | |-------|--------| | `W` `A` `S` `D` | Move | | Mouse | Look | | `Space` | Jump (coyote-time + buffered) | | `Left Shift` | Sprint | | `Left Ctrl` | Crouch | | `Esc` | Release / re-grab mouse cursor | ## Scene Workflow - Levels are saved as native Bevy dynamic scenes under `assets/levels/`. - Editable entities use reflectable authoring components from `crates/shared`. - The editable player start is an authored `PlayerSpawn`; moving it with the transform gizmo changes where Play mode boots the runtime player. - Project settings provide default ambient/sun lighting. A scene-authored directional `LightDesc` acts as a per-scene sun override and disables the project sun contribution. - Hydration systems turn authoring data into runtime meshes, GLTF scenes, materials, lights, and Avian colliders. Persisted `ActorId` / `ComponentInstanceId` values are stable; raw Bevy `Entity` IDs are runtime-only. - Inspector component active toggles are saved in `InspectorOrder`. Inactive authoring components keep their data but do not hydrate runtime meshes, lights, physics, or post-process effects. - Dragging glTF/GLB/FBX assets creates `StaticMeshRenderer` actors by default from normalized artifacts in `assets/meshes/generated/`. Renderer slots reference imported content-browser mesh and material assets, while generated collision is stored separately in `ColliderDesc` plus `RigidBodyDesc`. Switch a model asset's placement mode to **Scene Instance** in Asset Browser details when you need full `SceneRoot` loading for animation/skinning/scene data. Expanding a model in the Asset Browser exposes normalized mesh/material/texture subassets; dragging a mesh subasset places that part through the same static mesh renderer path. - Brush actors are persisted as `ActorKind::Brush + BrushDesc`; the MVP hydrates additive convex cube brushes into generated preview meshes while face/edge/CSG editing remains roadmap work. - Draw Brush mode (`B` or toolbar pencil) places snapped floor points and commits an additive prism brush with `Enter`; `Esc` or right-click cancels without changing the scene. - Runtime-only handles/colliders are not serialized directly, keeping scenes stable and portable. - Editor-only cameras and helper roots are filtered from selection, hierarchy, and scene save. - **PIE restores player sim only** (transform, velocity, jump state) when you stop Play; authored `LevelObject` edits made during PIE **remain** in the scene (the level may show as dirty). - Play mode swaps the unified viewport between the player camera (possessed) and editor fly camera (ejected). - Gameplay movement runs in a fixed timestep in `crates/sim`. Raw keyboard/mouse input is translated by `crates/game` into `protocol::PlayerInputIntent` before it mutates simulation state. ## Troubleshooting ### Viewport is black or wireframe-only 1. Check the **mode badge** (bottom-right of the viewport). **Collider** hides meshes — click the **sun** toolbar icon for **Lit** shading. 2. **Window → Rendering → Active Camera**: check **Runtime lights** (directional/point/spot counts), scene vs project sun status, and contributing post-process volumes. Scene directionals override the project sun; use **Scene → Lighting → Use project sun** to restore project defaults. 3. **Solari requested but not visually changing**: check **Window → Rendering → Active Camera** for requested/effective GI, fallback reason, tagged meshes, Solari-compatible mesh assets, render instances, bind-group readiness, and compatible lights. Effective Solari forces an HDR camera target because Bevy Solari writes through a storage texture; imported/custom meshes need TriangleList geometry, POSITION/NORMAL/UV_0/TANGENT attributes, and U32 indices for Bevy 0.18 Solari. 4. **Lighting changes do nothing in Solari**: Bevy 0.18 Solari samples directional lights and emissive meshes, not point/spot lights. Use Directional lights or emissive materials in Solari; switch to Forward PBR for point/spot light authoring. 5. **GiMode Auto shows Forward**: Solari RT wgpu features are unavailable on this GPU. Dev RT override: `BEVY_FPS_FORCE_SOLARI=1`. 6. **Volume overrides ignored**: confirm camera is inside volume AABB; check priority in Rendering → Volumes tab. 7. **Custom post FX**: RON under `assets/post_fx/`; assign path in volume inspector (see [docs/editor/rendering.md](docs/editor/rendering.md)). 8. Scene **Open** / **Recent** runs hydration immediately; **zero runtime lights** after load usually means missing `LightDesc` on light actors (see Rendering panel). ### Hierarchy sort jumps when clicking **Sort: Name** / **Type** now use stable tie-breakers (duplicate names like several `Pillar` rows stay put). Use **Sort: Manual** and drag-and-drop for explicit sibling order. ### Hierarchy scrolls during drag Scroll-to-drag is disabled while dragging actors. Release the mouse to finish or cancel the drag. ## Cursor / VSCode Setup The `.vscode/` folder is preconfigured: - `extensions.json` recommends rust-analyzer, Even Better TOML, CodeLLDB, crates, and Error Lens. - `settings.json` runs `clippy` on save, enables proc-macro/build-script support, formats on save, and excludes `target/` from search/watch. - `tasks.json` provides workspace check/clippy plus game/editor build/run tasks. - `launch.json` provides CodeLLDB launch configs for the editor and game. - `.github/workflows/ci.yml` mirrors local formatting, check, clippy, test, and binary build verification. ## Architecture Decisions - [Documentation index](docs/README.md) - [Mission — editor framework & principles](docs/mission.md) - [Editor framework docs](docs/editor/README.md) - [ADR 0001: Roadmap Architecture](docs/adr/0001-roadmap-architecture.md) - [ADR 0002: Bevy Version And Migration Policy](docs/adr/0002-bevy-version-and-migration-policy.md) - [ADR 0003: Editor Framework Mission](docs/adr/0003-editor-framework-mission.md) - [ADR 0014: Unified Viewport Model](docs/adr/0014-unified-viewport-model.md) - [ADR 0016: Unified Rendering Contract](docs/adr/0016-unified-rendering-contract.md) - [ADR 0017: Normalized Static Mesh Assets](docs/adr/0017-normalized-static-mesh-assets.md) ## Project Layout ``` crates/ protocol/ Shared tick, input intent, command, and message/event protocol types sim/ Fixed-step gameplay simulation and determinism test scaffolding shared/ Reflectable authoring components + hydration systems game/ Runtime FPS game library + standalone game binary editor/ In-process egui editor binary ``` ## Implementation Checklist - [x] Workspace: `game`, `shared`, `editor`, `protocol`, `sim` - [x] Shared authoring components + hydration - [x] Protocol tick, input intent, command, and basic message/event types - [x] Fixed-step deterministic sim crate for player movement/controller state - [x] Runtime game refactored into `GamePlugin` - [x] Raw game input translated into protocol intent before simulation - [x] Docked egui editor scaffold - [x] Editor fly camera - [x] Mesh picking selection + highlights - [x] Transform gizmos - [x] Editor-side scene visualizers for colliders, lights, player spawns, prefab/model anchors, and runtime player/cameras - [x] Selectable, screen-sized actor root icons in the 3D viewport with per-category visibility controls - [x] Filtered hierarchy, inspector, viewport, toolbar, asset browser, and status panels - [x] Delete, duplicate, rename, and structural/material undo-redo - [x] Native Bevy scene New/Open/Save/Save As with dirty title tracking - [x] Asset import, static mesh/prefab placement, texture assignment, and selection export - [x] PIE player-only snapshot/restore (authored `LevelObject` edits persist on stop) - [x] Unified viewport render-to-texture target + Play session bootstrap - [x] `PlayerSpawn` authoring marker for editor Play start location - [x] BRP enabled in the editor - [x] Cursor/VSCode workspace tasks + launch configs - [x] Opaque Wayland window launch defaults + SDR/HDR runtime toggle - [x] CI workflow for format, check, clippy, tests, and binary builds - [x] ADRs for roadmap architecture and Bevy migration policy - [x] Determinism harness: same inputs over same ticks produce the same state summary/hash - [x] Verify: `cargo fmt --check` / `cargo check --workspace` / `cargo clippy --workspace` / strict foundation clippy / `cargo test -p sim` - [x] Stable asset registry with UUIDs + import settings in asset browser details - [x] Asset Browser expandable model subasset shelves, independent mesh/material/texture thumbnails, staged import/material details with shader-schema parameters, context actions, and trash-first file removal - [x] Prefab instances (`PrefabInstance`) + save-as-prefab + unpack - [x] Unsaved-scene confirm on New/Open/Recent - [x] Hierarchy multi-select, reparent undo, multi-entity gizmo transform - [x] Typed inspector undo (light, rigid body, collider, primitive, material, static mesh renderer) + registry-driven Add Component footer - [x] Gameplay authoring markers + visualizers (`WeaponSpawn`, `TriggerVolume`, etc.) - [x] Command palette execution; PIE sim step (F7); `xtask validate-levels` - [x] ADRs 0005–0012 (prefab/registry, scene schema, EditorPlugin, editor structure, authoring/hydration, ActorKind, sun policy, zero-debt) - [x] `ActorInspectorSection` registry + game demo section (`game::editor_ext`) - [x] Command palette: reset lighting, group selection, focus selection - [x] CI: `cargo test -p shared`, scene authoring-only check on repo level, `cargo check -p editor --features dev` - [x] `scene` crate schema stamp/migrate/validate on save/load + CI `validate-levels` - [x] Project Settings draft + Apply (HDR/swapchain safe); File → Project New/Open - [x] Editor lib/bin split + `EditorPluginGroup`; game EditorPlugin dogfood panel - [x] FBX/glTF model import + normalized `StaticMeshRenderer` placement; explicit scene-instance load via `bevy_ufbx` / `ModelRef` - [x] Asset browser model thumbnails (unified `assets/thumbnails/` pipeline; `ThumbnailState` cache; FBX via `FbxThumbnailSource`) - [x] Material assets (`assets/materials/*.ron`, inspector picker, drag-drop to selection) - [x] Prefab instance overrides v2 (transform/material/child visibility apply-revert groups) - [x] Advanced rendering: `GiMode`, post-process volumes, Rendering panel, requested/effective render stack, Solari integration, emissive materials, post FX assets ([ADR 0013](docs/adr/0013-rendering-tiers-and-post-process-volumes.md), [ADR 0016](docs/adr/0016-unified-rendering-contract.md), [rendering guide](docs/editor/rendering.md)) - [x] Static mesh renderer component, generated normalized mesh artifacts, source/one-actor hierarchy placement, and inspector renderer slots ([ADR 0017](docs/adr/0017-normalized-static-mesh-assets.md)) - [x] Componentized actor inspector, unified component cards, thumbnail static mesh slots, imported asset Browse/Locate/Clear refs with inherited source defaults, collider split, shader-aware actor material data, and texture picker/drop refs ([ADR 0018](docs/adr/0018-componentized-actor-inspector-and-materials.md)) - [x] Brush authoring schema MVP with `ActorKind::Brush`, cube `BrushDesc`, generated mesh hydration, scene migration, and inspector Add Component support ([ADR 0021](docs/adr/0021-brush-authoring-schema.md)) ## Notes / Future Work - Crouch lowers the camera and movement speed; the collider stays full-height for stability. - The editor asset browser is filesystem-backed with folder/tree navigation, grid/list views, texture and model thumbnails (glTF albedo fast-path; offscreen render studio for FBX and untextured models), material sphere thumbnails, search/filter/sort controls, expandable model subasset shelves, and a staged details pane. **File → Import Assets** accepts glTF/GLB and **FBX** (binary; copies sibling `.fbm` texture folders when present). Model assets generate normalized static mesh manifests under `assets/meshes/generated/`; drag/drop uses `StaticMeshRenderer` by default with imported asset refs and optional separate static mesh collider components. Expanded mesh subassets generate independent thumbnails and can be placed independently. Asset details can switch placement to **Scene Instance** for `ModelRef`/`SceneRoot` playback, shared material assets can be edited from the browser, and delete actions move files to `assets/.trash/`. Skeletal animation playback is not supported by the static mesh path yet. - Prefab instances use stable asset IDs (`PrefabInstance` + registry) with serialized `PrefabOverrides` (transform, material, per-child visibility by name). Inspector **Apply/Revert** per field group; **Unpack** removes the instance link. - Material assets are RON files under `assets/materials/`; shader schemas live under `assets/shaders/`. `MaterialDesc` stores shader kind, typed parameters, texture bindings, and StandardMaterial fields. The Asset Browser material details editor can apply shader schemas, edit typed parameters/textures, and regenerate sphere thumbnails. Actor inspector material edits live on the actor `MaterialDesc`; static mesh source-material refs remain imported defaults and shared material assets are edited from the Asset Browser. - Per-field reflect undo for all components remains future work; typed `shared` inspectors cover the common authoring path. - The authoring/hydration layer is intentionally small so richer asset workflows (terrain, material graphs, lighting profiles) can be added without changing the scene format foundation. - The M2 sim split keeps camera pitch as local presentation state; body yaw and movement are fixed-step intent consumers. M3 should map the existing protocol envelopes onto Lightyear and add server-authoritative snapshots/prediction rather than broadening the local input path ad hoc. - The editor renders egui on a dedicated full-window `Camera2d` (`PrimaryEguiContext`, `RenderLayers::none()`, `auto_create_primary_context = false`) so the 3D viewport camera can be cropped to the viewport panel without cropping egui itself. Attaching egui to a viewport-cropped 3D camera makes `egui_dock` lay out into a NaN rect and panics in `advance_cursor_after_rect`.