Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/tools/scene_viewer/scene_viewer_plugin.rs
6601 views
1
//! A glTF scene viewer plugin. Provides controls for directional lighting, and switching between scene cameras.
2
//! To use in your own application:
3
//! - Copy the code for the `SceneViewerPlugin` and add the plugin to your App.
4
//! - Insert an initialized `SceneHandle` resource into your App's `AssetServer`.
5
6
use bevy::{
7
gltf::Gltf, input::common_conditions::input_just_pressed, prelude::*, scene::InstanceId,
8
};
9
10
use std::{f32::consts::*, fmt};
11
12
use super::camera_controller::*;
13
14
#[derive(Resource)]
15
pub struct SceneHandle {
16
pub gltf_handle: Handle<Gltf>,
17
scene_index: usize,
18
instance_id: Option<InstanceId>,
19
pub is_loaded: bool,
20
pub has_light: bool,
21
}
22
23
impl SceneHandle {
24
pub fn new(gltf_handle: Handle<Gltf>, scene_index: usize) -> Self {
25
Self {
26
gltf_handle,
27
scene_index,
28
instance_id: None,
29
is_loaded: false,
30
has_light: false,
31
}
32
}
33
}
34
35
#[cfg(not(feature = "animation"))]
36
const INSTRUCTIONS: &str = r#"
37
Scene Controls:
38
L - animate light direction
39
U - toggle shadows
40
C - cycle through the camera controller and any cameras loaded from the scene
41
42
compile with "--features animation" for animation controls.
43
"#;
44
45
#[cfg(feature = "animation")]
46
const INSTRUCTIONS: &str = "
47
Scene Controls:
48
L - animate light direction
49
U - toggle shadows
50
B - toggle bounding boxes
51
C - cycle through the camera controller and any cameras loaded from the scene
52
53
Space - Play/Pause animation
54
Enter - Cycle through animations
55
";
56
57
impl fmt::Display for SceneHandle {
58
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59
write!(f, "{INSTRUCTIONS}")
60
}
61
}
62
63
pub struct SceneViewerPlugin;
64
65
impl Plugin for SceneViewerPlugin {
66
fn build(&self, app: &mut App) {
67
app.init_resource::<CameraTracker>()
68
.add_systems(PreUpdate, scene_load_check)
69
.add_systems(
70
Update,
71
(
72
update_lights,
73
camera_tracker,
74
toggle_bounding_boxes.run_if(input_just_pressed(KeyCode::KeyB)),
75
),
76
);
77
}
78
}
79
80
fn toggle_bounding_boxes(mut config: ResMut<GizmoConfigStore>) {
81
config.config_mut::<AabbGizmoConfigGroup>().1.draw_all ^= true;
82
}
83
84
fn scene_load_check(
85
asset_server: Res<AssetServer>,
86
mut scenes: ResMut<Assets<Scene>>,
87
gltf_assets: Res<Assets<Gltf>>,
88
mut scene_handle: ResMut<SceneHandle>,
89
mut scene_spawner: ResMut<SceneSpawner>,
90
) {
91
match scene_handle.instance_id {
92
None => {
93
if asset_server
94
.load_state(&scene_handle.gltf_handle)
95
.is_loaded()
96
{
97
let gltf = gltf_assets.get(&scene_handle.gltf_handle).unwrap();
98
if gltf.scenes.len() > 1 {
99
info!(
100
"Displaying scene {} out of {}",
101
scene_handle.scene_index,
102
gltf.scenes.len()
103
);
104
info!("You can select the scene by adding '#Scene' followed by a number to the end of the file path (e.g '#Scene1' to load the second scene).");
105
}
106
107
let gltf_scene_handle =
108
gltf.scenes
109
.get(scene_handle.scene_index)
110
.unwrap_or_else(|| {
111
panic!(
112
"glTF file doesn't contain scene {}!",
113
scene_handle.scene_index
114
)
115
});
116
let scene = scenes.get_mut(gltf_scene_handle).unwrap();
117
118
let mut query = scene
119
.world
120
.query::<(Option<&DirectionalLight>, Option<&PointLight>)>();
121
scene_handle.has_light =
122
query
123
.iter(&scene.world)
124
.any(|(maybe_directional_light, maybe_point_light)| {
125
maybe_directional_light.is_some() || maybe_point_light.is_some()
126
});
127
128
scene_handle.instance_id = Some(scene_spawner.spawn(gltf_scene_handle.clone()));
129
130
info!("Spawning scene...");
131
}
132
}
133
Some(instance_id) if !scene_handle.is_loaded => {
134
if scene_spawner.instance_is_ready(instance_id) {
135
info!("...done!");
136
scene_handle.is_loaded = true;
137
}
138
}
139
Some(_) => {}
140
}
141
}
142
143
fn update_lights(
144
key_input: Res<ButtonInput<KeyCode>>,
145
time: Res<Time>,
146
mut query: Query<(&mut Transform, &mut DirectionalLight)>,
147
mut animate_directional_light: Local<bool>,
148
) {
149
for (_, mut light) in &mut query {
150
if key_input.just_pressed(KeyCode::KeyU) {
151
light.shadows_enabled = !light.shadows_enabled;
152
}
153
}
154
155
if key_input.just_pressed(KeyCode::KeyL) {
156
*animate_directional_light = !*animate_directional_light;
157
}
158
if *animate_directional_light {
159
for (mut transform, _) in &mut query {
160
transform.rotation = Quat::from_euler(
161
EulerRot::ZYX,
162
0.0,
163
time.elapsed_secs() * PI / 15.0,
164
-FRAC_PI_4,
165
);
166
}
167
}
168
}
169
170
#[derive(Resource, Default)]
171
struct CameraTracker {
172
active_index: Option<usize>,
173
cameras: Vec<Entity>,
174
}
175
176
impl CameraTracker {
177
fn track_camera(&mut self, entity: Entity) -> bool {
178
self.cameras.push(entity);
179
if self.active_index.is_none() {
180
self.active_index = Some(self.cameras.len() - 1);
181
true
182
} else {
183
false
184
}
185
}
186
187
fn active_camera(&self) -> Option<Entity> {
188
self.active_index.map(|i| self.cameras[i])
189
}
190
191
fn set_next_active(&mut self) -> Option<Entity> {
192
let active_index = self.active_index?;
193
let new_i = (active_index + 1) % self.cameras.len();
194
self.active_index = Some(new_i);
195
Some(self.cameras[new_i])
196
}
197
}
198
199
fn camera_tracker(
200
mut camera_tracker: ResMut<CameraTracker>,
201
keyboard_input: Res<ButtonInput<KeyCode>>,
202
mut queries: ParamSet<(
203
Query<(Entity, &mut Camera), (Added<Camera>, Without<CameraController>)>,
204
Query<(Entity, &mut Camera), (Added<Camera>, With<CameraController>)>,
205
Query<&mut Camera>,
206
)>,
207
) {
208
// track added scene camera entities first, to ensure they are preferred for the
209
// default active camera
210
for (entity, mut camera) in queries.p0().iter_mut() {
211
camera.is_active = camera_tracker.track_camera(entity);
212
}
213
214
// iterate added custom camera entities second
215
for (entity, mut camera) in queries.p1().iter_mut() {
216
camera.is_active = camera_tracker.track_camera(entity);
217
}
218
219
if keyboard_input.just_pressed(KeyCode::KeyC) {
220
// disable currently active camera
221
if let Some(e) = camera_tracker.active_camera()
222
&& let Ok(mut camera) = queries.p2().get_mut(e)
223
{
224
camera.is_active = false;
225
}
226
227
// enable next active camera
228
if let Some(e) = camera_tracker.set_next_active()
229
&& let Ok(mut camera) = queries.p2().get_mut(e)
230
{
231
camera.is_active = true;
232
}
233
}
234
}
235
236