Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/testbed/3d.rs
9325 views
1
//! 3d testbed
2
//!
3
//! You can switch scene by pressing the spacebar
4
5
mod helpers;
6
7
use argh::FromArgs;
8
use bevy::prelude::*;
9
use helpers::Next;
10
11
#[derive(FromArgs)]
12
/// 3d testbed
13
pub struct Args {
14
#[argh(positional)]
15
scene: Option<Scene>,
16
}
17
18
fn main() {
19
#[cfg(not(target_arch = "wasm32"))]
20
let args: Args = argh::from_env();
21
#[cfg(target_arch = "wasm32")]
22
let args: Args = Args::from_args(&[], &[]).unwrap();
23
24
let mut app = App::new();
25
app.add_plugins((DefaultPlugins,))
26
.add_systems(OnEnter(Scene::Light), light::setup)
27
.add_systems(OnEnter(Scene::Bloom), bloom::setup)
28
.add_systems(OnEnter(Scene::Gltf), gltf::setup)
29
.add_systems(OnEnter(Scene::Animation), animation::setup)
30
.add_systems(OnEnter(Scene::Gizmos), gizmos::setup)
31
.add_systems(
32
OnEnter(Scene::GltfCoordinateConversion),
33
gltf_coordinate_conversion::setup,
34
)
35
.add_systems(Update, switch_scene)
36
.add_systems(Update, gizmos::draw_gizmos.run_if(in_state(Scene::Gizmos)))
37
.add_systems(
38
Update,
39
gltf_coordinate_conversion::draw_gizmos
40
.run_if(in_state(Scene::GltfCoordinateConversion)),
41
);
42
43
match args.scene {
44
None => app.init_state::<Scene>(),
45
Some(scene) => app.insert_state(scene),
46
};
47
48
#[cfg(feature = "bevy_ci_testing")]
49
app.add_systems(Update, helpers::switch_scene_in_ci::<Scene>);
50
51
app.run();
52
}
53
54
#[derive(Debug, Clone, Eq, PartialEq, Hash, States, Default)]
55
enum Scene {
56
#[default]
57
Light,
58
Bloom,
59
Gltf,
60
Animation,
61
Gizmos,
62
GltfCoordinateConversion,
63
}
64
65
impl std::str::FromStr for Scene {
66
type Err = String;
67
68
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
69
let mut isit = Self::default();
70
while s.to_lowercase() != format!("{isit:?}").to_lowercase() {
71
isit = isit.next();
72
if isit == Self::default() {
73
return Err(format!("Invalid Scene name: {s}"));
74
}
75
}
76
Ok(isit)
77
}
78
}
79
80
impl Next for Scene {
81
fn next(&self) -> Self {
82
match self {
83
Scene::Light => Scene::Bloom,
84
Scene::Bloom => Scene::Gltf,
85
Scene::Gltf => Scene::Animation,
86
Scene::Animation => Scene::Gizmos,
87
Scene::Gizmos => Scene::GltfCoordinateConversion,
88
Scene::GltfCoordinateConversion => Scene::Light,
89
}
90
}
91
}
92
93
fn switch_scene(
94
keyboard: Res<ButtonInput<KeyCode>>,
95
scene: Res<State<Scene>>,
96
mut next_scene: ResMut<NextState<Scene>>,
97
) {
98
if keyboard.just_pressed(KeyCode::Space) {
99
info!("Switching scene");
100
next_scene.set(scene.get().next());
101
}
102
}
103
104
mod light {
105
use std::f32::consts::PI;
106
107
use bevy::{
108
color::palettes::css::{DEEP_PINK, LIME, RED},
109
prelude::*,
110
};
111
112
const CURRENT_SCENE: super::Scene = super::Scene::Light;
113
114
pub fn setup(
115
mut commands: Commands,
116
mut meshes: ResMut<Assets<Mesh>>,
117
mut materials: ResMut<Assets<StandardMaterial>>,
118
) {
119
commands.spawn((
120
Mesh3d(meshes.add(Plane3d::default().mesh().size(10.0, 10.0))),
121
MeshMaterial3d(materials.add(StandardMaterial {
122
base_color: Color::WHITE,
123
perceptual_roughness: 1.0,
124
..default()
125
})),
126
DespawnOnExit(CURRENT_SCENE),
127
));
128
129
commands.spawn((
130
Mesh3d(meshes.add(Cuboid::default())),
131
MeshMaterial3d(materials.add(StandardMaterial {
132
base_color: DEEP_PINK.into(),
133
..default()
134
})),
135
Transform::from_xyz(0.0, 1.0, 0.0),
136
DespawnOnExit(CURRENT_SCENE),
137
));
138
139
commands.spawn((
140
PointLight {
141
intensity: 100_000.0,
142
color: RED.into(),
143
shadow_maps_enabled: true,
144
..default()
145
},
146
Transform::from_xyz(1.0, 2.0, 0.0),
147
DespawnOnExit(CURRENT_SCENE),
148
));
149
150
commands.spawn((
151
SpotLight {
152
intensity: 100_000.0,
153
color: LIME.into(),
154
shadow_maps_enabled: true,
155
inner_angle: 0.6,
156
outer_angle: 0.8,
157
..default()
158
},
159
Transform::from_xyz(-1.0, 2.0, 0.0).looking_at(Vec3::new(-1.0, 0.0, 0.0), Vec3::Z),
160
DespawnOnExit(CURRENT_SCENE),
161
));
162
163
commands.spawn((
164
DirectionalLight {
165
illuminance: light_consts::lux::OVERCAST_DAY,
166
shadow_maps_enabled: true,
167
..default()
168
},
169
Transform {
170
translation: Vec3::new(0.0, 2.0, 0.0),
171
rotation: Quat::from_rotation_x(-PI / 4.),
172
..default()
173
},
174
DespawnOnExit(CURRENT_SCENE),
175
));
176
177
commands.spawn((
178
Camera3d::default(),
179
Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
180
DespawnOnExit(CURRENT_SCENE),
181
));
182
}
183
}
184
185
mod bloom {
186
use bevy::{core_pipeline::tonemapping::Tonemapping, post_process::bloom::Bloom, prelude::*};
187
188
const CURRENT_SCENE: super::Scene = super::Scene::Bloom;
189
190
pub fn setup(
191
mut commands: Commands,
192
mut meshes: ResMut<Assets<Mesh>>,
193
mut materials: ResMut<Assets<StandardMaterial>>,
194
) {
195
commands.spawn((
196
Camera3d::default(),
197
Tonemapping::TonyMcMapface,
198
Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
199
Bloom::NATURAL,
200
DespawnOnExit(CURRENT_SCENE),
201
));
202
203
let material_emissive1 = materials.add(StandardMaterial {
204
emissive: LinearRgba::rgb(13.99, 5.32, 2.0),
205
..default()
206
});
207
let material_emissive2 = materials.add(StandardMaterial {
208
emissive: LinearRgba::rgb(2.0, 13.99, 5.32),
209
..default()
210
});
211
212
let mesh = meshes.add(Sphere::new(0.5).mesh().ico(5).unwrap());
213
214
for z in -2..3_i32 {
215
let material = match (z % 2).abs() {
216
0 => material_emissive1.clone(),
217
1 => material_emissive2.clone(),
218
_ => unreachable!(),
219
};
220
221
commands.spawn((
222
Mesh3d(mesh.clone()),
223
MeshMaterial3d(material),
224
Transform::from_xyz(z as f32 * 2.0, 0.0, 0.0),
225
DespawnOnExit(CURRENT_SCENE),
226
));
227
}
228
}
229
}
230
231
mod gltf {
232
use bevy::prelude::*;
233
234
const CURRENT_SCENE: super::Scene = super::Scene::Gltf;
235
236
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
237
commands.spawn((
238
Camera3d::default(),
239
Transform::from_xyz(0.7, 0.7, 1.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y),
240
EnvironmentMapLight {
241
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
242
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
243
intensity: 250.0,
244
..default()
245
},
246
DespawnOnExit(CURRENT_SCENE),
247
));
248
249
commands.spawn((
250
DirectionalLight {
251
shadow_maps_enabled: true,
252
..default()
253
},
254
DespawnOnExit(CURRENT_SCENE),
255
));
256
commands.spawn((
257
SceneRoot(asset_server.load(
258
GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"),
259
)),
260
DespawnOnExit(CURRENT_SCENE),
261
));
262
}
263
}
264
265
mod animation {
266
use std::{f32::consts::PI, time::Duration};
267
268
use bevy::{prelude::*, scene::SceneInstanceReady};
269
270
const CURRENT_SCENE: super::Scene = super::Scene::Animation;
271
const FOX_PATH: &str = "models/animated/Fox.glb";
272
273
#[derive(Resource)]
274
struct Animation {
275
animation: AnimationNodeIndex,
276
graph: Handle<AnimationGraph>,
277
}
278
279
pub fn setup(
280
mut commands: Commands,
281
asset_server: Res<AssetServer>,
282
mut graphs: ResMut<Assets<AnimationGraph>>,
283
) {
284
let (graph, node) = AnimationGraph::from_clip(
285
asset_server.load(GltfAssetLabel::Animation(2).from_asset(FOX_PATH)),
286
);
287
288
let graph_handle = graphs.add(graph);
289
commands.insert_resource(Animation {
290
animation: node,
291
graph: graph_handle,
292
});
293
294
commands.spawn((
295
Camera3d::default(),
296
Transform::from_xyz(100.0, 100.0, 150.0).looking_at(Vec3::new(0.0, 20.0, 0.0), Vec3::Y),
297
DespawnOnExit(CURRENT_SCENE),
298
));
299
300
commands.spawn((
301
Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),
302
DirectionalLight {
303
shadow_maps_enabled: true,
304
..default()
305
},
306
DespawnOnExit(CURRENT_SCENE),
307
));
308
309
commands
310
.spawn((
311
SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(FOX_PATH))),
312
DespawnOnExit(CURRENT_SCENE),
313
))
314
.observe(pause_animation_frame);
315
}
316
317
fn pause_animation_frame(
318
scene_ready: On<SceneInstanceReady>,
319
children: Query<&Children>,
320
mut commands: Commands,
321
animation: Res<Animation>,
322
mut players: Query<(Entity, &mut AnimationPlayer)>,
323
) {
324
for child in children.iter_descendants(scene_ready.entity) {
325
if let Ok((entity, mut player)) = players.get_mut(child) {
326
let mut transitions = AnimationTransitions::new();
327
transitions
328
.play(&mut player, animation.animation, Duration::ZERO)
329
.seek_to(0.5)
330
.pause();
331
332
commands
333
.entity(entity)
334
.insert(AnimationGraphHandle(animation.graph.clone()))
335
.insert(transitions);
336
}
337
}
338
}
339
}
340
341
mod gizmos {
342
use bevy::{color::palettes::css::*, prelude::*};
343
344
pub fn setup(mut commands: Commands) {
345
commands.spawn((
346
Camera3d::default(),
347
Transform::from_xyz(-1.0, 2.5, 6.5).looking_at(Vec3::ZERO, Vec3::Y),
348
DespawnOnExit(super::Scene::Gizmos),
349
));
350
}
351
352
pub fn draw_gizmos(mut gizmos: Gizmos) {
353
gizmos.cube(
354
Transform::from_translation(Vec3::X * -1.75).with_scale(Vec3::splat(1.25)),
355
RED,
356
);
357
gizmos
358
.sphere(Isometry3d::from_translation(Vec3::X * -3.5), 0.75, GREEN)
359
.resolution(30_000 / 3);
360
361
// 3d grids with all variations of outer edges on or off
362
for i in 0..8 {
363
let x = 1.5 * (i % 4) as f32;
364
let y = 1.0 * (0.5 - (i / 4) as f32);
365
let mut grid = gizmos.grid_3d(
366
Isometry3d::from_translation(Vec3::new(x, y, 0.0)),
367
UVec3::new(5, 4, 3),
368
Vec3::splat(0.175),
369
Color::WHITE,
370
);
371
if i & 1 > 0 {
372
grid = grid.outer_edges_x();
373
}
374
if i & 2 > 0 {
375
grid = grid.outer_edges_y();
376
}
377
if i & 4 > 0 {
378
grid.outer_edges_z();
379
}
380
}
381
}
382
}
383
384
mod gltf_coordinate_conversion {
385
use bevy::{
386
color::palettes::basic::*,
387
gltf::{convert_coordinates::GltfConvertCoordinates, GltfLoaderSettings},
388
prelude::*,
389
scene::SceneInstanceReady,
390
};
391
392
const CURRENT_SCENE: super::Scene = super::Scene::GltfCoordinateConversion;
393
394
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
395
commands.spawn((
396
Camera3d::default(),
397
Transform::from_xyz(-4.0, 4.0, -5.0).looking_at(Vec3::ZERO, Vec3::Y),
398
DespawnOnExit(CURRENT_SCENE),
399
));
400
401
commands.spawn((
402
DirectionalLight {
403
color: BLUE.into(),
404
..default()
405
},
406
Transform::IDENTITY.looking_to(Dir3::Z, Dir3::Y),
407
DespawnOnExit(CURRENT_SCENE),
408
));
409
410
commands.spawn((
411
DirectionalLight {
412
color: RED.into(),
413
..default()
414
},
415
Transform::IDENTITY.looking_to(Dir3::X, Dir3::Y),
416
DespawnOnExit(CURRENT_SCENE),
417
));
418
419
commands.spawn((
420
DirectionalLight {
421
color: GREEN.into(),
422
..default()
423
},
424
Transform::IDENTITY.looking_to(Dir3::NEG_Y, Dir3::X),
425
DespawnOnExit(CURRENT_SCENE),
426
));
427
428
commands
429
.spawn((
430
SceneRoot(asset_server.load_with_settings(
431
GltfAssetLabel::Scene(0).from_asset("models/Faces/faces.glb"),
432
|s: &mut GltfLoaderSettings| {
433
s.convert_coordinates = Some(GltfConvertCoordinates {
434
rotate_scene_entity: true,
435
rotate_meshes: true,
436
});
437
},
438
)),
439
DespawnOnExit(CURRENT_SCENE),
440
))
441
.observe(show_aabbs);
442
}
443
444
pub fn show_aabbs(
445
scene_ready: On<SceneInstanceReady>,
446
mut commands: Commands,
447
children: Query<&Children>,
448
meshes: Query<(), With<Mesh3d>>,
449
) {
450
for child in children
451
.iter_descendants(scene_ready.entity)
452
.filter(|&e| meshes.contains(e))
453
{
454
commands.entity(child).insert(ShowAabbGizmo {
455
color: Some(BLACK.into()),
456
});
457
}
458
}
459
460
pub fn draw_gizmos(mut gizmos: Gizmos) {
461
gizmos.axes(Transform::IDENTITY, 1.0);
462
}
463
}
464
465