Bevy Forge 0.1.0: visual scene editor for Bevy 0.20 BSN

This commit is contained in:
Emil Shanaty
2026-10-27 18:52:10 +03:00
commit 13bf16d510
79 changed files with 44353 additions and 0 deletions
+126
View File
@@ -0,0 +1,126 @@
# bsn-smoke — Bevy 0.20 generated-code compile check
`bsn-smoke` is Bevy Forge's proof that exported scenes are **real Bevy 0.20 code**, not
just plausible-looking text. It is the only crate in this repository that depends on
Bevy; it is excluded from the workspace (`Cargo.toml` → `exclude = ["compat/bsn-smoke"]`)
so the editor, core and CLI crates stay Bevy-free and build in seconds.
```text
compat/bsn-smoke/
├── Cargo.toml standalone package (empty [workspace]), pinned Bevy 0.20.0
├── Cargo.lock lockfile for the verification dependency graph
├── src/lib.rs hand-written scenes mirroring every codegen shape + support types
├── src/generated.rs BSN export of the 3D garden demo (written by the gate script)
├── src/generated_2d.rs BSN export of the 2D platformer demo (written by the gate script)
└── src/rust_export.rs imperative `Commands::spawn` export shape, also compiled
```
## What is verified
Compile-checked against `bevy 0.20.0` (`default-features = false`):
- **Roots:** single root `bsn!` / `-> impl Scene`; multiple roots `bsn_list!` /
`-> impl SceneList` with `--` separators.
- **Hierarchy:** nested `Children [ ... -- ... ]`, including a `Visibility` patch on a
grandchild and components before/after `Children`.
- **Names and transforms:** `Name("...")`, full three-field `Transform` with
`Quat::from_euler(EulerRot::XYZ, x.to_radians(), ...)`.
- **Sprites:** image path handles, `Color::srgba`, `flip_x`/`flip_y`, and braced
`custom_size: { Some(Vec2::new(w, h)) }` (the brace form is mandatory in BSN patches).
- **Text:** `Text2d`, `TextFont { font_size: FontSize::Px(..) }`, `TextColor`.
- **Cameras:** bare `Camera2d`, `Camera3d::default()`.
- **Lights:** `PointLight` (color/intensity/range/shadows) and `DirectionalLight`
(color/illuminance/shadows).
- **Custom components:** struct-with-fields patches (`Health { current: 10.0, max: 10.0 }`,
`forge_demo::Orbit { radius: 2.4, speed: 0.45 }`) and bare unit-component patches
(`Marker`).
- **3D meshes:** all five primitive shapes (`Cuboid`, `Sphere`, `Plane3d`, `Cylinder`,
`Capsule3d`) with braced method-call handles (`Mesh3d({ meshes.add(..) })`) and inline
materials (`MeshMaterial3d::<StandardMaterial>({ materials.add(StandardMaterial { ..default() }) })`),
plus an asset mesh path (`Mesh3d("models/...#Mesh0/Primitive0")`) with an inline material.
- **Generated signatures:** mesh scenes take `&mut Assets<Mesh>` and
`&mut Assets<StandardMaterial>` only when needed.
- **Imperative Rust export shape** (`src/rust_export.rs`): `commands.spawn`,
`with_children`, `Name::new`, `Transform::from_xyz(..).with_rotation(..).with_scale(..)`,
`Sprite { ..default() }`, `Mesh3d`/`MeshMaterial3d`, `Text2d::new`, `TextFont`,
`TextColor`, `Camera3d::default()`, lights and `Visibility`.
The committed `src/generated*.rs` files are the CLI exports produced by the last
successful gate run (plus the shim described below), so the crate compiles on a fresh
checkout even before the script runs. `scripts/check-generated-code.sh` regenerates them
from the demo project and recompiles, verifying the generator + demo scenes end to end.
The exports are deterministic, so re-running the gate leaves the files unchanged.
### Compatibility shim for custom type paths
Generated BSN references custom components by their project type path, e.g.
`forge_demo::Orbit`. In a real Bevy project the generated function is pasted where that
path is already in scope. Here the generated file is a module, so the gate script prepends
four lines (`#![allow(unused_imports)]`, two comments, and `use crate::*;`) to expose the
crate-root support types; **the generated code itself is appended unchanged**. `lib.rs`
provides `forge_demo::Orbit` with the same fields as `examples/demo-project/src/main.rs`.
If a demo scene introduces another custom type, add its support type to `lib.rs`.
## Bevy version and feature set
Pinned to **Bevy 0.20.0** through `Cargo.lock`. Feature set (chosen by
`.swarm/recon/R02.md` to keep compile time and APIs aligned with the verified BSN forms):
```toml
bevy = { version = "0.20", default-features = false, features = [
"std", "bevy_scene", "bevy_sprite", "bevy_asset", "bevy_color", "bevy_text",
"bevy_pbr", "bevy_mesh", "bevy_camera", "bevy_light", "bevy_image",
] }
```
No windowing or audio backends are compiled in and no GPU device is initialized: this is a
**compile** check that needs no display, asset files or `App`.
## Running
Directly (the load-bearing check):
```bash
cd compat/bsn-smoke
CARGO_NET_OFFLINE=true cargo check
```
End to end (generator → CLI → Bevy compile):
```bash
bash scripts/check-generated-code.sh
```
The script exports `examples/demo-project/scenes/garden.forge.ron` and
`.../platformer.forge.ron` with `cargo run -q -p bevy_forge_cli -- export ... --format bsn`,
writes the results into this crate, and runs the check. It fails loudly on any error; if
the CLI or demo project does not exist yet it fails with a clear message instead of
silently passing. `FORGE_SKIP_GENERATED=1` skips with an explicit message (bootstrap only);
`FORGE_SKIP_SECOND_SCENE=1` skips the 2D export; `FORGE_GARDEN_SCENE` /
`FORGE_PLATFORMER_SCENE` override the scene paths; `CARGO_TARGET_DIR` is passed through.
## Expected cost
Measured on this Linux machine (Rust 1.99.1, 2026-10):
| Build | Wall time | `target/` size |
|---|---|---|
| First `cargo check`, cold target dir | **2 min 15 s** | ~803 MB |
| Subsequent `cargo check`, warm target dir | < 1 s | — |
| Reusing an existing Bevy 0.20 `target/` (e.g. recon) | ~1 s | — |
`compat/bsn-smoke/target/` is ignored by `.gitignore`; never commit it. If you already
have a warm Bevy 0.20 target directory, point `CARGO_TARGET_DIR` at it to avoid the cold
build (the dependency feature set must match exactly).
## Runtime caveats (compile ≠ loaded assets)
- Asset **paths** (`Sprite { image: "textures/x.png" }`, `Mesh3d("models/...gltf#...")`)
compile, but Bevy turns them into `HandleTemplate::Path` values that are resolved by the
`AssetServer` at spawn time. If the project has no loader/asset for the path, the handle
stays loading/failed and nothing renders — it is not a compile error (see R02 §9).
- Bevy Forge never emits material **asset paths** (`MeshMaterial3d::<StandardMaterial>("...")`),
because those need a project-registered material loader; materials are always inline
`materials.add(StandardMaterial { ..default() })` handles.
- The check is compile-only: it does not spawn the scenes or validate asset existence.
Asset references are validated separately by `forge validate` / the editor.