1use crate::SceneSettings;
4use bevy::pbr::NotShadowCaster;
5use bevy::prelude::*;
6
7pub struct ScenePlugin;
8
9impl Plugin for ScenePlugin {
10 fn build(&self, app: &mut App) {
11 app.init_resource::<SceneSettings>();
13 app.add_systems(
14 Startup,
15 setup_scene.run_if(resource_exists::<SceneSettings>),
16 )
17 .add_systems(
18 Update,
19 rebuild_scene_on_change.run_if(resource_exists::<SceneSettings>),
20 );
21 }
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
25pub enum SceneType {
26 #[default]
27 Room,
28 Road,
29 Parking,
30 Outdoor,
31}
32
33impl SceneType {
34 pub fn default_dimensions(&self) -> (f32, f32, f32, f32) {
35 match self {
37 SceneType::Room => (4.0, 5.0, 2.8, 2.5),
38 SceneType::Road => (10.0, 30.0, 0.0, 8.0),
39 SceneType::Parking => (20.0, 30.0, 0.0, 6.0),
40 SceneType::Outdoor => (10.0, 15.0, 0.0, 3.0),
41 }
42 }
43}
44
45#[derive(Component)]
46pub struct SceneGeometry;
47
48fn setup_scene(
49 mut commands: Commands,
50 mut meshes: ResMut<Assets<Mesh>>,
51 mut materials: ResMut<Assets<StandardMaterial>>,
52 settings: Res<SceneSettings>,
53) {
54 build_scene(&mut commands, &mut meshes, &mut materials, &settings);
55}
56
57fn rebuild_scene_on_change(
58 mut commands: Commands,
59 mut meshes: ResMut<Assets<Mesh>>,
60 mut materials: ResMut<Assets<StandardMaterial>>,
61 settings: Res<SceneSettings>,
62 query: Query<Entity, With<SceneGeometry>>,
63) {
64 if !settings.is_changed() {
65 return;
66 }
67
68 for entity in query.iter() {
70 commands.entity(entity).despawn_recursive();
71 }
72
73 build_scene(&mut commands, &mut meshes, &mut materials, &settings);
75}
76
77fn build_scene(
78 commands: &mut Commands,
79 meshes: &mut ResMut<Assets<Mesh>>,
80 materials: &mut ResMut<Assets<StandardMaterial>>,
81 settings: &SceneSettings,
82) {
83 match settings.scene_type {
84 SceneType::Room => build_room(commands, meshes, materials, settings),
85 SceneType::Road => build_road(commands, meshes, materials, settings),
86 SceneType::Parking => build_parking(commands, meshes, materials, settings),
87 SceneType::Outdoor => build_outdoor(commands, meshes, materials, settings),
88 }
89
90 commands.insert_resource(AmbientLight {
92 color: Color::srgb(0.9, 0.9, 1.0),
93 brightness: 50.0, });
95}
96
97fn build_room(
98 commands: &mut Commands,
99 meshes: &mut ResMut<Assets<Mesh>>,
100 materials: &mut ResMut<Assets<StandardMaterial>>,
101 settings: &SceneSettings,
102) {
103 let w = settings.room_width;
104 let l = settings.room_length;
105 let h = settings.room_height;
106
107 let floor_material = materials.add(StandardMaterial {
109 base_color: Color::srgb(0.85, 0.85, 0.85),
110 perceptual_roughness: 0.8,
111 ..default()
112 });
113
114 commands.spawn((
115 Mesh3d(meshes.add(Plane3d::default().mesh().size(w, l))),
116 MeshMaterial3d(floor_material.clone()),
117 Transform::from_xyz(w / 2.0, 0.0, l / 2.0),
118 SceneGeometry,
119 ));
120
121 let ceiling_material = materials.add(StandardMaterial {
123 base_color: Color::srgb(0.95, 0.95, 0.95),
124 perceptual_roughness: 0.9,
125 ..default()
126 });
127
128 commands.spawn((
129 Mesh3d(meshes.add(Plane3d::default().mesh().size(w, l))),
130 MeshMaterial3d(ceiling_material),
131 Transform::from_xyz(w / 2.0, h, l / 2.0)
132 .with_rotation(Quat::from_rotation_x(std::f32::consts::PI)),
133 SceneGeometry,
134 ));
135
136 let wall_material = materials.add(StandardMaterial {
138 base_color: Color::srgb(0.95, 0.95, 0.95),
139 perceptual_roughness: 0.9,
140 ..default()
141 });
142
143 commands.spawn((
145 Mesh3d(meshes.add(Plane3d::default().mesh().size(w, h))),
146 MeshMaterial3d(wall_material.clone()),
147 Transform::from_xyz(w / 2.0, h / 2.0, 0.0)
148 .with_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
149 SceneGeometry,
150 ));
151
152 commands.spawn((
154 Mesh3d(meshes.add(Plane3d::default().mesh().size(w, h))),
155 MeshMaterial3d(wall_material.clone()),
156 Transform::from_xyz(w / 2.0, h / 2.0, l)
157 .with_rotation(Quat::from_rotation_x(std::f32::consts::FRAC_PI_2)),
158 SceneGeometry,
159 ));
160
161 commands.spawn((
163 Mesh3d(meshes.add(Plane3d::default().mesh().size(l, h))),
164 MeshMaterial3d(wall_material.clone()),
165 Transform::from_xyz(0.0, h / 2.0, l / 2.0)
166 .with_rotation(Quat::from_rotation_z(-std::f32::consts::FRAC_PI_2)),
167 SceneGeometry,
168 ));
169
170 commands.spawn((
172 Mesh3d(meshes.add(Plane3d::default().mesh().size(l, h))),
173 MeshMaterial3d(wall_material),
174 Transform::from_xyz(w, h / 2.0, l / 2.0)
175 .with_rotation(Quat::from_rotation_z(std::f32::consts::FRAC_PI_2)),
176 SceneGeometry,
177 ));
178}
179
180fn build_road(
181 commands: &mut Commands,
182 meshes: &mut ResMut<Assets<Mesh>>,
183 materials: &mut ResMut<Assets<StandardMaterial>>,
184 settings: &SceneSettings,
185) {
186 let w = settings.room_width;
187 let l = settings.room_length;
188
189 let road_material = materials.add(StandardMaterial {
191 base_color: Color::srgb(0.15, 0.15, 0.15),
192 perceptual_roughness: 0.9,
193 ..default()
194 });
195
196 commands.spawn((
197 Mesh3d(meshes.add(Plane3d::default().mesh().size(w, l))),
198 MeshMaterial3d(road_material),
199 Transform::from_xyz(w / 2.0, 0.0, l / 2.0),
200 SceneGeometry,
201 ));
202
203 let marking_material = materials.add(StandardMaterial {
205 base_color: Color::WHITE,
206 emissive: LinearRgba::new(0.3, 0.3, 0.3, 1.0),
207 ..default()
208 });
209
210 let mut z = 2.0;
211 while z < l - 2.0 {
212 commands.spawn((
213 Mesh3d(meshes.add(Cuboid::new(0.15, 0.02, 2.0))),
214 MeshMaterial3d(marking_material.clone()),
215 Transform::from_xyz(w / 2.0, 0.01, z),
216 SceneGeometry,
217 ));
218 z += 4.0;
219 }
220
221 let sidewalk_material = materials.add(StandardMaterial {
223 base_color: Color::srgb(0.6, 0.6, 0.6),
224 perceptual_roughness: 0.8,
225 ..default()
226 });
227
228 commands.spawn((
229 Mesh3d(meshes.add(Cuboid::new(1.0, 0.1, l))),
230 MeshMaterial3d(sidewalk_material.clone()),
231 Transform::from_xyz(0.5, 0.05, l / 2.0),
232 SceneGeometry,
233 ));
234
235 commands.spawn((
236 Mesh3d(meshes.add(Cuboid::new(1.0, 0.1, l))),
237 MeshMaterial3d(sidewalk_material),
238 Transform::from_xyz(w - 0.5, 0.05, l / 2.0),
239 SceneGeometry,
240 ));
241
242 spawn_pole(
244 commands,
245 meshes,
246 materials,
247 Vec3::new(w - 0.7, 0.0, l / 2.0),
248 settings.mounting_height,
249 );
250}
251
252fn build_parking(
253 commands: &mut Commands,
254 meshes: &mut ResMut<Assets<Mesh>>,
255 materials: &mut ResMut<Assets<StandardMaterial>>,
256 settings: &SceneSettings,
257) {
258 let w = settings.room_width;
259 let l = settings.room_length;
260
261 let lot_material = materials.add(StandardMaterial {
263 base_color: Color::srgb(0.2, 0.2, 0.2),
264 perceptual_roughness: 0.85,
265 ..default()
266 });
267
268 commands.spawn((
269 Mesh3d(meshes.add(Plane3d::default().mesh().size(w, l))),
270 MeshMaterial3d(lot_material),
271 Transform::from_xyz(w / 2.0, 0.0, l / 2.0),
272 SceneGeometry,
273 ));
274
275 let line_material = materials.add(StandardMaterial {
277 base_color: Color::WHITE,
278 emissive: LinearRgba::new(0.2, 0.2, 0.2, 1.0),
279 ..default()
280 });
281
282 let space_width = 2.5;
283 let space_length = 5.0;
284
285 let mut row = 3.0;
286 while row < l - 3.0 {
287 let mut col = space_width;
288 while col < w - 1.0 {
289 commands.spawn((
290 Mesh3d(meshes.add(Cuboid::new(0.1, 0.02, space_length))),
291 MeshMaterial3d(line_material.clone()),
292 Transform::from_xyz(col, 0.01, row),
293 SceneGeometry,
294 ));
295 col += space_width;
296 }
297 row += space_length + 1.0;
298 }
299
300 spawn_pole(
302 commands,
303 meshes,
304 materials,
305 Vec3::new(w / 2.0, 0.0, l / 2.0),
306 settings.mounting_height,
307 );
308}
309
310fn build_outdoor(
311 commands: &mut Commands,
312 meshes: &mut ResMut<Assets<Mesh>>,
313 materials: &mut ResMut<Assets<StandardMaterial>>,
314 settings: &SceneSettings,
315) {
316 let w = settings.room_width;
317 let l = settings.room_length;
318
319 let grass_material = materials.add(StandardMaterial {
321 base_color: Color::srgb(0.15, 0.3, 0.1),
322 perceptual_roughness: 0.95,
323 ..default()
324 });
325
326 commands.spawn((
327 Mesh3d(meshes.add(Plane3d::default().mesh().size(w, l))),
328 MeshMaterial3d(grass_material),
329 Transform::from_xyz(w / 2.0, 0.0, l / 2.0),
330 SceneGeometry,
331 ));
332
333 let path_material = materials.add(StandardMaterial {
335 base_color: Color::srgb(0.5, 0.5, 0.5),
336 perceptual_roughness: 0.8,
337 ..default()
338 });
339
340 commands.spawn((
341 Mesh3d(meshes.add(Cuboid::new(1.2, 0.02, l - 2.0))),
342 MeshMaterial3d(path_material),
343 Transform::from_xyz(w / 2.0, 0.01, l / 2.0),
344 SceneGeometry,
345 ));
346
347 let bush_material = materials.add(StandardMaterial {
349 base_color: Color::srgb(0.1, 0.25, 0.05),
350 perceptual_roughness: 0.95,
351 ..default()
352 });
353
354 for (x, y, z) in [
355 (2.0, 0.4, 3.0),
356 (w - 2.0, 0.3, l - 4.0),
357 (1.5, 0.35, l - 2.0),
358 ] {
359 commands.spawn((
360 Mesh3d(meshes.add(Sphere::new(y))),
361 MeshMaterial3d(bush_material.clone()),
362 Transform::from_xyz(x, y, z),
363 SceneGeometry,
364 ));
365 }
366
367 spawn_pole(
369 commands,
370 meshes,
371 materials,
372 Vec3::new(w / 2.0, 0.0, l / 2.0),
373 settings.mounting_height,
374 );
375}
376
377fn spawn_pole(
378 commands: &mut Commands,
379 meshes: &mut ResMut<Assets<Mesh>>,
380 materials: &mut ResMut<Assets<StandardMaterial>>,
381 position: Vec3,
382 height: f32,
383) {
384 let pole_material = materials.add(StandardMaterial {
385 base_color: Color::srgb(0.4, 0.4, 0.4),
386 metallic: 0.6,
387 perceptual_roughness: 0.4,
388 ..default()
389 });
390
391 commands.spawn((
393 Mesh3d(meshes.add(Cylinder::new(0.08, height - 0.3))),
394 MeshMaterial3d(pole_material.clone()),
395 Transform::from_xyz(position.x, height / 2.0, position.z),
396 SceneGeometry,
397 NotShadowCaster,
398 ));
399
400 commands.spawn((
402 Mesh3d(meshes.add(Cylinder::new(0.05, 0.3))),
403 MeshMaterial3d(pole_material),
404 Transform::from_xyz(position.x - 0.05, height - 0.2, position.z)
405 .with_rotation(Quat::from_rotation_z(std::f32::consts::FRAC_PI_2)),
406 SceneGeometry,
407 NotShadowCaster,
408 ));
409}