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