-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
318 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
( | ||
events: [ | ||
(100, Screenshot), | ||
(200, Custom("switch_scene")), | ||
(300, Screenshot), | ||
(400, Custom("switch_scene")), | ||
(500, Screenshot), | ||
(600, Custom("switch_scene")), | ||
(700, Screenshot), | ||
(800, AppExit), | ||
] | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,298 @@ | ||
//! 3d testbed | ||
//! | ||
//! You can switch scene by pressing the spacebar | ||
#[cfg(feature = "bevy_ci_testing")] | ||
use bevy::dev_tools::ci_testing::CiTestingCustomEvent; | ||
use bevy::prelude::*; | ||
|
||
fn main() { | ||
let mut app = App::new(); | ||
app.add_plugins((DefaultPlugins,)) | ||
.init_state::<Scene>() | ||
.enable_state_scoped_entities::<Scene>() | ||
.add_systems(OnEnter(Scene::Light), light::setup) | ||
.add_systems(OnEnter(Scene::Bloom), bloom::setup) | ||
.add_systems(OnEnter(Scene::Gltf), gltf::setup) | ||
.add_systems(OnEnter(Scene::Animation), animation::setup) | ||
.add_systems(Update, switch_scene); | ||
app.run(); | ||
} | ||
|
||
#[derive(Debug, Clone, Eq, PartialEq, Hash, States, Default)] | ||
enum Scene { | ||
#[default] | ||
Light, | ||
Bloom, | ||
Gltf, | ||
Animation, | ||
} | ||
|
||
fn switch_scene( | ||
keyboard: Res<ButtonInput<KeyCode>>, | ||
#[cfg(feature = "bevy_ci_testing")] mut ci_events: EventReader<CiTestingCustomEvent>, | ||
scene: Res<State<Scene>>, | ||
mut next_scene: ResMut<NextState<Scene>>, | ||
) { | ||
let mut should_switch = false; | ||
should_switch |= keyboard.just_pressed(KeyCode::Space); | ||
#[cfg(feature = "bevy_ci_testing")] | ||
{ | ||
should_switch |= ci_events.read().any(|event| match event { | ||
CiTestingCustomEvent(event) => event == "switch_scene", | ||
}); | ||
} | ||
if should_switch { | ||
info!("Switching scene"); | ||
next_scene.set(match scene.get() { | ||
Scene::Light => Scene::Bloom, | ||
Scene::Bloom => Scene::Gltf, | ||
Scene::Gltf => Scene::Animation, | ||
Scene::Animation => Scene::Light, | ||
}); | ||
} | ||
} | ||
|
||
mod light { | ||
use std::f32::consts::PI; | ||
|
||
use bevy::{ | ||
color::palettes::css::{DEEP_PINK, LIME, RED}, | ||
prelude::*, | ||
}; | ||
|
||
const CURRENT_SCENE: super::Scene = super::Scene::Light; | ||
|
||
pub fn setup( | ||
mut commands: Commands, | ||
mut meshes: ResMut<Assets<Mesh>>, | ||
mut materials: ResMut<Assets<StandardMaterial>>, | ||
) { | ||
commands.spawn(( | ||
Mesh3d(meshes.add(Plane3d::default().mesh().size(10.0, 10.0))), | ||
MeshMaterial3d(materials.add(StandardMaterial { | ||
base_color: Color::WHITE, | ||
perceptual_roughness: 1.0, | ||
..default() | ||
})), | ||
StateScoped(CURRENT_SCENE), | ||
)); | ||
|
||
commands.spawn(( | ||
Mesh3d(meshes.add(Cuboid::default())), | ||
MeshMaterial3d(materials.add(StandardMaterial { | ||
base_color: DEEP_PINK.into(), | ||
..default() | ||
})), | ||
Transform::from_xyz(0.0, 1.0, 0.0), | ||
StateScoped(CURRENT_SCENE), | ||
)); | ||
|
||
commands.spawn(( | ||
PointLight { | ||
intensity: 100_000.0, | ||
color: RED.into(), | ||
shadows_enabled: true, | ||
..default() | ||
}, | ||
Transform::from_xyz(1.0, 2.0, 0.0), | ||
StateScoped(CURRENT_SCENE), | ||
)); | ||
|
||
commands.spawn(( | ||
SpotLight { | ||
intensity: 100_000.0, | ||
color: LIME.into(), | ||
shadows_enabled: true, | ||
inner_angle: 0.6, | ||
outer_angle: 0.8, | ||
..default() | ||
}, | ||
Transform::from_xyz(-1.0, 2.0, 0.0).looking_at(Vec3::new(-1.0, 0.0, 0.0), Vec3::Z), | ||
StateScoped(CURRENT_SCENE), | ||
)); | ||
|
||
commands.spawn(( | ||
DirectionalLight { | ||
illuminance: light_consts::lux::OVERCAST_DAY, | ||
shadows_enabled: true, | ||
..default() | ||
}, | ||
Transform { | ||
translation: Vec3::new(0.0, 2.0, 0.0), | ||
rotation: Quat::from_rotation_x(-PI / 4.), | ||
..default() | ||
}, | ||
StateScoped(CURRENT_SCENE), | ||
)); | ||
|
||
commands.spawn(( | ||
Camera3d::default(), | ||
Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), | ||
StateScoped(CURRENT_SCENE), | ||
)); | ||
} | ||
} | ||
|
||
mod bloom { | ||
use bevy::{ | ||
core_pipeline::{bloom::Bloom, tonemapping::Tonemapping}, | ||
prelude::*, | ||
}; | ||
|
||
const CURRENT_SCENE: super::Scene = super::Scene::Bloom; | ||
|
||
pub fn setup( | ||
mut commands: Commands, | ||
mut meshes: ResMut<Assets<Mesh>>, | ||
mut materials: ResMut<Assets<StandardMaterial>>, | ||
) { | ||
commands.spawn(( | ||
Camera3d::default(), | ||
Camera { | ||
hdr: true, | ||
..default() | ||
}, | ||
Tonemapping::TonyMcMapface, | ||
Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), | ||
Bloom::NATURAL, | ||
StateScoped(CURRENT_SCENE), | ||
)); | ||
|
||
let material_emissive1 = materials.add(StandardMaterial { | ||
emissive: LinearRgba::rgb(13.99, 5.32, 2.0), | ||
..default() | ||
}); | ||
let material_emissive2 = materials.add(StandardMaterial { | ||
emissive: LinearRgba::rgb(2.0, 13.99, 5.32), | ||
..default() | ||
}); | ||
|
||
let mesh = meshes.add(Sphere::new(0.5).mesh().ico(5).unwrap()); | ||
|
||
for z in -2..3_i32 { | ||
let material = match (z % 2).abs() { | ||
0 => material_emissive1.clone(), | ||
1 => material_emissive2.clone(), | ||
_ => unreachable!(), | ||
}; | ||
|
||
commands.spawn(( | ||
Mesh3d(mesh.clone()), | ||
MeshMaterial3d(material), | ||
Transform::from_xyz(z as f32 * 2.0, 0.0, 0.0), | ||
StateScoped(CURRENT_SCENE), | ||
)); | ||
} | ||
} | ||
} | ||
|
||
mod gltf { | ||
use bevy::prelude::*; | ||
|
||
const CURRENT_SCENE: super::Scene = super::Scene::Gltf; | ||
|
||
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { | ||
commands.spawn(( | ||
Camera3d::default(), | ||
Transform::from_xyz(0.7, 0.7, 1.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y), | ||
EnvironmentMapLight { | ||
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"), | ||
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), | ||
intensity: 250.0, | ||
..default() | ||
}, | ||
StateScoped(CURRENT_SCENE), | ||
)); | ||
|
||
commands.spawn(( | ||
DirectionalLight { | ||
shadows_enabled: true, | ||
..default() | ||
}, | ||
StateScoped(CURRENT_SCENE), | ||
)); | ||
commands.spawn(( | ||
SceneRoot(asset_server.load( | ||
GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"), | ||
)), | ||
StateScoped(CURRENT_SCENE), | ||
)); | ||
} | ||
} | ||
|
||
mod animation { | ||
use std::{f32::consts::PI, time::Duration}; | ||
|
||
use bevy::{prelude::*, scene::SceneInstanceReady}; | ||
|
||
const CURRENT_SCENE: super::Scene = super::Scene::Animation; | ||
const FOX_PATH: &str = "models/animated/Fox.glb"; | ||
|
||
#[derive(Resource)] | ||
struct Animation { | ||
animation: AnimationNodeIndex, | ||
graph: Handle<AnimationGraph>, | ||
} | ||
|
||
pub fn setup( | ||
mut commands: Commands, | ||
asset_server: Res<AssetServer>, | ||
mut graphs: ResMut<Assets<AnimationGraph>>, | ||
) { | ||
let (graph, node) = AnimationGraph::from_clip( | ||
asset_server.load(GltfAssetLabel::Animation(2).from_asset(FOX_PATH)), | ||
); | ||
|
||
let graph_handle = graphs.add(graph); | ||
commands.insert_resource(Animation { | ||
animation: node, | ||
graph: graph_handle, | ||
}); | ||
|
||
commands.spawn(( | ||
Camera3d::default(), | ||
Transform::from_xyz(100.0, 100.0, 150.0).looking_at(Vec3::new(0.0, 20.0, 0.0), Vec3::Y), | ||
StateScoped(CURRENT_SCENE), | ||
)); | ||
|
||
commands.spawn(( | ||
Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)), | ||
DirectionalLight { | ||
shadows_enabled: true, | ||
..default() | ||
}, | ||
StateScoped(CURRENT_SCENE), | ||
)); | ||
|
||
commands | ||
.spawn(( | ||
SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(FOX_PATH))), | ||
StateScoped(CURRENT_SCENE), | ||
)) | ||
.observe(start_animation); | ||
} | ||
|
||
fn start_animation( | ||
trigger: Trigger<SceneInstanceReady>, | ||
children: Query<&Children>, | ||
mut commands: Commands, | ||
animation: Res<Animation>, | ||
mut players: Query<(Entity, &mut AnimationPlayer)>, | ||
) { | ||
let entity = children.get(trigger.entity()).unwrap()[0]; | ||
let entity = children.get(entity).unwrap()[0]; | ||
|
||
let (entity, mut player) = players.get_mut(entity).unwrap(); | ||
let mut transitions = AnimationTransitions::new(); | ||
transitions | ||
.play(&mut player, animation.animation, Duration::ZERO) | ||
.seek_to(0.5) | ||
.pause(); | ||
|
||
commands | ||
.entity(entity) | ||
.insert(AnimationGraphHandle(animation.graph.clone())) | ||
.insert(transitions); | ||
} | ||
} |