1use crate::app::App;
2use crate::core::world::World;
3use crate::core::Bundle;
4use crate::bundles::{RigidBodyBundle, CameraBundle};
5use crate::physics::components::{Collider, Transform};
6use crate::physics::world::PhysicsWorld;
7use crate::renderer::asset::AssetManager;
8use crate::renderer::components::{Camera, Material, MeshRenderer};
9use crate::renderer::Renderer;
10use std::f32::consts::{FRAC_PI_2, PI};
11use crate::math::{Quat, Vec3, Vec4};
12use crate::systems;
13
14#[derive(Debug, Clone, Copy, PartialEq)]
15pub struct SimpleSceneState {
16 pub camera_speed: f32,
17 pub camera_pitch: f32,
18 pub camera_yaw: f32,
19 pub camera_pos: Vec3,
20}
21
22#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
23pub struct CameraSettings {
24 pub speed: f32,
25 pub pitch: f32,
26 pub yaw: f32,
27 pub pos: Vec3,
28 pub exposure: f32,
29 pub bloom_intensity: f32,
30}
31
32impl Default for CameraSettings {
33 fn default() -> Self {
34 Self {
35 speed: 15.0,
36 pitch: 0.0,
37 yaw: 0.0,
38 pos: Vec3::new(0.0, 2.0, 5.0),
39 exposure: 1.0,
40 bloom_intensity: 0.05,
41 }
42 }
43}
44
45#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
46pub struct LightingSettings {
47 pub preset: u32,
48 pub preset_2: u32,
49 pub blend_t: f32,
50 pub auto_cycle: bool,
51 pub rotation_speed: f32,
52 pub direct_intensity: f32,
53}
54
55impl Default for LightingSettings {
56 fn default() -> Self {
57 Self {
58 preset: 0,
59 preset_2: 1,
60 blend_t: 0.0,
61 auto_cycle: false,
62 rotation_speed: 1.0,
63 direct_intensity: 4.0,
64 }
65 }
66}
67
68#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
69#[derive(Default)]
70#[non_exhaustive]
71pub enum CameraState {
72 Orbiting,
73 Stationary,
74 #[default]
75 Manual,
76}
77
78
79#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
80#[derive(Default)]
81#[non_exhaustive]
82pub enum EditorState {
83 #[default]
84 PlayMode,
85 EditMode,
86 Paused,
87}
88
89
90
91pub struct SceneBuilder<'a> {
92 pub world: &'a mut World,
93 pub renderer: &'a Renderer,
94 pub asset_manager: &'a mut AssetManager,
95}
96
97impl<'a> SceneBuilder<'a> {
98 pub fn spawn_cube(&mut self, position: Vec3, size: f32, color: Vec3) {
99 let mesh = AssetManager::create_cube(&self.renderer.device);
100 let tex = self.asset_manager.create_white_texture(
101 &self.renderer.device,
102 &self.renderer.queue,
103 &self.renderer.scene.texture_bind_group_layout,
104 );
105 let mat = Material::new(tex).with_pbr(Vec4::new(color.x, color.y, color.z, 1.0), 1.0, 0.0);
106
107 let half_extents = size / 2.0;
110
111 let ent = self.world.spawn();
112 self.world.add_component(
113 ent,
114 Transform::new(position).with_scale(Vec3::splat(half_extents)),
115 );
116 self.world.add_component(ent, crate::physics::components::GlobalTransform::default());
117 self.world.add_component(ent, mesh);
118 self.world.add_component(ent, mat);
119 self.world.add_component(ent, MeshRenderer::new());
120 self.world.add_bundle(ent, RigidBodyBundle::dynamic(10.0).with_collider(Collider::box_collider(Vec3::splat(half_extents))));
121 }
122
123 pub fn spawn_sphere(&mut self, position: Vec3, radius: f32, color: Vec3) {
124 let mesh = AssetManager::create_sphere(&self.renderer.device, radius, 32, 32);
125 let tex = self.asset_manager.create_white_texture(
126 &self.renderer.device,
127 &self.renderer.queue,
128 &self.renderer.scene.texture_bind_group_layout,
129 );
130 let mat = Material::new(tex).with_pbr(Vec4::new(color.x, color.y, color.z, 1.0), 1.0, 0.0);
131
132 let ent = self.world.spawn();
133 self.world.add_component(
134 ent,
135 Transform::new(position), );
137 self.world.add_component(ent, crate::physics::components::GlobalTransform::default());
138 self.world.add_component(ent, mesh);
139 self.world.add_component(ent, mat);
140 self.world.add_component(ent, MeshRenderer::new());
141 self.world.add_bundle(ent, RigidBodyBundle::dynamic(10.0).with_collider(Collider::sphere(radius)));
142 }
143
144 pub fn spawn_textured_cube(&mut self, position: Vec3, size: f32) -> crate::core::entity::Entity {
145 let mesh = AssetManager::create_cube(&self.renderer.device);
146 let tex = self.asset_manager.create_uv_debug_texture(
147 &self.renderer.device,
148 &self.renderer.queue,
149 &self.renderer.scene.texture_bind_group_layout,
150 );
151 let mat = Material::new(tex).with_pbr(Vec4::new(0.5, 0.5, 0.5, 1.0), 0.6, 0.0);
152
153 let half_extents = size / 2.0;
154 let ent = self.world.spawn();
155 self.world.add_component(ent, Transform::new(position).with_scale(Vec3::splat(half_extents)));
156 self.world.add_component(ent, crate::physics::components::GlobalTransform::default());
157 self.world.add_component(ent, mesh);
158 self.world.add_component(ent, mat);
159 self.world.add_component(ent, MeshRenderer::new());
160 self.world.add_bundle(ent, RigidBodyBundle::dynamic(10.0).with_collider(Collider::box_collider(Vec3::splat(half_extents))));
161 ent
162 }
163
164 pub fn spawn_textured_sphere(&mut self, position: Vec3, radius: f32) -> crate::core::entity::Entity {
165 let mesh = AssetManager::create_sphere(&self.renderer.device, radius, 32, 32);
166 let tex = self.asset_manager.create_uv_debug_texture(
167 &self.renderer.device,
168 &self.renderer.queue,
169 &self.renderer.scene.texture_bind_group_layout,
170 );
171 let mat = Material::new(tex).with_pbr(Vec4::new(0.5, 0.5, 0.5, 1.0), 0.6, 0.0);
172
173 let ent = self.world.spawn();
174 self.world.add_component(ent, Transform::new(position));
175 self.world.add_component(ent, crate::physics::components::GlobalTransform::default());
176 self.world.add_component(ent, mesh);
177 self.world.add_component(ent, mat);
178 self.world.add_component(ent, MeshRenderer::new());
179 self.world.add_bundle(ent, RigidBodyBundle::dynamic(10.0).with_collider(Collider::sphere(radius)));
180 ent
181 }
182
183 pub fn spawn_textured_cylinder(&mut self, position: Vec3, radius: f32, height: f32) -> crate::core::entity::Entity {
184 let mesh = AssetManager::create_cylinder(&self.renderer.device, radius, height, 32);
185 let tex = self.asset_manager.create_uv_debug_texture(&self.renderer.device, &self.renderer.queue, &self.renderer.scene.texture_bind_group_layout);
186 let mat = Material::new(tex).with_pbr(Vec4::new(0.5, 0.5, 0.5, 1.0), 0.6, 0.0);
187 let ent = self.world.spawn();
188 self.world.add_component(ent, Transform::new(position));
189 self.world.add_component(ent, crate::physics::components::GlobalTransform::default());
190 self.world.add_component(ent, mesh);
191 self.world.add_component(ent, mat);
192 self.world.add_component(ent, MeshRenderer::new());
193 self.world.add_bundle(ent, RigidBodyBundle::dynamic(10.0).with_collider(Collider::sphere(radius)));
194 ent
195 }
196
197 pub fn spawn_textured_cone(&mut self, position: Vec3, radius: f32, height: f32) -> crate::core::entity::Entity {
198 let mesh = AssetManager::create_cone(&self.renderer.device, radius, height, 32);
199 let tex = self.asset_manager.create_uv_debug_texture(&self.renderer.device, &self.renderer.queue, &self.renderer.scene.texture_bind_group_layout);
200 let mat = Material::new(tex).with_pbr(Vec4::new(0.5, 0.5, 0.5, 1.0), 0.6, 0.0);
201 let ent = self.world.spawn();
202 self.world.add_component(ent, Transform::new(position));
203 self.world.add_component(ent, crate::physics::components::GlobalTransform::default());
204 self.world.add_component(ent, mesh);
205 self.world.add_component(ent, mat);
206 self.world.add_component(ent, MeshRenderer::new());
207 self.world.add_bundle(ent, RigidBodyBundle::dynamic(10.0).with_collider(Collider::sphere(radius))); ent
209 }
210
211 pub fn spawn_textured_torus(&mut self, position: Vec3, radius: f32, tube_radius: f32) -> crate::core::entity::Entity {
212 let mesh = AssetManager::create_torus(&self.renderer.device, radius, tube_radius, 32, 16);
213 let tex = self.asset_manager.create_uv_debug_texture(&self.renderer.device, &self.renderer.queue, &self.renderer.scene.texture_bind_group_layout);
214 let mat = Material::new(tex).with_pbr(Vec4::new(0.5, 0.5, 0.5, 1.0), 0.6, 0.0);
215 let ent = self.world.spawn();
216 self.world.add_component(ent, Transform::new(position));
217 self.world.add_component(ent, crate::physics::components::GlobalTransform::default());
218 self.world.add_component(ent, mesh);
219 self.world.add_component(ent, mat);
220 self.world.add_component(ent, MeshRenderer::new());
221 self.world.add_bundle(ent, RigidBodyBundle::dynamic(10.0).with_collider(Collider::sphere(radius + tube_radius))); ent
223 }
224
225 pub fn spawn_textured_capsule(&mut self, position: Vec3, radius: f32, depth: f32) -> crate::core::entity::Entity {
226 let mesh = AssetManager::create_capsule(&self.renderer.device, radius, depth, 16, 32);
227 let tex = self.asset_manager.create_uv_debug_texture(&self.renderer.device, &self.renderer.queue, &self.renderer.scene.texture_bind_group_layout);
228 let mat = Material::new(tex).with_pbr(Vec4::new(0.5, 0.5, 0.5, 1.0), 0.6, 0.0);
229 let ent = self.world.spawn();
230 self.world.add_component(ent, Transform::new(position));
231 self.world.add_component(ent, crate::physics::components::GlobalTransform::default());
232 self.world.add_component(ent, mesh);
233 self.world.add_component(ent, mat);
234 self.world.add_component(ent, MeshRenderer::new());
235 self.world.add_bundle(ent, RigidBodyBundle::dynamic(10.0).with_collider(Collider::sphere(radius)));
236 ent
237 }
238
239 pub fn spawn_textured_tetrahedron(&mut self, position: Vec3, size: f32) -> crate::core::entity::Entity {
240 let mesh = AssetManager::create_tetrahedron(&self.renderer.device, size);
241 let tex = self.asset_manager.create_uv_debug_texture(&self.renderer.device, &self.renderer.queue, &self.renderer.scene.texture_bind_group_layout);
242 let mat = Material::new(tex).with_pbr(Vec4::new(0.5, 0.5, 0.5, 1.0), 0.6, 0.0);
243 let ent = self.world.spawn();
244 self.world.add_component(ent, Transform::new(position));
245 self.world.add_component(ent, crate::physics::components::GlobalTransform::default());
246 self.world.add_component(ent, mesh);
247 self.world.add_component(ent, mat);
248 self.world.add_component(ent, MeshRenderer::new());
249 self.world.add_bundle(ent, RigidBodyBundle::dynamic(10.0).with_collider(Collider::sphere(size)));
250 ent
251 }
252
253 pub fn spawn_textured_conical_frustum(&mut self, position: Vec3, radius_bottom: f32, radius_top: f32, height: f32) -> crate::core::entity::Entity {
254 let mesh = AssetManager::create_conical_frustum(&self.renderer.device, radius_bottom, radius_top, height, 32);
255 let tex = self.asset_manager.create_uv_debug_texture(&self.renderer.device, &self.renderer.queue, &self.renderer.scene.texture_bind_group_layout);
256 let mat = Material::new(tex).with_pbr(Vec4::new(0.5, 0.5, 0.5, 1.0), 0.6, 0.0);
257 let ent = self.world.spawn();
258 self.world.add_component(ent, Transform::new(position));
259 self.world.add_component(ent, crate::physics::components::GlobalTransform::default());
260 self.world.add_component(ent, mesh);
261 self.world.add_component(ent, mat);
262 self.world.add_component(ent, MeshRenderer::new());
263 self.world.add_bundle(ent, RigidBodyBundle::dynamic(10.0).with_collider(Collider::sphere(radius_bottom.max(radius_top))));
264 ent
265 }
266
267 pub fn spawn_textured_convex_extrusion(&mut self, position: Vec3, points: &[[f32; 2]], depth: f32) -> crate::core::entity::Entity {
268 let mesh = AssetManager::create_convex_extrusion(&self.renderer.device, points, depth);
269 let tex = self.asset_manager.create_uv_debug_texture(&self.renderer.device, &self.renderer.queue, &self.renderer.scene.texture_bind_group_layout);
270 let mat = Material::new(tex).with_pbr(Vec4::new(0.5, 0.5, 0.5, 1.0), 0.6, 0.0);
271 let ent = self.world.spawn();
272 self.world.add_component(ent, Transform::new(position));
275 self.world.add_component(ent, crate::physics::components::GlobalTransform::default());
276 self.world.add_component(ent, mesh);
277 self.world.add_component(ent, mat);
278 self.world.add_component(ent, MeshRenderer::new());
279 self.world.add_bundle(ent, RigidBodyBundle::dynamic(10.0).with_collider(Collider::sphere(1.0)));
280 ent
281 }
282
283 pub fn spawn_textured_ring_extrusion(&mut self, position: Vec3, inner_points: &[[f32; 2]], outer_points: &[[f32; 2]], depth: f32) -> crate::core::entity::Entity {
284 let mesh = AssetManager::create_ring_extrusion(&self.renderer.device, inner_points, outer_points, depth);
285 let tex = self.asset_manager.create_uv_debug_texture(&self.renderer.device, &self.renderer.queue, &self.renderer.scene.texture_bind_group_layout);
286 let mat = Material::new(tex).with_pbr(Vec4::new(0.5, 0.5, 0.5, 1.0), 0.6, 0.0);
287 let ent = self.world.spawn();
288 self.world.add_component(ent, Transform::new(position));
289 self.world.add_component(ent, crate::physics::components::GlobalTransform::default());
290 self.world.add_component(ent, mesh);
291 self.world.add_component(ent, mat);
292 self.world.add_component(ent, MeshRenderer::new());
293 self.world.add_bundle(ent, RigidBodyBundle::dynamic(10.0).with_collider(Collider::sphere(1.0)));
294 ent
295 }
296
297 pub fn spawn_ground(&mut self, radius: f32) {
298 let mesh = AssetManager::create_plane(&self.renderer.device, radius * 2.0);
299 let tex = self.asset_manager.create_white_texture(
300 &self.renderer.device,
301 &self.renderer.queue,
302 &self.renderer.scene.texture_bind_group_layout,
303 );
304 let mat = Material::new(tex).with_pbr(Vec4::new(0.2, 0.2, 0.2, 1.0), 0.9, 0.0);
305
306 let ent = self.world.spawn();
307 self.world.add_component(
308 ent,
309 Transform::new(Vec3::new(0.0, 0.0, 0.0)),
310 );
311 self.world.add_component(ent, crate::physics::components::GlobalTransform::default());
312 self.world.add_component(ent, mesh);
313 self.world.add_component(ent, mat);
314 self.world.add_component(ent, MeshRenderer::new());
315 self.world.add_bundle(ent, RigidBodyBundle::static_body().with_collider(Collider::plane(Vec3::new(0.0, 1.0, 0.0), 0.0)));
316 }
317
318 pub fn spawn_point_light(&mut self, position: Vec3) {
319 let light_ent = self.world.spawn();
320 let bundle = crate::bundles::PointLightBundle {
321 position,
322 color: Vec3::new(1.0, 1.0, 1.0),
323 intensity: 20.0,
324 ..Default::default()
325 };
326
327 bundle.apply(self.world, light_ent);
328 }
329
330 pub fn spawn_camera(&mut self, state: &mut SimpleSceneState, pos: Vec3, look_at: Vec3) {
331 let look_dir = (look_at - pos).normalize_or_zero();
332 state.camera_pos = pos;
333 if look_dir != Vec3::ZERO {
334 state.camera_yaw = look_dir.z.atan2(look_dir.x);
335 state.camera_pitch = look_dir.y.asin();
336 }
337
338 let camera_ent = self.world.spawn();
339 let bundle = CameraBundle {
340 position: state.camera_pos,
341 yaw: state.camera_yaw,
342 pitch: state.camera_pitch,
343 ..Default::default()
344 };
345
346 bundle.apply(self.world, camera_ent);
347 }
348}
349
350pub trait SimpleAppExt {
351 fn with_simple_scene<F>(self, setup_fn: F) -> Self
352 where
353 F: FnOnce(&mut SceneBuilder, &mut SimpleSceneState) + 'static;
354}
355
356impl SimpleAppExt for App<SimpleSceneState> {
357 fn with_simple_scene<F>(self, setup_fn: F) -> Self
358 where
359 F: FnOnce(&mut SceneBuilder, &mut SimpleSceneState) + 'static,
360 {
361 self.set_setup(move |world, renderer| {
362 let mut asset_manager = AssetManager::new();
363 let phys_world = PhysicsWorld::new().with_gravity(Vec3::new(0.0, -9.81, 0.0));
364
365 let mut state = SimpleSceneState {
366 camera_speed: 15.0,
367 camera_pitch: 0.0,
368 camera_yaw: 0.0,
369 camera_pos: Vec3::new(0.0, 2.0, 5.0),
370 };
371
372 let mut builder = SceneBuilder {
373 world,
374 renderer,
375 asset_manager: &mut asset_manager,
376 };
377
378 setup_fn(&mut builder, &mut state);
379
380 world.insert_resource(phys_world);
381 world.insert_resource(asset_manager);
382 state
383 })
384 .set_update(|world, state, dt, input| {
385 if input.is_mouse_button_pressed(1) {
386 let delta = input.mouse_delta();
387 state.camera_yaw -= delta.0 * 0.005;
388 state.camera_pitch -= delta.1 * 0.005;
389 state.camera_pitch = state.camera_pitch.clamp(-PI / 2.0 + 0.1, PI / 2.0 - 0.1);
390 }
391
392 let fx = state.camera_yaw.cos() * state.camera_pitch.cos();
393 let fy = state.camera_pitch.sin();
394 let fz = state.camera_yaw.sin() * state.camera_pitch.cos();
395 let forward = Vec3::new(fx, fy, fz).normalize();
396 let right = forward.cross(Vec3::new(0.0, 1.0, 0.0)).normalize();
397 let up = Vec3::new(0.0, 1.0, 0.0);
398
399 let speed = if input.is_key_pressed(crate::winit::keyboard::KeyCode::ShiftLeft as u32) {
400 state.camera_speed * 3.0
401 } else {
402 state.camera_speed
403 };
404
405 let mut cam_move = Vec3::ZERO;
406 if input.is_key_pressed(crate::winit::keyboard::KeyCode::KeyW as u32) { cam_move += forward; }
407 if input.is_key_pressed(crate::winit::keyboard::KeyCode::KeyS as u32) { cam_move -= forward; }
408 if input.is_key_pressed(crate::winit::keyboard::KeyCode::KeyD as u32) { cam_move += right; }
409 if input.is_key_pressed(crate::winit::keyboard::KeyCode::KeyA as u32) { cam_move -= right; }
410 if input.is_key_pressed(crate::winit::keyboard::KeyCode::KeyE as u32) { cam_move += up; }
411 if input.is_key_pressed(crate::winit::keyboard::KeyCode::KeyQ as u32) { cam_move -= up; }
412
413 if cam_move.length_squared() > 0.0 {
414 state.camera_pos += cam_move.normalize() * speed * dt;
415 }
416
417 if let Some(mut q) = world.query_mut::<(
418 crate::core::query::Mut<Transform>,
419 crate::core::query::Mut<Camera>,
420 )>() {
421 let yaw_rot = Quat::from_rotation_y(-state.camera_yaw + FRAC_PI_2);
422 let pitch_rot = Quat::from_rotation_x(state.camera_pitch);
423 let rot = yaw_rot * pitch_rot;
424
425 for (_, (mut trans, mut cam)) in q.iter_mut() {
426 trans.position = state.camera_pos;
427 trans.rotation = rot;
428 cam.yaw = state.camera_yaw;
429 cam.pitch = state.camera_pitch;
430 }
431 }
432
433 let mut physics_dt = dt.min(0.1);
434 while physics_dt > 0.0 {
435 let step = physics_dt.min(0.016);
436 systems::cpu_physics_step_system(world, step);
437 physics_dt -= step;
438 }
439
440 use crate::core::system::System;
441 let mut transform_sync = systems::transform::TransformSyncSystem;
442 let mut transform_propagate = systems::transform::TransformPropagateSystem;
443 transform_sync.run(world, dt);
444 transform_propagate.run(world, dt);
445 })
446 .set_render(|world, _state, encoder, view, renderer, _light_time| {
447 renderer.gpu_physics = None;
450 renderer.gpu_fluid = None;
451 renderer.gpu_particles = None;
452 renderer.ssr = None; renderer.ssgi = None; systems::default_render_pass(world, encoder, view, renderer);
456 })
457 }
458}
459
460#[cfg(test)]
461mod tests {
462 use super::*;
463
464 #[test]
465 fn test_camera_settings_default() {
466 let settings = CameraSettings::default();
467 assert_eq!(settings.speed, 15.0);
468 assert_eq!(settings.pitch, 0.0);
469 assert_eq!(settings.yaw, 0.0);
470 assert_eq!(settings.pos, Vec3::new(0.0, 2.0, 5.0));
471 assert_eq!(settings.exposure, 1.0);
472 assert_eq!(settings.bloom_intensity, 0.05);
473 }
474
475 #[test]
476 fn test_lighting_settings_default() {
477 let settings = LightingSettings::default();
478 assert_eq!(settings.preset, 0);
479 assert_eq!(settings.preset_2, 1);
480 assert_eq!(settings.blend_t, 0.0);
481 assert!(!settings.auto_cycle);
482 assert_eq!(settings.rotation_speed, 1.0);
483 assert_eq!(settings.direct_intensity, 4.0);
484 }
485
486 #[test]
487 fn test_camera_state_transitions() {
488 let mut state = CameraState::default();
489 assert_eq!(state, CameraState::Manual);
490
491 state = CameraState::Orbiting;
492 assert_eq!(state, CameraState::Orbiting);
493
494 state = CameraState::Stationary;
495 assert_eq!(state, CameraState::Stationary);
496 }
497
498 #[test]
499 fn test_editor_state_transitions() {
500 let mut state = EditorState::default();
501 assert_eq!(state, EditorState::PlayMode);
502
503 state = EditorState::EditMode;
504 assert_eq!(state, EditorState::EditMode);
505
506 state = EditorState::Paused;
507 assert_eq!(state, EditorState::Paused);
508 }
509}