Blacksite/README.md
Rbanh f29712d158
Some checks are pending
CI / Format, lint, test, build (push) Waiting to run
Initial project import
2026-06-05 21:44:45 -04:00

315 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.
- 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 |
| 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 |
| Viewport toolbar (sun / brush / box icons) | Shading: Lit, Unlit (albedo), Colliders (mesh off) |
| Viewport eye/options | Toggle actor root icon categories, 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, inspect import settings; drag assets into the viewport to place |
| Asset Browser texture action | Assign base-color texture to selection |
| `Ctrl+P` | Command palette (`scene.reset_lighting`, `selection.group`, `selection.focus`, 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 | Pinned search/add control for registered authoring, rendering, physics, and gameplay components with undo |
| Edit → Project Settings… | Edit `assets/project.ron` (rendering, physics, input) |
### 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.
- 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.
- 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 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] 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 00050012 (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 material data, and per-actor material overrides ([ADR 0018](docs/adr/0018-componentized-actor-inspector-and-materials.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), search/filter/sort controls, and a 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. Asset details can switch placement to **Scene Instance** for `ModelRef`/`SceneRoot`
playback. 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. Actor inspector edits create per-actor `MaterialOverride` data for static mesh slots instead of mutating shared material assets.
- 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`.