This commit is contained in:
Jan Hohenheim 2025-07-18 16:55:59 +08:00 committed by GitHub
commit 6981cd4e63
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 145 additions and 75 deletions

View File

@ -605,6 +605,10 @@ bevy_state = { path = "crates/bevy_state", version = "0.17.0-dev", default-featu
bevy_asset = { path = "crates/bevy_asset", version = "0.17.0-dev", default-features = false } bevy_asset = { path = "crates/bevy_asset", version = "0.17.0-dev", default-features = false }
bevy_reflect = { path = "crates/bevy_reflect", version = "0.17.0-dev", default-features = false } bevy_reflect = { path = "crates/bevy_reflect", version = "0.17.0-dev", default-features = false }
bevy_image = { path = "crates/bevy_image", version = "0.17.0-dev", default-features = false } bevy_image = { path = "crates/bevy_image", version = "0.17.0-dev", default-features = false }
# Opt into the new default glTF coordinate conversion that will be the default in 0.18.0
bevy_gltf = { path = "crates/bevy_gltf", version = "0.17.0-dev", default-features = false, features = [
"gltf_convert_coordinates_default",
] }
bevy_gizmos = { path = "crates/bevy_gizmos", version = "0.17.0-dev", default-features = false } bevy_gizmos = { path = "crates/bevy_gizmos", version = "0.17.0-dev", default-features = false }
# Needed to poll Task examples # Needed to poll Task examples
futures-lite = "2.0.1" futures-lite = "2.0.1"

View File

@ -11,7 +11,7 @@ use bevy::{
}; };
/// The initial position of the camera. /// The initial position of the camera.
const CAMERA_INITIAL_POSITION: Vec3 = vec3(-0.4, 0.0, 0.0); const CAMERA_INITIAL_POSITION: Vec3 = vec3(0.4, 0.0, 0.0);
/// The current settings of the app, as chosen by the user. /// The current settings of the app, as chosen by the user.
#[derive(Resource)] #[derive(Resource)]
@ -107,7 +107,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, app_status: Res
commands.spawn(( commands.spawn((
SceneRoot(asset_server.load("models/AnisotropyBarnLamp/AnisotropyBarnLamp.gltf#Scene0")), SceneRoot(asset_server.load("models/AnisotropyBarnLamp/AnisotropyBarnLamp.gltf#Scene0")),
Transform::from_xyz(0.0, 0.07, -0.13), Transform::from_xyz(0.0, 0.07, 0.13),
Scene::BarnLamp, Scene::BarnLamp,
)); ));

View File

@ -279,9 +279,13 @@ fn setup(
} }
// Flight Helmet // Flight Helmet
commands.spawn(SceneRoot(asset_server.load( commands.spawn((
GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"), SceneRoot(
))); asset_server
.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")),
),
Transform::default().looking_to(Vec3::Z, Vec3::Y),
));
// Light // Light
commands.spawn(( commands.spawn((

View File

@ -110,7 +110,7 @@ fn setup_terrain_scene(
), ),
Transform::from_xyz(-1.0, 0.0, -0.5) Transform::from_xyz(-1.0, 0.0, -0.5)
.with_scale(Vec3::splat(0.5)) .with_scale(Vec3::splat(0.5))
.with_rotation(Quat::from_rotation_y(PI / 2.0)), .with_rotation(Quat::from_rotation_y(-PI / 2.0)),
)); ));
} }

View File

@ -66,9 +66,12 @@ fn setup_terrain_scene(
)); ));
// Terrain // Terrain
commands.spawn(SceneRoot(asset_server.load( commands.spawn((
GltfAssetLabel::Scene(0).from_asset("models/terrain/Mountains.gltf"), SceneRoot(
))); asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/terrain/Mountains.gltf")),
),
Transform::default().looking_to(Vec3::Z, Vec3::Y),
));
// Sky // Sky
commands.spawn(( commands.spawn((

View File

@ -356,9 +356,12 @@ fn add_camera(commands: &mut Commands, asset_server: &AssetServer, color_grading
fn add_basic_scene(commands: &mut Commands, asset_server: &AssetServer) { fn add_basic_scene(commands: &mut Commands, asset_server: &AssetServer) {
// Spawn the main scene. // Spawn the main scene.
commands.spawn(SceneRoot(asset_server.load( commands.spawn((
GltfAssetLabel::Scene(0).from_asset("models/TonemappingTest/TonemappingTest.gltf"), SceneRoot(asset_server.load(
))); GltfAssetLabel::Scene(0).from_asset("models/TonemappingTest/TonemappingTest.gltf"),
)),
Transform::default().looking_to(Vec3::Z, Vec3::Y),
));
// Spawn the flight helmet. // Spawn the flight helmet.
commands.spawn(( commands.spawn((
@ -366,7 +369,7 @@ fn add_basic_scene(commands: &mut Commands, asset_server: &AssetServer) {
asset_server asset_server
.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")), .load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")),
), ),
Transform::from_xyz(0.5, 0.0, -0.5).with_rotation(Quat::from_rotation_y(-0.15 * PI)), Transform::from_xyz(0.5, 0.0, -0.5).with_rotation(Quat::from_rotation_y(-0.15 * PI + PI)),
)); ));
// Spawn the light. // Spawn the light.

View File

@ -75,10 +75,13 @@ fn setup(
let helmet_scene = asset_server let helmet_scene = asset_server
.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")); .load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"));
commands.spawn(SceneRoot(helmet_scene.clone())); commands.spawn((
SceneRoot(helmet_scene.clone()),
Transform::default().looking_to(Vec3::Z, Vec3::Y),
));
commands.spawn(( commands.spawn((
SceneRoot(helmet_scene), SceneRoot(helmet_scene),
Transform::from_xyz(-4.0, 0.0, -3.0), Transform::from_xyz(-4.0, 0.0, -3.0).looking_to(Vec3::Z, Vec3::Y),
)); ));
let mut forward_mat: StandardMaterial = Color::srgb(0.1, 0.2, 0.1).into(); let mut forward_mat: StandardMaterial = Color::srgb(0.1, 0.2, 0.1).into();

View File

@ -84,9 +84,15 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, app_settings: R
} }
// Spawn the scene. // Spawn the scene.
commands.spawn(SceneRoot(asset_server.load( commands.spawn((
GltfAssetLabel::Scene(0).from_asset("models/DepthOfFieldExample/DepthOfFieldExample.glb"), SceneRoot(
))); asset_server.load(
GltfAssetLabel::Scene(0)
.from_asset("models/DepthOfFieldExample/DepthOfFieldExample.glb"),
),
),
Transform::default().looking_to(Vec3::Z, Vec3::Y),
));
// Spawn the help text. // Spawn the help text.
commands.spawn(( commands.spawn((

View File

@ -41,17 +41,20 @@ fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
let flight_helmet = asset_server let flight_helmet = asset_server
.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")); .load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"));
// This model will keep its original materials // This model will keep its original materials
commands.spawn(SceneRoot(flight_helmet.clone())); commands.spawn((
SceneRoot(flight_helmet.clone()),
Transform::default().looking_to(Vec3::Z, Dir3::Y),
));
// This model will be tinted red // This model will be tinted red
commands.spawn(( commands.spawn((
SceneRoot(flight_helmet.clone()), SceneRoot(flight_helmet.clone()),
Transform::from_xyz(-1.25, 0., 0.), Transform::from_xyz(-1.25, 0., 0.).looking_to(Vec3::Z, Dir3::Y),
ColorOverride(palettes::tailwind::RED_300.into()), ColorOverride(palettes::tailwind::RED_300.into()),
)); ));
// This model will be tinted green // This model will be tinted green
commands.spawn(( commands.spawn((
SceneRoot(flight_helmet), SceneRoot(flight_helmet),
Transform::from_xyz(1.25, 0., 0.), Transform::from_xyz(1.25, 0., 0.).looking_to(Vec3::Z, Dir3::Y),
ColorOverride(palettes::tailwind::GREEN_300.into()), ColorOverride(palettes::tailwind::GREEN_300.into()),
)); ));
} }

View File

@ -57,8 +57,8 @@ static CLICK_TO_MOVE_HELP_TEXT: &str = "Left click: Move the object";
static GIZMO_COLOR: Color = Color::Srgba(YELLOW); static GIZMO_COLOR: Color = Color::Srgba(YELLOW);
static VOXEL_FROM_WORLD: Mat4 = Mat4::from_cols_array_2d(&[ static VOXEL_FROM_WORLD: Mat4 = Mat4::from_cols_array_2d(&[
[-42.317566, 0.0, 0.0, 0.0], [42.317566, 0.0, 0.0, 0.0],
[0.0, 0.0, 44.601563, 0.0], [0.0, 0.0, -44.601563, 0.0],
[0.0, 16.73776, 0.0, 0.0], [0.0, 16.73776, 0.0, 0.0],
[0.0, 6.544792, 0.0, 1.0], [0.0, 6.544792, 0.0, 1.0],
]); ]);

View File

@ -22,8 +22,8 @@ use widgets::{
#[path = "../helpers/widgets.rs"] #[path = "../helpers/widgets.rs"]
mod widgets; mod widgets;
/// The speed at which the cube rotates, in radians per frame. /// The speed at which the cube rotates, in radians per second.
const CUBE_ROTATION_SPEED: f32 = 0.02; const CUBE_ROTATION_SPEED: f32 = FRAC_PI_2;
/// The speed at which the selection can be moved, in spherical coordinate /// The speed at which the selection can be moved, in spherical coordinate
/// radians per mouse unit. /// radians per mouse unit.
@ -381,9 +381,9 @@ fn draw_gizmos(mut gizmos: Gizmos, spotlight: Query<(&GlobalTransform, &SpotLigh
} }
/// Rotates the cube a bit every frame. /// Rotates the cube a bit every frame.
fn rotate_cube(mut meshes: Query<&mut Transform, With<Rotate>>) { fn rotate_cube(mut meshes: Query<&mut Transform, With<Rotate>>, time: Res<Time>) {
for mut transform in &mut meshes { for mut transform in &mut meshes {
transform.rotate_y(CUBE_ROTATION_SPEED); transform.rotate_y(CUBE_ROTATION_SPEED * time.delta_secs());
} }
} }

View File

@ -40,9 +40,13 @@ fn main() {
} }
fn setup(mut commands: Commands, asset_server: Res<AssetServer>, args: Res<Args>) { fn setup(mut commands: Commands, asset_server: Res<AssetServer>, args: Res<Args>) {
commands.spawn(SceneRoot(asset_server.load( commands.spawn((
GltfAssetLabel::Scene(0).from_asset("models/CornellBox/CornellBox.glb"), SceneRoot(
))); asset_server
.load(GltfAssetLabel::Scene(0).from_asset("models/CornellBox/CornellBox.glb")),
),
Transform::default().looking_to(Vec3::Z, Vec3::Y),
));
let mut camera = commands.spawn(( let mut camera = commands.spawn((
Camera3d::default(), Camera3d::default(),

View File

@ -43,9 +43,14 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
} }
.build(), .build(),
)); ));
commands.spawn(SceneRoot(asset_server.load( commands.spawn((
GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"), SceneRoot(
))); asset_server
.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")),
),
// Rotate the scene to face the camera.
Transform::default().looking_to(Vec3::Z, Vec3::Y),
));
} }
fn animate_light_direction( fn animate_light_direction(

View File

@ -109,7 +109,7 @@ fn setup(
MeshMaterial3d(debug_material.clone()), MeshMaterial3d(debug_material.clone()),
Transform::default() Transform::default()
.with_scale(Vec3::splat(0.2)) .with_scale(Vec3::splat(0.2))
.with_rotation(Quat::from_rotation_y(PI)) .looking_to(Vec3::Z, Vec3::Y)
.with_translation(Vec3::new(x as f32 / 2.0, 0.0, 0.3)), .with_translation(Vec3::new(x as f32 / 2.0, 0.0, 0.3)),
)); ));
} }

View File

@ -157,7 +157,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, app_status: Res
fn spawn_camera(commands: &mut Commands) { fn spawn_camera(commands: &mut Commands) {
commands commands
.spawn(Camera3d::default()) .spawn(Camera3d::default())
.insert(Transform::from_xyz(-0.7, 0.7, 1.0).looking_at(vec3(0.0, 0.3, 0.0), Vec3::Y)); .insert(Transform::from_xyz(0.7, 0.7, -1.0).looking_at(vec3(0.0, 0.3, 0.0), Vec3::Y));
} }
/// Spawns the scene. /// Spawns the scene.

View File

@ -88,9 +88,12 @@ fn spawn_camera(commands: &mut Commands, asset_server: &AssetServer) {
/// variety of colors. /// variety of colors.
fn spawn_scene(commands: &mut Commands, asset_server: &AssetServer) { fn spawn_scene(commands: &mut Commands, asset_server: &AssetServer) {
// Spawn the main scene. // Spawn the main scene.
commands.spawn(SceneRoot(asset_server.load( commands.spawn((
GltfAssetLabel::Scene(0).from_asset("models/TonemappingTest/TonemappingTest.gltf"), SceneRoot(asset_server.load(
))); GltfAssetLabel::Scene(0).from_asset("models/TonemappingTest/TonemappingTest.gltf"),
)),
Transform::default().looking_to(Vec3::Z, Vec3::Y),
));
// Spawn the flight helmet. // Spawn the flight helmet.
commands.spawn(( commands.spawn((
@ -98,7 +101,7 @@ fn spawn_scene(commands: &mut Commands, asset_server: &AssetServer) {
asset_server asset_server
.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")), .load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")),
), ),
Transform::from_xyz(0.5, 0.0, -0.5).with_rotation(Quat::from_rotation_y(-0.15 * PI)), Transform::from_xyz(0.5, 0.0, -0.5).with_rotation(Quat::from_rotation_y(-0.15 * PI + PI)),
)); ));
// Spawn the light. // Spawn the light.

View File

@ -139,6 +139,7 @@ fn spawn_reflection_probe(commands: &mut Commands, cubemaps: &Cubemaps) {
diffuse_map: cubemaps.diffuse.clone(), diffuse_map: cubemaps.diffuse.clone(),
specular_map: cubemaps.specular_reflection_probe.clone(), specular_map: cubemaps.specular_reflection_probe.clone(),
intensity: 5000.0, intensity: 5000.0,
rotation: Quat::from_rotation_y(PI),
..default() ..default()
}, },
// 2.0 because the sphere's radius is 1.0 and we want to fully enclose it. // 2.0 because the sphere's radius is 1.0 and we want to fully enclose it.
@ -175,7 +176,7 @@ fn add_environment_map_to_camera(
.insert(Skybox { .insert(Skybox {
image: cubemaps.skybox.clone(), image: cubemaps.skybox.clone(),
brightness: 5000.0, brightness: 5000.0,
..default() rotation: Quat::from_rotation_y(PI),
}); });
} }
} }
@ -285,6 +286,7 @@ fn create_camera_environment_map_light(cubemaps: &Cubemaps) -> EnvironmentMapLig
diffuse_map: cubemaps.diffuse.clone(), diffuse_map: cubemaps.diffuse.clone(),
specular_map: cubemaps.specular_environment_map.clone(), specular_map: cubemaps.specular_environment_map.clone(),
intensity: 5000.0, intensity: 5000.0,
rotation: Quat::from_rotation_y(PI),
..default() ..default()
} }
} }

View File

@ -41,9 +41,13 @@ fn main() {
fn setup(mut commands: Commands, asset_server: Res<AssetServer>, args: Res<Args>) { fn setup(mut commands: Commands, asset_server: Res<AssetServer>, args: Res<Args>) {
commands commands
.spawn(SceneRoot(asset_server.load( .spawn((
GltfAssetLabel::Scene(0).from_asset("models/CornellBox/CornellBox.glb"), SceneRoot(
))) asset_server
.load(GltfAssetLabel::Scene(0).from_asset("models/CornellBox/CornellBox.glb")),
),
Transform::default().looking_to(Vec3::Z, Vec3::Y),
))
.observe(add_raytracing_meshes_on_scene_load); .observe(add_raytracing_meshes_on_scene_load);
commands.spawn(( commands.spawn((

View File

@ -36,7 +36,7 @@ const CAMERA_ZOOM_RANGE: Range<f32> = 2.0..12.0;
static TURN_SSR_OFF_HELP_TEXT: &str = "Press Space to turn screen-space reflections off"; static TURN_SSR_OFF_HELP_TEXT: &str = "Press Space to turn screen-space reflections off";
static TURN_SSR_ON_HELP_TEXT: &str = "Press Space to turn screen-space reflections on"; static TURN_SSR_ON_HELP_TEXT: &str = "Press Space to turn screen-space reflections on";
static MOVE_CAMERA_HELP_TEXT: &str = static MOVE_CAMERA_HELP_TEXT: &str =
"Press WASD or use the mouse wheel to pan and orbit the camera"; "Press WASD to orbit and use the mouse wheel to zoom the camera";
static SWITCH_TO_FLIGHT_HELMET_HELP_TEXT: &str = "Press Enter to switch to the flight helmet model"; static SWITCH_TO_FLIGHT_HELMET_HELP_TEXT: &str = "Press Enter to switch to the flight helmet model";
static SWITCH_TO_CUBE_HELP_TEXT: &str = "Press Enter to switch to the cube model"; static SWITCH_TO_CUBE_HELP_TEXT: &str = "Press Enter to switch to the cube model";

View File

@ -101,6 +101,7 @@ fn setup_basic_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
SceneRoot(asset_server.load( SceneRoot(asset_server.load(
GltfAssetLabel::Scene(0).from_asset("models/TonemappingTest/TonemappingTest.gltf"), GltfAssetLabel::Scene(0).from_asset("models/TonemappingTest/TonemappingTest.gltf"),
)), )),
Transform::default().looking_to(Vec3::Z, Vec3::Y),
SceneNumber(1), SceneNumber(1),
)); ));
@ -110,7 +111,7 @@ fn setup_basic_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
asset_server asset_server
.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")), .load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")),
), ),
Transform::from_xyz(0.5, 0.0, -0.5).with_rotation(Quat::from_rotation_y(-0.15 * PI)), Transform::from_xyz(0.5, 0.0, -0.5).with_rotation(Quat::from_rotation_y(-0.15 * PI + PI)),
SceneNumber(1), SceneNumber(1),
)); ));

View File

@ -36,7 +36,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// Spawn the scene as a child of this entity at the given transform // Spawn the scene as a child of this entity at the given transform
commands.spawn(( commands.spawn((
Transform::from_xyz(-1.0, 0.0, 0.0), Transform::from_xyz(-1.0, 0.0, 0.0).looking_to(Vec3::Z, Vec3::Y),
SceneRoot( SceneRoot(
asset_server asset_server
.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")), .load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")),
@ -45,6 +45,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// Spawn a second scene, and add a tag component to be able to target it later // Spawn a second scene, and add a tag component to be able to target it later
commands.spawn(( commands.spawn((
Transform::default().looking_to(Vec3::Z, Vec3::Y),
SceneRoot( SceneRoot(
asset_server asset_server
.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")), .load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")),

View File

@ -114,6 +114,7 @@ fn setup(
asset_server asset_server
.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")), .load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")),
), ),
Transform::default().looking_to(Vec3::Z, Vec3::Y),
MainModel::HighPoly, MainModel::HighPoly,
)); ));
@ -124,6 +125,7 @@ fn setup(
.from_asset("models/FlightHelmetLowPoly/FlightHelmetLowPoly.gltf"), .from_asset("models/FlightHelmetLowPoly/FlightHelmetLowPoly.gltf"),
), ),
), ),
Transform::default().looking_to(Vec3::Z, Vec3::Y),
MainModel::LowPoly, MainModel::LowPoly,
)); ));

View File

@ -57,9 +57,15 @@ fn main() {
/// Initializes the scene. /// Initializes the scene.
fn setup(mut commands: Commands, asset_server: Res<AssetServer>, app_settings: Res<AppSettings>) { fn setup(mut commands: Commands, asset_server: Res<AssetServer>, app_settings: Res<AppSettings>) {
// Spawn the glTF scene. // Spawn the glTF scene.
commands.spawn(SceneRoot(asset_server.load( commands.spawn((
GltfAssetLabel::Scene(0).from_asset("models/VolumetricFogExample/VolumetricFogExample.glb"), SceneRoot(
))); asset_server.load(
GltfAssetLabel::Scene(0)
.from_asset("models/VolumetricFogExample/VolumetricFogExample.glb"),
),
),
Transform::default().looking_to(Vec3::Z, Vec3::Y),
));
// Spawn the camera. // Spawn the camera.
commands commands

View File

@ -54,10 +54,13 @@ fn setup_mesh_and_animation(
// containing our mesh once it has loaded. // containing our mesh once it has loaded.
let mesh_scene = SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(GLTF_PATH))); let mesh_scene = SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(GLTF_PATH)));
// Make sure the mesh is looking at the camera
let transform = Transform::default().looking_to(Vec3::Z, Vec3::Y);
// Spawn an entity with our components, and connect it to an observer that // Spawn an entity with our components, and connect it to an observer that
// will trigger when the scene is loaded and spawned. // will trigger when the scene is loaded and spawned.
commands commands
.spawn((animation_to_play, mesh_scene)) .spawn((animation_to_play, mesh_scene, transform))
.observe(play_animation_when_ready); .observe(play_animation_when_ready);
} }

View File

@ -76,8 +76,9 @@ fn setup(
)); ));
// Fox // Fox
commands.spawn(SceneRoot( commands.spawn((
asset_server.load(GltfAssetLabel::Scene(0).from_asset(FOX_PATH)), SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(FOX_PATH))),
Transform::default().looking_to(Vec3::Z, Vec3::Y),
)); ));
// Instructions // Instructions

View File

@ -122,8 +122,9 @@ fn setup(
)); ));
// Fox // Fox
commands.spawn(SceneRoot( commands.spawn((
asset_server.load(GltfAssetLabel::Scene(0).from_asset(FOX_PATH)), SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(FOX_PATH))),
Transform::default().looking_to(Vec3::Z, Vec3::Y),
)); ));
// We're seeding the PRNG here to make this example deterministic for testing purposes. // We're seeding the PRNG here to make this example deterministic for testing purposes.

View File

@ -242,7 +242,7 @@ fn setup_scene(
SceneRoot( SceneRoot(
asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/animated/Fox.glb")), asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/animated/Fox.glb")),
), ),
Transform::from_scale(Vec3::splat(0.07)), Transform::from_scale(Vec3::splat(0.07)).looking_to(Vec3::Z, Vec3::Y),
)); ));
// Ground // Ground

View File

@ -143,7 +143,7 @@ fn setup_scene(
SceneRoot( SceneRoot(
asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/animated/Fox.glb")), asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/animated/Fox.glb")),
), ),
Transform::from_scale(Vec3::splat(0.07)), Transform::from_scale(Vec3::splat(0.07)).looking_to(Vec3::Z, Vec3::Y),
)); ));
// Spawn the ground. // Spawn the ground.

View File

@ -25,9 +25,13 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
)); ));
// Spawn the first scene in `models/SimpleSkin/SimpleSkin.gltf` // Spawn the first scene in `models/SimpleSkin/SimpleSkin.gltf`
commands.spawn(SceneRoot(asset_server.load( commands.spawn((
GltfAssetLabel::Scene(0).from_asset("models/SimpleSkin/SimpleSkin.gltf"), SceneRoot(
))); asset_server
.load(GltfAssetLabel::Scene(0).from_asset("models/SimpleSkin/SimpleSkin.gltf")),
),
Transform::default().looking_to(Vec3::Z, Vec3::Y),
));
} }
/// The scene hierarchy currently looks somewhat like this: /// The scene hierarchy currently looks somewhat like this:

View File

@ -82,7 +82,9 @@ fn setup(
), ),
// Note: the scale adjustment is purely an accident of our fox model, which renders // Note: the scale adjustment is purely an accident of our fox model, which renders
// HUGE unless mitigated! // HUGE unless mitigated!
Transform::from_translation(Vec3::splat(0.0)).with_scale(Vec3::splat(0.025)), Transform::from_translation(Vec3::splat(0.0))
.with_scale(Vec3::splat(0.025))
.looking_to(Vec3::Z, Vec3::Y),
)); ));
commands.spawn(( commands.spawn((

View File

@ -146,7 +146,7 @@ fn load_level_1(
// Spawn the fox. // Spawn the fox.
commands.spawn(( commands.spawn((
SceneRoot(fox.clone()), SceneRoot(fox.clone()),
Transform::from_xyz(0.0, 0.0, 0.0), Transform::default().looking_to(Vec3::Z, Vec3::Y),
LevelComponents, LevelComponents,
)); ));
@ -179,7 +179,11 @@ fn load_level_2(
loading_data loading_data
.loading_assets .loading_assets
.push(helmet_scene.clone().into()); .push(helmet_scene.clone().into());
commands.spawn((SceneRoot(helmet_scene.clone()), LevelComponents)); commands.spawn((
SceneRoot(helmet_scene.clone()),
Transform::default().looking_to(Vec3::Z, Vec3::Y),
LevelComponents,
));
// Spawn the light. // Spawn the light.
commands.spawn(( commands.spawn((

View File

@ -133,17 +133,12 @@ fn setup(
// Foxes // Foxes
// Concentric rings of foxes, running in opposite directions. The rings are spaced at 2m radius intervals. // Concentric rings of foxes, running in opposite directions. The rings are spaced at 2m radius intervals.
// The foxes in each ring are spaced at least 2m apart around its circumference.' // The foxes in each ring are spaced at least 2m apart around its circumference.'
// NOTE: This fox model faces +z
let fox_handle = let fox_handle =
asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/animated/Fox.glb")); asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/animated/Fox.glb"));
let ring_directions = [ let ring_directions = [
( (Quat::IDENTITY, RotationDirection::CounterClockwise),
Quat::from_rotation_y(PI), (Quat::from_rotation_y(PI), RotationDirection::Clockwise),
RotationDirection::CounterClockwise,
),
(Quat::IDENTITY, RotationDirection::Clockwise),
]; ];
let mut ring_index = 0; let mut ring_index = 0;

View File

@ -217,6 +217,7 @@ mod gltf {
SceneRoot(asset_server.load( SceneRoot(asset_server.load(
GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"), GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"),
)), )),
Transform::default().looking_to(Vec3::Z, Vec3::Y),
DespawnOnExitState(CURRENT_SCENE), DespawnOnExitState(CURRENT_SCENE),
)); ));
} }
@ -269,6 +270,7 @@ mod animation {
commands commands
.spawn(( .spawn((
SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(FOX_PATH))), SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(FOX_PATH))),
Transform::default().looking_to(Vec3::Z, Vec3::Y),
DespawnOnExitState(CURRENT_SCENE), DespawnOnExitState(CURRENT_SCENE),
)) ))
.observe(pause_animation_frame); .observe(pause_animation_frame);

View File

@ -173,8 +173,11 @@ fn setup_scene_after_load(
let mut camera = commands.spawn(( let mut camera = commands.spawn((
Camera3d::default(), Camera3d::default(),
Projection::from(projection), Projection::from(projection),
Transform::from_translation(Vec3::from(aabb.center) + size * Vec3::new(0.5, 0.25, 0.5)) // Spawn the camera so that it is facing the model's forward direction
.looking_at(Vec3::from(aabb.center), Vec3::Y), Transform::from_translation(
Vec3::from(aabb.center) + size * Vec3::new(-0.5, 0.25, -0.5),
)
.looking_at(Vec3::from(aabb.center), Vec3::Y),
Camera { Camera {
is_active: false, is_active: false,
..default() ..default()
@ -214,7 +217,7 @@ fn setup_scene_after_load(
info!("Spawning a directional light"); info!("Spawning a directional light");
let mut light = commands.spawn(( let mut light = commands.spawn((
DirectionalLight::default(), DirectionalLight::default(),
Transform::from_xyz(1.0, 1.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y), Transform::from_xyz(-1.0, 1.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y),
)); ));
if args.occlusion_culling == Some(true) { if args.occlusion_culling == Some(true) {
light.insert(OcclusionCulling); light.insert(OcclusionCulling);

View File

@ -1,7 +1,7 @@
--- ---
title: Allow importing glTFs with a corrected coordinate system title: Allow importing glTFs with a corrected coordinate system
authors: ["@janhohenheim"] authors: ["@janhohenheim"]
pull_requests: [19633, 19685, 19816] pull_requests: [19633, 19685, 19816, 20099]
--- ---
glTF uses the following coordinate system: glTF uses the following coordinate system:
@ -17,6 +17,7 @@ and Bevy uses:
- right: X - right: X
This means that to correctly import glTFs into Bevy, vertex data should be rotated by 180 degrees around the Y axis. This means that to correctly import glTFs into Bevy, vertex data should be rotated by 180 degrees around the Y axis.
For the longest time, Bevy has simply ignored this distinction. That caused issues when working across programs, as most software respects the For the longest time, Bevy has simply ignored this distinction. That caused issues when working across programs, as most software respects the
glTF coordinate system when importing and exporting glTFs. Your scene might have looked correct in Blender, Maya, TrenchBroom, etc. but everything would be flipped when importing it into Bevy! glTF coordinate system when importing and exporting glTFs. Your scene might have looked correct in Blender, Maya, TrenchBroom, etc. but everything would be flipped when importing it into Bevy!
@ -84,7 +85,7 @@ After opting into the new behavior, your scene will be oriented such that your m
For example, Blender assumes -Y to be forward, so exporting the following model to glTF and loading it in Bevy with the new settings will ensure everything is For example, Blender assumes -Y to be forward, so exporting the following model to glTF and loading it in Bevy with the new settings will ensure everything is
oriented the right way across all programs in your pipeline: oriented the right way across all programs in your pipeline:
<!-- TODO: Add png from PR description --> <!-- TODO: Add Fox PNG from https://github.com/bevyengine/bevy/pull/19633 description -->
![Blender Coordinate System](blender-coords.png) ![Blender Coordinate System](blender-coords.png)
If you opt into this, please let us know how it's working out! Is your scene looking like you expected? Are the animations playing correctly? Is the camera at the right place? Are the lights shining from the right spots? If you opt into this, please let us know how it's working out! Is your scene looking like you expected? Are the animations playing correctly? Is the camera at the right place? Are the lights shining from the right spots?