use std::collections::HashMap; use std::error::Error; use std::sync::Arc; use super::settings_scene::SettingsScene; use crate::core::app::DEPTH_IMAGE_ID; use crate::core::app::context::WindowContext; use crate::core::app::user_event::UserEvent; use crate::core::render::primitives::camera::Camera3D; use crate::core::render::primitives::transform::Transform; use crate::core::render::primitives::{AsDescriptorSet, AsRecordable}; use crate::core::render::render_pass_manager::{RenderPassConfig, RenderPassManager}; use crate::core::render::resources::meshes::{ObjMesh, SquareMesh}; use crate::core::render::resources::pipeline::PipelineLoader; use crate::core::render::resources::texture::{TextureLoadInfo, TextureLoader, TextureSourceKind}; use crate::core::scene::Scene; use crate::game::assets::pipelines::simple::SimplePipeline; use egui_winit_vulkano::egui; use glam::EulerRot; use glam::Quat; use glam::Vec3; use vulkano::format::Format; use vulkano::image::sampler::{Filter, SamplerAddressMode, SamplerCreateInfo}; use vulkano::{ command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage}, sync::GpuFuture, }; use winit::window::CursorGrabMode; pub struct MainSceneState { texture_loader: TextureLoader, pipeline_loader: PipelineLoader, square: SquareMesh, obj: ObjMesh, square_instances: Vec, obj_instances: Vec, camera: Camera3D, speed: f32, } #[derive(Default)] pub struct MainScene { state: Option, } impl Scene for MainScene { fn loaded(&self) -> bool { self.state.is_some() } fn load(&mut self, app_context: &mut WindowContext) -> Result<(), Box> { let depth_image_view = app_context.with_renderer_mut(|renderer| { renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone() }); let swapchain_image_view = app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); let mut pipeline_loader = PipelineLoader::new( app_context.device.clone(), swapchain_image_view.format(), depth_image_view.format(), ); pipeline_loader.register::()?; pipeline_loader.load_pipelines()?; let mut texture_loader = TextureLoader::new(app_context); texture_loader.add_texture( "wooden-crate".to_string(), TextureLoadInfo { source: TextureSourceKind::File("res/textures/wooden-crate.jpg".to_string()), sampler_create_info: SamplerCreateInfo { mag_filter: Filter::Linear, min_filter: Filter::Linear, address_mode: [SamplerAddressMode::Repeat; 3], ..Default::default() }, image_format: Format::R8G8B8A8_SRGB, }, ); texture_loader.add_texture( "cube-diffuse".to_string(), TextureLoadInfo { source: TextureSourceKind::File("res/objects/cube-diffuse.jpg".to_string()), sampler_create_info: SamplerCreateInfo { mag_filter: Filter::Linear, min_filter: Filter::Linear, address_mode: [SamplerAddressMode::Repeat; 3], ..Default::default() }, image_format: Format::R8G8B8A8_SRGB, }, ); texture_loader.load_pending_textures()?; let square = SquareMesh::new(&app_context.memory_allocator)?; let obj = { let obj = ObjMesh::new(&app_context.memory_allocator, "res/objects/cube.obj")?; obj.into_iter().next().unwrap() }; let num_instances = 100; let instance_size = 10.0; let instance_spacing = 10.0; let num_instances_per_row = (num_instances as f32 / instance_spacing).ceil() as u32; let square_instances: Vec = (0..num_instances) .map(|i| { Transform::new( Vec3::new( (i % num_instances_per_row) as f32 * (instance_spacing + instance_size), 0.0, (i / num_instances_per_row) as f32 * (instance_spacing + instance_size), ), Quat::from_euler(EulerRot::XYZ, 0.0, rand::random_range(0.0..=360.0), 0.0), Vec3::new(instance_size, instance_size, instance_size), ) }) .collect(); let obj_instances: Vec = (0..num_instances) .map(|i| { Transform::new( Vec3::new( (i % num_instances_per_row) as f32 * (instance_spacing + instance_size), 0.0, (i / num_instances_per_row) as f32 * (instance_spacing + instance_size) * -1.0 - instance_spacing * 2.0, ), Quat::from_euler(EulerRot::XYZ, 0.0, rand::random_range(0.0..=360.0), 0.0), Vec3::new( instance_size * 0.5, instance_size * 0.5, instance_size * 0.5, ), ) }) .collect(); let camera = app_context.with_renderer(|renderer| { Camera3D::new( renderer.aspect_ratio(), std::f32::consts::FRAC_PI_2, 0.01, 1000.0, ) }); self.state = Some(MainSceneState { square, obj, pipeline_loader, square_instances, obj_instances, camera, speed: 50.0, texture_loader, }); Ok(()) } fn update(&mut self, app_context: &mut WindowContext) -> Result<(), Box> { let state = self.state.as_mut().unwrap(); app_context.with_input_manager(|input_manager| { app_context.with_timer(|timer| { state.camera.update( input_manager, timer, state.speed, 10.0, app_context.get_aspect_ratio(), ); }); }); let delta_time = app_context.get_delta_time(); for (i, instance) in state.square_instances.iter_mut().enumerate() { let rotation_speed = (i % 10) as f32; let rotation_delta = Quat::from_rotation_y(rotation_speed * delta_time); instance.rotate(rotation_delta); } for (i, instance) in state.obj_instances.iter_mut().enumerate() { let rotation_speed = (i % 10) as f32; let rotation_delta = Quat::from_rotation_y(rotation_speed * delta_time); instance.rotate(rotation_delta); } if app_context .with_input_manager(|input_manager| input_manager.get_virtual_input_state("mouse_left")) > 0.0 { let _ = app_context .event_loop_proxy .send_event(UserEvent::CursorVisible(app_context.window_id, false)); let _ = app_context .event_loop_proxy .send_event(UserEvent::CursorGrabMode( app_context.window_id, CursorGrabMode::Locked, )); } if app_context.with_input_manager(|input_manager| { input_manager.get_virtual_input_state("mouse_right") }) > 0.0 { let _ = app_context .event_loop_proxy .send_event(UserEvent::CursorVisible(app_context.window_id, true)); let _ = app_context .event_loop_proxy .send_event(UserEvent::CursorGrabMode( app_context.window_id, CursorGrabMode::None, )); } Ok(()) } fn render( &mut self, before_future: Box, app_context: &mut WindowContext, ) -> Result, Box> { let state = self.state.as_mut().ok_or("State not loaded")?; let mut builder = AutoCommandBufferBuilder::primary( app_context.command_buffer_allocator.clone(), app_context.graphics_queue.queue_family_index(), CommandBufferUsage::OneTimeSubmit, )?; { let swapchain_image_view = app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); let depth_image_view = app_context.with_renderer_mut(|renderer| { renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone() }); let config = RenderPassConfig::default(); RenderPassManager::begin_standard_rendering( &mut builder, &config, swapchain_image_view, Some(depth_image_view), app_context.get_window_size(), )?; } // Create camera uniform using the actual camera let camera_uniform = Arc::new(state.camera.create_buffer(&app_context.memory_allocator)?); let square_transform_uniform = Transform::create_buffer(&app_context.memory_allocator, &state.square_instances)?; let obj_transform_uniform = Transform::create_buffer(&app_context.memory_allocator, &state.obj_instances)?; state .pipeline_loader .with_pipeline::(|pipeline| { SimplePipeline::record_commands( &mut builder, &app_context.descriptor_set_allocator, pipeline, &state.square, &square_transform_uniform, vec![ camera_uniform.clone() as Arc, state .texture_loader .get_texture("wooden-crate") .unwrap() .clone(), ], )?; SimplePipeline::record_commands( &mut builder, &app_context.descriptor_set_allocator, pipeline, &state.obj, &obj_transform_uniform, vec![ camera_uniform.clone() as Arc, state .texture_loader .get_texture("cube-diffuse") .unwrap() .clone(), ], )?; Ok(()) })?; RenderPassManager::end_rendering(&mut builder)?; let command_buffer = builder.build()?; let render_future = before_future.then_execute(app_context.graphics_queue.clone(), command_buffer)?; let swapchain_image_view = app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); let input_manager_status = app_context.with_input_manager(|input_manager| format!("{:#?}", input_manager)); let event_loop_proxy = app_context.event_loop_proxy.clone(); let delta_time = app_context.get_delta_time(); let window_id = app_context.window_id; let window_size = app_context.get_window_size(); let render_future = app_context.with_gui_mut(|gui| { gui.immediate_ui(|gui| { let ctx = gui.context(); egui::TopBottomPanel::top("top_panel").show(&ctx, |ui| { ui.horizontal(|ui| { ui.heading("Vulkan Test - Moteur 3D"); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { if ui.button("Paramètres").clicked() { let _ = event_loop_proxy.send_event(UserEvent::ChangeScene( window_id, Box::new(SettingsScene::default()), )); } if ui.button("Quitter").clicked() { let _ = event_loop_proxy.send_event(UserEvent::Exit(window_id)); } }); }); }); egui::SidePanel::left("side_panel").show(&ctx, |ui| { ui.heading("Informations"); ui.separator(); ui.label(format!("Résolution: {:?}", window_size)); ui.label(format!("Delta Time: {:.2}ms", delta_time * 1000.0)); ui.separator(); ui.label("Position caméra:"); let position = state.camera.get_position(); ui.label(format!(" X: {:.2}", position[0])); ui.label(format!(" Y: {:.2}", position[1])); ui.label(format!(" Z: {:.2}", position[2])); ui.separator(); ui.label("Rotation caméra:"); let rotation = state.camera.get_rotation(); ui.label(format!(" Yaw: {:.2}°", rotation.y.to_degrees())); ui.label(format!(" Pitch: {:.2}°", rotation.x.to_degrees())); ui.separator(); ui.label(input_manager_status); }); }); gui.draw_on_image(render_future, swapchain_image_view.clone()) }); Ok(render_future) } fn unload(&mut self) { self.state = None; } }