Bevy Forge 0.1.0: visual scene editor for Bevy 0.20 BSN
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user