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
+130
View File
@@ -0,0 +1,130 @@
// compat/bsn-smoke shim: expose crate-root support types (demo custom components).
// The generated code below this line is unchanged.
#![allow(unused_imports)] // the crate::* glob also provides the Bevy prelude names
use crate::*;
use bevy::prelude::*;
use bevy::scene::bsn_list;
pub fn scene(
meshes: &mut Assets<Mesh>,
materials: &mut Assets<StandardMaterial>,
) -> impl SceneList {
bsn_list! {
Name("Camera")
Transform {
translation: Vec3::new(7.5, 5.0, 12.0),
rotation: Quat::from_euler(EulerRot::XYZ, -16.0f32.to_radians(), 30.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Camera3d::default()
--
Name("Sun")
Transform {
translation: Vec3::new(0.0, 0.0, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, -55.0f32.to_radians(), -25.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
DirectionalLight { color: Color::srgba(1.0, 0.95, 0.85, 1.0), illuminance: 11000.0, shadow_maps_enabled: true }
--
Name("Ground")
Transform {
translation: Vec3::new(0.0, 0.0, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Mesh3d({ meshes.add(Plane3d::new(Vec3::Y, Vec2::new(13.0, 13.0))) })
MeshMaterial3d::<StandardMaterial>({ materials.add(StandardMaterial {
base_color: Color::srgba(0.31, 0.48, 0.23, 1.0),
metallic: 0.0,
perceptual_roughness: 0.92,
unlit: false,
..default()
}) })
--
Name("Shrub")
Transform {
translation: Vec3::new(-3.4, 1.0, 1.8),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.25, 0.9, 1.25),
}
Mesh3d({ meshes.add(Sphere::new(1.0)) })
MeshMaterial3d::<StandardMaterial>({ materials.add(StandardMaterial {
base_color: Color::srgba(0.24, 0.45, 0.22, 1.0),
metallic: 0.0,
perceptual_roughness: 0.85,
unlit: false,
..default()
}) })
--
Name("Flower Stem")
Transform {
translation: Vec3::new(2.6, 0.75, 1.4),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Mesh3d({ meshes.add(Cylinder::new(0.09, 1.5)) })
MeshMaterial3d::<StandardMaterial>({ materials.add(StandardMaterial {
base_color: Color::srgba(0.35, 0.55, 0.27, 1.0),
metallic: 0.0,
perceptual_roughness: 0.8,
unlit: false,
..default()
}) })
Children [
Name("Flower Bloom")
Transform {
translation: Vec3::new(0.0, 1.0, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 0.75, 1.0),
}
Mesh3d({ meshes.add(Sphere::new(0.42)) })
MeshMaterial3d::<StandardMaterial>({ materials.add(StandardMaterial {
base_color: Color::srgba(0.91, 0.42, 0.65, 1.0),
metallic: 0.0,
perceptual_roughness: 0.4,
unlit: false,
..default()
}) })
]
--
Name("Orbit Crystal")
Transform {
translation: Vec3::new(0.0, 2.0, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Mesh3d({ meshes.add(Sphere::new(0.8)) })
MeshMaterial3d::<StandardMaterial>({ materials.add(StandardMaterial {
base_color: Color::srgba(0.27, 0.76, 0.69, 1.0),
metallic: 0.15,
perceptual_roughness: 0.2,
unlit: false,
..default()
}) })
forge_demo::Orbit { radius: 2.4, speed: 0.45 }
--
Name("Lantern Post")
Transform {
translation: Vec3::new(3.8, 1.2, -1.2),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Mesh3d({ meshes.add(Cylinder::new(0.12, 2.4)) })
MeshMaterial3d::<StandardMaterial>({ materials.add(StandardMaterial {
base_color: Color::srgba(0.18, 0.2, 0.25, 1.0),
metallic: 0.6,
perceptual_roughness: 0.35,
unlit: false,
..default()
}) })
Children [
Name("Lantern Glow")
Transform {
translation: Vec3::new(0.0, 1.25, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
PointLight { color: Color::srgba(1.0, 0.82, 0.55, 1.0), intensity: 600.0, range: 12.0, shadow_maps_enabled: true }
]
}
}
+191
View File
@@ -0,0 +1,191 @@
// compat/bsn-smoke shim: expose crate-root support types (demo custom components).
// The generated code below this line is unchanged.
#![allow(unused_imports)] // the crate::* glob also provides the Bevy prelude names
use crate::*;
use bevy::prelude::*;
use bevy::scene::bsn_list;
pub fn scene() -> impl SceneList {
bsn_list! {
Name("Backdrop")
Transform {
translation: Vec3::new(0.0, 0.0, -30.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/background.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(960.0, 540.0)) }, flip_x: false, flip_y: false }
--
Name("Camera")
Transform {
translation: Vec3::new(0.0, 0.0, 10.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Camera2d
--
Name("Title")
Transform {
translation: Vec3::new(0.0, 214.0, 5.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Text2d("Bevy Forge Platformer")
TextFont { font_size: FontSize::Px(44.0) }
TextColor(Color::srgba(0.96, 0.94, 0.88, 1.0))
--
Name("Hint")
Transform {
translation: Vec3::new(0.0, 176.0, 5.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Text2d("Collect the coins!")
TextFont { font_size: FontSize::Px(20.0) }
TextColor(Color::srgba(0.95, 0.67, 0.25, 1.0))
--
Name("Ground 1")
Transform {
translation: Vec3::new(-336.0, -224.0, -2.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/tile.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(96.0, 96.0)) }, flip_x: false, flip_y: false }
--
Name("Ground 2")
Transform {
translation: Vec3::new(-240.0, -224.0, -2.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/tile.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(96.0, 96.0)) }, flip_x: false, flip_y: false }
--
Name("Ground 3")
Transform {
translation: Vec3::new(-144.0, -224.0, -2.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/tile.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(96.0, 96.0)) }, flip_x: false, flip_y: false }
--
Name("Ground 4")
Transform {
translation: Vec3::new(-48.0, -224.0, -2.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/tile.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(96.0, 96.0)) }, flip_x: false, flip_y: false }
--
Name("Ground 5")
Transform {
translation: Vec3::new(48.0, -224.0, -2.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/tile.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(96.0, 96.0)) }, flip_x: false, flip_y: false }
--
Name("Ground 6")
Transform {
translation: Vec3::new(144.0, -224.0, -2.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/tile.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(96.0, 96.0)) }, flip_x: false, flip_y: false }
--
Name("Ground 7")
Transform {
translation: Vec3::new(240.0, -224.0, -2.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/tile.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(96.0, 96.0)) }, flip_x: false, flip_y: false }
--
Name("Ground 8")
Transform {
translation: Vec3::new(336.0, -224.0, -2.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/tile.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(96.0, 96.0)) }, flip_x: false, flip_y: false }
--
Name("Ledge 1")
Transform {
translation: Vec3::new(-48.0, -64.0, -2.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/tile.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(96.0, 96.0)) }, flip_x: false, flip_y: false }
--
Name("Ledge 2")
Transform {
translation: Vec3::new(48.0, -64.0, -2.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/tile.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(96.0, 96.0)) }, flip_x: false, flip_y: false }
--
Name("Ledge 3")
Transform {
translation: Vec3::new(144.0, -64.0, -2.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/tile.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(96.0, 96.0)) }, flip_x: false, flip_y: false }
--
Name("Player")
Transform {
translation: Vec3::new(-192.0, -128.0, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/player.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(64.0, 96.0)) }, flip_x: false, flip_y: false }
Children [
Name("Hat")
Transform {
translation: Vec3::new(0.0, 42.0, 1.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/hat.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(56.0, 28.0)) }, flip_x: false, flip_y: false }
]
--
Name("Coin 1")
Transform {
translation: Vec3::new(-288.0, -104.0, 1.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/coin.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(48.0, 48.0)) }, flip_x: false, flip_y: false }
--
Name("Coin 2")
Transform {
translation: Vec3::new(48.0, 28.0, 1.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/coin.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(48.0, 48.0)) }, flip_x: false, flip_y: false }
--
Name("Coin 3")
Transform {
translation: Vec3::new(240.0, -96.0, 1.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/coin.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(48.0, 48.0)) }, flip_x: false, flip_y: false }
--
Name("Crate")
Transform {
translation: Vec3::new(352.0, -144.0, -1.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/crate.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(64.0, 64.0)) }, flip_x: false, flip_y: false }
--
Name("Crate 2")
Transform {
translation: Vec3::new(352.0, -80.0, -1.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0f32.to_radians(), 0.0f32.to_radians(), 0.0f32.to_radians()),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/crate.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0), custom_size: { Some(Vec2::new(64.0, 64.0)) }, flip_x: false, flip_y: false }
}
}
+334
View File
@@ -0,0 +1,334 @@
//! Compile-smoke crate for Bevy Forge's generated code.
//!
//! This crate is intentionally **not** a workspace member: it is the only place
//! in the repository that depends on Bevy (0.20.0). It compiles:
//!
//! * hand-written scenes that mirror `ExportFormat::Bsn` output for every
//! supported component, hierarchy shape and asset pattern (this file), and
//! * the real files written by `scripts/check-generated-code.sh`
//! (`src/generated.rs`, `src/generated_2d.rs`).
//!
//! See `README.md` for the feature set, commands and expected build cost.
//! The generated code targets the compile-verified forms in `.swarm/recon/R02.md`.
use bevy::prelude::*;
use bevy::scene::bsn;
use bevy::scene::bsn_list;
/// Bevy Forge's BSN export for `examples/demo-project/scenes/garden.forge.ron`.
///
/// Committed with a harmless starter scene so this crate compiles before the
/// end-to-end gate has run; `scripts/check-generated-code.sh` replaces it with
/// the real CLI export.
#[path = "generated.rs"]
pub mod generated;
/// Bevy Forge's BSN export for `examples/demo-project/scenes/platformer.forge.ron`.
#[path = "generated_2d.rs"]
pub mod generated_2d;
/// Imperative `Commands::spawn` export shape (`ExportFormat::Rust`), mirroring
/// what the CLI emits. It is compiled here so the secondary export format is
/// verified against real Bevy types as well.
pub mod rust_export;
/// A custom component with fields, as emitted for `ComponentData::Custom`.
///
/// `Component + Default + Clone` is enough for Bevy 0.20's blanket
/// `FromTemplate` implementation (see R02 §4), so generated
/// `Health { current: 10.0, max: 10.0 }` patches compile.
#[derive(Component, Default, Clone)]
pub struct Health {
pub current: f32,
pub max: f32,
}
/// A unit custom component: Bevy Forge emits the bare type path when a custom
/// component has no fields.
#[derive(Component, Default, Clone)]
pub struct Marker;
/// Support types from `examples/demo-project/src/main.rs`.
///
/// The demo garden scene carries a custom `forge_demo::Orbit` component, so the
/// generated garden BSN references that path. Providing the module here lets the
/// export compile in the smoke crate without editing the generated file.
pub mod forge_demo {
use bevy::prelude::*;
/// Slowly orbits an entity around the world origin (demo project component).
#[derive(Component, Default, Clone)]
pub struct Orbit {
pub radius: f32,
pub speed: f32,
}
}
/// 2D scene: single root, `Name`/`Transform`/`Sprite` with image and braced
/// `custom_size`, a custom component with fields, a unit custom component, and
/// nested `Children` (including a visibility patch on a grandchild).
///
/// Mirrors the shape generated for a typical platformer root entity.
pub fn scene_2d() -> impl Scene {
bsn! {
Name("Player")
Transform {
translation: Vec3::new(0.0, 1.0, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite {
image: "textures/player.png",
color: Color::srgba(1.0, 1.0, 1.0, 1.0),
custom_size: { Some(Vec2::new(64.0, 64.0)) },
flip_x: false,
flip_y: false,
}
Health { current: 10.0, max: 10.0 }
forge_demo::Orbit { radius: 2.5, speed: 0.8 }
Children [
Name("Hat")
Transform {
translation: Vec3::new(0.0, 40.0, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { color: Color::srgba(1.0, 0.5, 0.2, 1.0) }
Marker
Children [
Name("Glow")
Transform {
translation: Vec3::new(0.0, 0.5, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0),
scale: Vec3::new(0.5, 0.5, 1.0),
}
Sprite { color: Color::srgba(1.0, 1.0, 0.4, 0.6) }
Visibility::Visible
]
]
}
}
/// Multiple roots: the generator emits `bsn_list!` plus `--` separators and a
/// `-> impl SceneList` return type.
pub fn scene_2d_list() -> impl SceneList {
bsn_list! {
Name("Left")
Transform {
translation: Vec3::new(-2.0, 0.0, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { image: "textures/tile.png", color: Color::srgba(1.0, 1.0, 1.0, 1.0) }
--
Name("Right")
Transform {
translation: Vec3::new(2.0, 0.0, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { color: Color::srgba(0.9, 0.6, 0.3, 1.0) }
Children [
Name("Right Child")
Transform {
translation: Vec3::new(0.0, 1.0, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Sprite { color: Color::srgba(0.2, 0.8, 0.9, 1.0) }
]
}
}
/// Text: `Text2d` payload plus `TextFont`/`TextColor` patches.
pub fn scene_text() -> impl Scene {
bsn! {
Name("Title")
Text2d("Bevy Forge\nHello \"BSN\"")
TextFont { font_size: FontSize::Px(32.0) }
TextColor(Color::srgba(1.0, 0.9, 0.8, 1.0))
Transform {
translation: Vec3::new(0.0, 200.0, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0),
scale: Vec3::new(1.0, 1.0, 1.0),
}
}
}
/// Bare `Camera2d` patch, as emitted for 2D scenes.
pub fn scene_camera_2d() -> impl Scene {
bsn! {
Name("Main Camera")
Camera2d
Transform {
translation: Vec3::new(0.0, 0.0, 999.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0),
scale: Vec3::new(1.0, 1.0, 1.0),
}
}
}
/// 3D stage: `Camera3d::default()`, both light types and the `Visibility`
/// variants on children (a single-root scene with three child entities).
pub fn scene_3d_stage() -> impl Scene {
bsn! {
Name("Stage")
Transform {
translation: Vec3::new(0.0, 0.0, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Children [
Name("Main Camera")
Camera3d::default()
Transform {
translation: Vec3::new(0.0, 4.0, 10.0),
rotation: Quat::from_euler(EulerRot::XYZ, -0.35, 0.0, 0.0),
scale: Vec3::new(1.0, 1.0, 1.0),
}
--
Name("Sun")
DirectionalLight {
color: Color::srgb(1.0, 0.95, 0.9),
illuminance: 10000.0,
shadow_maps_enabled: true,
}
Transform {
translation: Vec3::new(0.0, 8.0, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, -0.8, 0.4, 0.0),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Visibility::Visible
--
Name("Lantern")
PointLight {
color: Color::srgb(1.0, 0.6, 0.25),
intensity: 800.0,
range: 20.0,
shadow_maps_enabled: true,
}
Transform {
translation: Vec3::new(1.5, 1.0, 1.5),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Visibility::Inherited
]
}
}
/// 3D primitives and inline materials: the generated function takes the
/// `meshes`/`materials` asset parameters when a scene contains meshes.
///
/// Covers all five `PrimitiveShape` variants from the codegen contract.
pub fn scene_3d_primitives(
meshes: &mut Assets<Mesh>,
materials: &mut Assets<StandardMaterial>,
) -> impl Scene {
bsn! {
Name("Primitives")
Transform {
translation: Vec3::new(0.0, 0.0, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0),
scale: Vec3::new(1.0, 1.0, 1.0),
}
Children [
Name("Cube")
Mesh3d({ meshes.add(Cuboid::new(1.0, 1.0, 1.0)) })
MeshMaterial3d::<StandardMaterial>({ materials.add(StandardMaterial {
base_color: Color::srgb(0.9, 0.35, 0.3),
metallic: 0.0,
perceptual_roughness: 0.5,
unlit: false,
..default()
}) })
Transform {
translation: Vec3::new(-2.0, 0.5, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0),
scale: Vec3::new(1.0, 1.0, 1.0),
}
--
Name("Sphere")
Mesh3d({ meshes.add(Sphere::new(0.5)) })
MeshMaterial3d::<StandardMaterial>({ materials.add(StandardMaterial {
base_color: Color::srgb(0.3, 0.6, 0.9),
metallic: 0.0,
perceptual_roughness: 0.5,
unlit: false,
..default()
}) })
Transform {
translation: Vec3::new(-1.0, 0.5, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0),
scale: Vec3::new(1.0, 1.0, 1.0),
}
--
Name("Plane")
Mesh3d({ meshes.add(Plane3d::new(Vec3::Y, Vec2::new(6.0, 6.0))) })
MeshMaterial3d::<StandardMaterial>({ materials.add(StandardMaterial {
base_color: Color::srgb(0.35, 0.5, 0.3),
metallic: 0.0,
perceptual_roughness: 0.9,
unlit: false,
..default()
}) })
Transform {
translation: Vec3::new(0.0, 0.0, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0),
scale: Vec3::new(1.0, 1.0, 1.0),
}
--
Name("Cylinder")
Mesh3d({ meshes.add(Cylinder::new(0.4, 1.6)) })
MeshMaterial3d::<StandardMaterial>({ materials.add(StandardMaterial {
base_color: Color::srgb(0.8, 0.7, 0.25),
metallic: 0.1,
perceptual_roughness: 0.4,
unlit: false,
..default()
}) })
Transform {
translation: Vec3::new(1.0, 0.8, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0),
scale: Vec3::new(1.0, 1.0, 1.0),
}
--
Name("Capsule")
Mesh3d({ meshes.add(Capsule3d::new(0.3, 1.0)) })
MeshMaterial3d::<StandardMaterial>({ materials.add(StandardMaterial {
base_color: Color::srgb(0.55, 0.4, 0.8),
metallic: 0.0,
perceptual_roughness: 0.6,
unlit: false,
..default()
}) })
Transform {
translation: Vec3::new(2.0, 0.8, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0),
scale: Vec3::new(1.0, 1.0, 1.0),
}
]
}
}
/// Asset mesh: `Mesh3d("<path>")` uses a path handle while the material is an
/// inline `materials.add(..)` handle (the generator never emits asset material
/// paths, which would need a project-registered loader at runtime).
pub fn scene_asset_mesh(materials: &mut Assets<StandardMaterial>) -> impl Scene {
bsn! {
Name("Crate")
Mesh3d("models/crate.gltf#Mesh0/Primitive0")
MeshMaterial3d::<StandardMaterial>({ materials.add(StandardMaterial {
base_color: Color::srgb(0.6, 0.45, 0.3),
metallic: 0.0,
perceptual_roughness: 0.8,
unlit: false,
..default()
}) })
Transform {
translation: Vec3::new(0.0, 0.5, 0.0),
rotation: Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0),
scale: Vec3::new(1.0, 1.0, 1.0),
}
}
}
+98
View File
@@ -0,0 +1,98 @@
//! Compile check for Bevy Forge's secondary export format: imperative
//! `Commands::spawn` code (`ExportFormat::Rust`, contract §3.2).
//!
//! This mirrors what a user gets when pasting the Rust export into a system.
//! It is hand-written from the contract shape (A04's generator output is
//! verified separately by its tests); the value here is that every Bevy API call
//! the exporter emits is checked against real Bevy 0.20.0.
use bevy::prelude::*;
use crate::Health;
/// Representative Rust export: a 2D sprite with a child and a custom component,
/// a primitive mesh with an inline material, text and a 3D camera + light.
///
/// The parameter set matches the contract: `commands` always, `asset_server`
/// only because a sprite image is loaded, `meshes`/`materials` only because a
/// primitive mesh is spawned.
pub fn scene(
commands: &mut Commands,
asset_server: &AssetServer,
meshes: &mut Assets<Mesh>,
materials: &mut Assets<StandardMaterial>,
) {
commands
.spawn((
Name::new("Player"),
Transform::from_xyz(0.0, 1.0, 0.0)
.with_rotation(Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0))
.with_scale(Vec3::new(1.0, 1.0, 1.0)),
Sprite {
image: asset_server.load("textures/player.png"),
color: Color::srgba(1.0, 1.0, 1.0, 1.0),
custom_size: Some(Vec2::new(64.0, 64.0)),
..default()
},
Health {
current: 10.0,
max: 10.0,
},
))
.with_children(|parent| {
parent.spawn((
Name::new("Hat"),
Transform::from_xyz(0.0, 40.0, 0.0),
Sprite {
color: Color::srgba(1.0, 0.5, 0.2, 1.0),
..default()
},
));
});
commands.spawn((
Name::new("Cube"),
Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
MeshMaterial3d(materials.add(StandardMaterial {
base_color: Color::srgb(0.9, 0.35, 0.3),
metallic: 0.0,
perceptual_roughness: 0.5,
unlit: false,
..default()
})),
Transform::from_xyz(0.0, 0.5, 0.0),
));
commands.spawn((
Name::new("Title"),
Text2d::new("Bevy Forge"),
TextFont {
font_size: FontSize::Px(32.0),
..default()
},
TextColor(Color::WHITE),
Transform::from_xyz(0.0, 2.0, 0.0),
));
commands.spawn((
Name::new("Main Camera"),
Camera3d::default(),
Transform::from_xyz(0.0, 4.0, 10.0),
));
commands.spawn((
Name::new("Sun"),
DirectionalLight {
illuminance: 10000.0,
shadow_maps_enabled: true,
..default()
},
Transform::default(),
));
commands.spawn((
Name::new("Hidden Helper"),
Transform::default(),
Visibility::Hidden,
));
}