diff --git a/src/core/app/context.rs b/src/core/app/context.rs new file mode 100644 index 0000000..1c0ccb6 --- /dev/null +++ b/src/core/app/context.rs @@ -0,0 +1,230 @@ +use std::sync::{Arc, Mutex}; + +use egui_winit_vulkano::Gui; +use vulkano::{ + command_buffer::allocator::StandardCommandBufferAllocator, + descriptor_set::allocator::StandardDescriptorSetAllocator, + device::{Device, Queue}, + instance::Instance, + memory::allocator::StandardMemoryAllocator, +}; +use vulkano_util::{renderer::VulkanoWindowRenderer, window::VulkanoWindows}; +use winit::{ + event_loop::EventLoopProxy, + window::{Window, WindowId}, +}; + +use crate::core::{input::InputManager, render::vulkan_context::VulkanContext, timer::Timer}; + +use super::user_event::UserEvent; + +/// Contexte d'application unifié avec Arc> pour la mutabilité partagée +#[derive(Clone)] +pub struct ApplicationContext { + // Données Vulkan (immutables) + pub vulkan_context: Arc, + pub device: Arc, + pub instance: Arc, + pub graphics_queue: Arc, + pub compute_queue: Arc, + pub transfer_queue: Option>, + pub memory_allocator: Arc, + pub command_buffer_allocator: Arc, + pub descriptor_set_allocator: Arc, + pub event_loop_proxy: EventLoopProxy, + pub window_id: WindowId, + + // Données mutables partagées avec Arc> + pub vulkano_windows: Arc>, + pub input_manager: Arc>, + pub timer: Arc>, + pub gui: Arc>, +} + +impl ApplicationContext { + pub fn new( + vulkan_context: Arc, + vulkano_windows: Arc>, + input_manager: Arc>, + timer: Arc>, + gui: Arc>, + event_loop_proxy: EventLoopProxy, + window_id: WindowId, + ) -> Self { + let vulkano_context_inner = vulkan_context.vulkano_context(); + + Self { + // Données Vulkan + vulkan_context: vulkan_context.clone(), + device: vulkano_context_inner.device().clone(), + instance: vulkano_context_inner.instance().clone(), + graphics_queue: vulkano_context_inner.graphics_queue().clone(), + compute_queue: vulkano_context_inner.compute_queue().clone(), + transfer_queue: vulkano_context_inner.transfer_queue().cloned(), + memory_allocator: vulkano_context_inner.memory_allocator().clone(), + command_buffer_allocator: vulkan_context.command_buffer_allocator().clone(), + descriptor_set_allocator: vulkan_context.descriptor_set_allocator().clone(), + event_loop_proxy, + window_id, + + // Données mutables partagées + vulkano_windows, + input_manager, + timer, + gui, + } + } + + /// Récupère les résolutions disponibles du moniteur (méthode utilitaire statique) + fn get_monitor_resolutions(window: &Window) -> Vec<(u32, u32)> { + // Première tentative : moniteur actuel + if let Some(monitor) = window.current_monitor() { + let resolutions = Self::extract_resolutions_from_monitor(&monitor); + if !resolutions.is_empty() { + log::debug!( + "Résolutions trouvées via moniteur actuel: {:?}", + resolutions + ); + return resolutions; + } + } + + // Deuxième tentative : tous les moniteurs disponibles + log::debug!("Tentative de récupération via tous les moniteurs disponibles..."); + let mut all_resolutions = Vec::new(); + + for monitor in window.available_monitors() { + let resolutions = Self::extract_resolutions_from_monitor(&monitor); + all_resolutions.extend(resolutions); + } + + if !all_resolutions.is_empty() { + // Supprime les doublons et trie + all_resolutions.sort_unstable(); + all_resolutions.dedup(); + all_resolutions.sort_by(|a, b| (b.0 * b.1).cmp(&(a.0 * a.1))); + + log::debug!( + "Résolutions trouvées via tous les moniteurs: {:?}", + all_resolutions + ); + return all_resolutions; + } + + // Aucune résolution détectée - retourne un vecteur vide + log::warn!("Aucune résolution détectée pour cette fenêtre"); + Vec::new() + } + + /// Extrait les résolutions d'un moniteur donné + fn extract_resolutions_from_monitor( + monitor: &winit::monitor::MonitorHandle, + ) -> Vec<(u32, u32)> { + let video_modes: Vec<_> = monitor.video_modes().collect(); + + if video_modes.is_empty() { + log::debug!( + "Aucun mode vidéo trouvé pour le moniteur {:?}", + monitor.name() + ); + return Vec::new(); + } + + let resolutions: Vec<(u32, u32)> = video_modes + .into_iter() + .map(|mode| { + let size = mode.size(); + (size.width, size.height) + }) + .collect(); + + log::debug!( + "Modes vidéo trouvés pour {:?}: {:?}", + monitor.name(), + resolutions + ); + resolutions + } + + /// Récupère les résolutions disponibles + pub fn get_available_resolutions(&self) -> Vec<(u32, u32)> { + self.with_renderer(|renderer| Self::get_monitor_resolutions(&renderer.window())) + } + + /// Récupère le delta time actuel depuis le timer + pub fn get_delta_time(&self) -> f32 { + self.with_timer(|timer| timer.delta_time()) + } + + /// Récupère la taille de la fenêtre depuis le renderer + pub fn get_window_size(&self) -> [f32; 2] { + self.with_renderer(|renderer| renderer.window_size()) + } + + /// Récupère l'aspect ratio depuis le renderer + pub fn get_aspect_ratio(&self) -> f32 { + self.with_renderer(|renderer| renderer.aspect_ratio()) + } + + pub fn with_renderer(&self, f: F) -> T + where + F: FnOnce(&VulkanoWindowRenderer) -> T, + { + let vulkano_windows = self + .vulkano_windows + .lock() + .expect("Failed to lock vulkano_windows"); + let renderer = vulkano_windows + .get_renderer(self.window_id) + .expect("Failed to get renderer"); + f(&renderer) + } + + pub fn with_renderer_mut(&mut self, f: F) -> T + where + F: FnOnce(&mut VulkanoWindowRenderer) -> T, + { + let mut vulkano_windows = self + .vulkano_windows + .lock() + .expect("Failed to lock vulkano_windows"); + let renderer = vulkano_windows + .get_renderer_mut(self.window_id) + .expect("Failed to get renderer"); + f(renderer) + } + + pub fn with_gui(&self, f: F) -> T + where + F: FnOnce(&Gui) -> T, + { + let gui = self.gui.lock().unwrap(); + f(&gui) + } + + pub fn with_gui_mut(&mut self, f: F) -> T + where + F: FnOnce(&mut Gui) -> T, + { + let mut gui = self.gui.lock().unwrap(); + f(&mut gui) + } + + /// Méthode utilitaire pour accéder à l'input manager de manière thread-safe + pub fn with_input_manager(&self, f: F) -> T + where + F: FnOnce(&InputManager) -> T, + { + let input_manager = self.input_manager.lock().unwrap(); + f(&input_manager) + } + + /// Méthode utilitaire pour accéder au timer de manière thread-safe + pub fn with_timer(&self, f: F) -> T + where + F: FnOnce(&Timer) -> T, + { + let timer = self.timer.lock().unwrap(); + f(&timer) + } +} diff --git a/src/core/app/mod.rs b/src/core/app/mod.rs index ab548f8..243420b 100644 --- a/src/core/app/mod.rs +++ b/src/core/app/mod.rs @@ -1,12 +1,11 @@ use std::collections::HashMap; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use super::render::vulkan_context::VulkanContext; -use super::scene::SceneContext; use crate::core::input::InputManager; -use crate::core::scene::SceneManager; +use crate::core::scene::manager::SceneManager; use crate::core::timer::Timer; -use crate::game::scenes::test_scene::TestScene; +use crate::game::scenes::main_scene::MainScene; use egui_winit_vulkano::{Gui, GuiConfig}; use user_event::UserEvent; use vulkano::format::Format; @@ -19,18 +18,23 @@ use winit::event::WindowEvent; use winit::event_loop::{ActiveEventLoop, EventLoopProxy}; use winit::window::WindowId; +use self::context::ApplicationContext; + +pub mod context; pub mod user_event; pub const DEPTH_IMAGE_ID: usize = 0; pub struct App { vulkan_context: Arc, - vulkano_windows: Arc, - gui: HashMap, + vulkano_windows: Arc>, + gui: HashMap>>, scene_manager: HashMap, - input_manager: Arc, - timer: Arc, + input_manager: Arc>, + timer: Arc>, event_loop_proxy: EventLoopProxy, + // Context d'application partagé par fenêtre - architecture unifiée + app_contexts: HashMap>>, } impl App { @@ -41,61 +45,61 @@ impl App { ) -> Self { Self { vulkan_context: Arc::new(VulkanContext::new(vulkano_context)), - vulkano_windows: Arc::new(VulkanoWindows::default()), + vulkano_windows: Arc::new(Mutex::new(VulkanoWindows::default())), gui: HashMap::new(), - input_manager: Arc::new(input_manager), + input_manager: Arc::new(Mutex::new(input_manager)), scene_manager: HashMap::new(), - timer: Arc::new(Timer::new()), + timer: Arc::new(Mutex::new(Timer::new())), event_loop_proxy, + app_contexts: HashMap::new(), } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - if let Some(vulkano_windows) = Arc::get_mut(&mut self.vulkano_windows) { - let window_id = vulkano_windows.create_window( + let mut vulkano_windows = self.vulkano_windows.lock().unwrap(); + let window_id = vulkano_windows.create_window( + event_loop, + &self.vulkan_context.vulkano_context(), + &WindowDescriptor { + title: "Rust ASH Test".to_string(), + width: 800.0, + height: 600.0, + present_mode: PresentMode::Fifo, + cursor_visible: false, + cursor_locked: true, + ..Default::default() + }, + |_| {}, + ); + + let renderer = vulkano_windows.get_renderer_mut(window_id).unwrap(); + renderer.add_additional_image_view( + DEPTH_IMAGE_ID, + Format::D16_UNORM, + ImageUsage::DEPTH_STENCIL_ATTACHMENT, + ); + + let gui = { + Gui::new( event_loop, - self.vulkan_context.vulkano_context(), - &WindowDescriptor { - title: "Rust ASH Test".to_string(), - width: 800.0, - height: 600.0, - present_mode: PresentMode::Fifo, - cursor_visible: false, - cursor_locked: true, + renderer.surface(), + renderer.graphics_queue(), + renderer.swapchain_format(), + GuiConfig { + is_overlay: true, + allow_srgb_render_target: true, ..Default::default() }, - |_| {}, - ); + ) + }; + self.gui.insert(window_id, Arc::new(Mutex::new(gui))); - let renderer = vulkano_windows.get_renderer_mut(window_id).unwrap(); - renderer.add_additional_image_view( - DEPTH_IMAGE_ID, - Format::D16_UNORM, - ImageUsage::DEPTH_STENCIL_ATTACHMENT, - ); + let mut scene_manager = SceneManager::new(); + scene_manager.load_scene(Box::new(MainScene::default())); - let gui = { - Gui::new( - event_loop, - renderer.surface(), - renderer.graphics_queue(), - renderer.swapchain_format(), - GuiConfig { - is_overlay: true, - allow_srgb_render_target: true, - ..Default::default() - }, - ) - }; - self.gui.insert(window_id, gui); - - let mut scene_manager = SceneManager::new(); - scene_manager.load_scene(Box::new(TestScene::default())); - - self.scene_manager.insert(window_id, scene_manager); - } + self.scene_manager.insert(window_id, scene_manager); } fn device_event( @@ -104,23 +108,17 @@ impl ApplicationHandler for App { _device_id: winit::event::DeviceId, event: winit::event::DeviceEvent, ) { - match Arc::get_mut(&mut self.input_manager) { - Some(input_manager) => input_manager.process_device_event(&event), - None => log::error!("Failed to get a mutable reference to the input manager"), - } + let mut input_manager = self.input_manager.lock().unwrap(); + input_manager.process_device_event(&event); } fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) { - let renderer = Arc::get_mut(&mut self.vulkano_windows) - .unwrap() - .get_renderer_mut(id) - .unwrap(); - let gui = self.gui.get_mut(&id).unwrap(); - - if !gui.update(&event) { - match Arc::get_mut(&mut self.input_manager) { - Some(input_manager) => input_manager.process_window_event(&event), - None => log::error!("Failed to get a mutable reference to the input manager"), + { + let gui = self.gui.get_mut(&id).unwrap(); + let mut gui = gui.lock().unwrap(); + if !gui.update(&event) { + let mut input_manager = self.input_manager.lock().unwrap(); + input_manager.process_window_event(&event); } } @@ -129,45 +127,64 @@ impl ApplicationHandler for App { log::debug!("The close button was pressed; stopping"); event_loop.exit(); } - WindowEvent::Resized(_) => { - renderer.resize(); - } - WindowEvent::ScaleFactorChanged { .. } => { - renderer.resize(); + WindowEvent::Resized(_) | WindowEvent::ScaleFactorChanged { .. } => { + let mut vulkano_windows = self.vulkano_windows.lock().unwrap(); + vulkano_windows.get_renderer_mut(id).unwrap().resize(); } WindowEvent::RedrawRequested => { - match Arc::get_mut(&mut self.input_manager) { - Some(input_manager) => input_manager.update(), - None => log::error!("Failed to get a mutable reference to the input manager"), + { + let mut input_manager = self + .input_manager + .lock() + .expect("Failed to lock input manager"); + input_manager.update(); } - match Arc::get_mut(&mut self.timer) { - Some(timer) => timer.update(), - None => log::error!("Failed to get a mutable reference to the timer"), + { + let mut timer = self.timer.lock().expect("Failed to lock timer"); + timer.update(); } - let mut scene_context = SceneContext::from(( - &mut *renderer, - &self.vulkan_context, - &self.input_manager, - &self.timer, - id, - &self.event_loop_proxy, - )); + // Créer ou mettre à jour le contexte d'application + let app_context = self + .app_contexts + .entry(id) + .or_insert_with(|| { + Arc::new(Mutex::new(ApplicationContext::new( + self.vulkan_context.clone(), + self.vulkano_windows.clone(), + self.input_manager.clone(), + self.timer.clone(), + self.gui.get(&id).unwrap().clone(), + self.event_loop_proxy.clone(), + id, + ))) + }) + .clone(); let scene_manager = self.scene_manager.get_mut(&id).unwrap(); - scene_manager - .load_scene_if_not_loaded(&scene_context) - .unwrap(); - if let Some(scene) = scene_manager.current_scene_mut() { - scene.update(&scene_context).unwrap(); + // Utiliser le contexte partagé pour les scènes + { + let mut context = app_context.lock().unwrap(); - let acquire_future = renderer.acquire(None, |_| {}).unwrap(); - // Update the swapchain image view to the current one after acquiring the next image - scene_context.swapchain_image_view = renderer.swapchain_image_view(); + scene_manager + .load_scene_if_not_loaded(&mut context) + .unwrap(); - let acquire_future = scene.render(acquire_future, &scene_context, gui).unwrap(); - renderer.present(acquire_future, true); + if let Some(scene) = scene_manager.current_scene_mut() { + scene.update(&mut context).unwrap(); + + let acquire_future = context + .with_renderer_mut(|renderer| renderer.acquire(None, |_| {}).unwrap()); + + let acquire_future = scene.render(acquire_future, &mut context).unwrap(); + + context.with_renderer_mut(|renderer| { + renderer.present(acquire_future, true); + }); + } else { + log::warn!("No current scene found for update!"); + } } } _ => {} @@ -175,25 +192,46 @@ impl ApplicationHandler for App { } fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { - let window = self.vulkano_windows.get_primary_window().unwrap(); + let vulkano_windows = self.vulkano_windows.lock().unwrap(); + let window = vulkano_windows.get_primary_window().unwrap(); window.request_redraw(); } fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) { match event { UserEvent::CursorGrabMode(window_id, grab) => { - let window = self.vulkano_windows.get_window(window_id).unwrap(); + let vulkano_windows = self.vulkano_windows.lock().unwrap(); + let window = vulkano_windows.get_window(window_id).unwrap(); if let Err(e) = window.set_cursor_grab(grab) { log::error!("Failed to set cursor grab: {}", e); } } UserEvent::CursorVisible(window_id, visible) => { - let window = self.vulkano_windows.get_window(window_id).unwrap(); + let vulkano_windows = self.vulkano_windows.lock().unwrap(); + let window = vulkano_windows.get_window(window_id).unwrap(); window.set_cursor_visible(visible); } UserEvent::ChangeScene(window_id, scene) => { - let scene_manager = self.scene_manager.get_mut(&window_id).unwrap(); - scene_manager.load_scene(scene); + if let Some(scene_manager) = self.scene_manager.get_mut(&window_id) { + scene_manager.load_scene(scene); + } + } + UserEvent::ChangeResolution(window_id, width, height) => { + let mut vulkano_windows = self.vulkano_windows.lock().unwrap(); + let window = vulkano_windows.get_window(window_id).unwrap(); + let _ = window.request_inner_size(winit::dpi::LogicalSize::new(width, height)); + let renderer = vulkano_windows.get_renderer_mut(window_id).unwrap(); + renderer.resize(); + log::info!( + "Resolution changed to {}x{} for window {:?}", + width, + height, + window_id + ); + } + UserEvent::Exit(window_id) => { + log::info!("Exit requested for window {:?}", window_id); + event_loop.exit(); } } } diff --git a/src/core/app/user_event.rs b/src/core/app/user_event.rs index 316ac4c..21b70f1 100644 --- a/src/core/app/user_event.rs +++ b/src/core/app/user_event.rs @@ -6,4 +6,6 @@ pub enum UserEvent { CursorGrabMode(WindowId, CursorGrabMode), CursorVisible(WindowId, bool), ChangeScene(WindowId, Box), + ChangeResolution(WindowId, f32, f32), + Exit(WindowId), } diff --git a/src/core/render/mod.rs b/src/core/render/mod.rs index 07e4134..f56fdf4 100644 --- a/src/core/render/mod.rs +++ b/src/core/render/mod.rs @@ -1,3 +1,4 @@ pub mod primitives; +pub mod render_pass_manager; pub mod texture; pub mod vulkan_context; diff --git a/src/core/render/render_pass_manager.rs b/src/core/render/render_pass_manager.rs new file mode 100644 index 0000000..02c3d7a --- /dev/null +++ b/src/core/render/render_pass_manager.rs @@ -0,0 +1,113 @@ +use std::sync::Arc; +use vulkano::{ + command_buffer::{AutoCommandBufferBuilder, RenderingAttachmentInfo, RenderingInfo}, + image::view::ImageView, + pipeline::graphics::viewport::Viewport, + render_pass::{AttachmentLoadOp, AttachmentStoreOp}, +}; + +/// Types de render passes disponibles +#[derive(Debug, Clone)] +pub enum RenderPassType { + Standard, + ShadowMap, + PostProcess, +} + +/// Configuration pour un render pass +#[derive(Debug, Clone)] +pub struct RenderPassConfig { + pub pass_type: RenderPassType, + pub clear_color: Option<[f32; 4]>, + pub clear_depth: Option, + pub load_op: AttachmentLoadOp, + pub store_op: AttachmentStoreOp, +} + +impl Default for RenderPassConfig { + fn default() -> Self { + Self { + pass_type: RenderPassType::Standard, + clear_color: Some([0.0, 0.0, 0.0, 1.0]), + clear_depth: Some(1.0), + load_op: AttachmentLoadOp::Clear, + store_op: AttachmentStoreOp::Store, + } + } +} + +/// Gestionnaire de render passes réutilisable +pub struct RenderPassManager; + +impl RenderPassManager { + /// Commence un render pass standard avec les paramètres donnés + pub fn begin_standard_rendering( + builder: &mut AutoCommandBufferBuilder, + config: &RenderPassConfig, + color_attachment: Arc, + depth_attachment: Option>, + window_size: [f32; 2], + ) -> Result<(), Box> { + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size, + depth_range: 0.0..=1.0, + }; + + let mut rendering_info = RenderingInfo { + color_attachments: vec![Some(RenderingAttachmentInfo { + load_op: config.load_op, + store_op: config.store_op, + clear_value: config.clear_color.map(|c| c.into()), + ..RenderingAttachmentInfo::image_view(color_attachment) + })], + depth_attachment: None, + ..Default::default() + }; + + if let Some(depth_view) = depth_attachment { + rendering_info.depth_attachment = Some(RenderingAttachmentInfo { + load_op: AttachmentLoadOp::Clear, + store_op: AttachmentStoreOp::DontCare, + clear_value: config.clear_depth.map(|d| [d].into()), + ..RenderingAttachmentInfo::image_view(depth_view) + }); + } + + builder + .begin_rendering(rendering_info)? + .set_viewport(0, [viewport].into_iter().collect())?; + + Ok(()) + } + + /// Termine le render pass actuel + pub fn end_rendering( + builder: &mut AutoCommandBufferBuilder, + ) -> Result<(), Box> { + builder.end_rendering()?; + Ok(()) + } + + /// Crée une configuration pour un render pass shadow map + pub fn shadow_map_config() -> RenderPassConfig { + RenderPassConfig { + pass_type: RenderPassType::ShadowMap, + clear_color: None, + clear_depth: Some(1.0), + load_op: AttachmentLoadOp::Clear, + store_op: AttachmentStoreOp::Store, + } + } + + /// Crée une configuration pour un render pass de post-processing + pub fn post_process_config() -> RenderPassConfig { + RenderPassConfig { + pass_type: RenderPassType::PostProcess, + clear_color: Some([0.0, 0.0, 0.0, 1.0]), + clear_depth: None, + load_op: AttachmentLoadOp::Load, + store_op: AttachmentStoreOp::Store, + } + } +} diff --git a/src/core/scene/context.rs b/src/core/scene/context.rs deleted file mode 100644 index 4e0dab3..0000000 --- a/src/core/scene/context.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::sync::Arc; - -use vulkano::{ - command_buffer::allocator::StandardCommandBufferAllocator, - descriptor_set::allocator::StandardDescriptorSetAllocator, - device::{Device, Queue}, - image::view::ImageView, - instance::Instance, - memory::allocator::StandardMemoryAllocator, -}; -use vulkano_util::renderer::VulkanoWindowRenderer; -use winit::{ - event_loop::EventLoopProxy, - window::{Window, WindowId}, -}; - -use crate::core::{ - app::{DEPTH_IMAGE_ID, user_event::UserEvent}, - input::InputManager, - render::vulkan_context::VulkanContext, - timer::Timer, -}; - -pub struct SceneContext<'a> { - pub instance: Arc, - pub device: Arc, - pub graphics_queue: Arc, - pub compute_queue: Arc, - pub transfer_queue: Option>, - pub memory_allocator: Arc, - pub command_buffer_allocator: Arc, - pub descriptor_set_allocator: Arc, - pub window_size: [f32; 2], - pub aspect_ratio: f32, - pub swapchain_image_view: Arc, - pub depth_stencil_image_view: Arc, - pub input_manager: Arc, - pub timer: Arc, - pub event_loop_proxy: &'a EventLoopProxy, - pub window_id: WindowId, -} - -impl<'a> - From<( - &mut VulkanoWindowRenderer, - &Arc, - &Arc, - &Arc, - WindowId, - &'a EventLoopProxy, - )> for SceneContext<'a> -{ - fn from( - (renderer, vulkan_context, input_manager, timer, window_id, event_loop_proxy): ( - &mut VulkanoWindowRenderer, - &Arc, - &Arc, - &Arc, - WindowId, - &'a EventLoopProxy, - ), - ) -> Self { - let (command_buffer_allocator, descriptor_set_allocator) = { - ( - vulkan_context.command_buffer_allocator().clone(), - vulkan_context.descriptor_set_allocator().clone(), - ) - }; - - let (instance, device, graphics_queue, compute_queue, transfer_queue, memory_allocator) = { - let vulkan_context = vulkan_context.vulkano_context(); - ( - vulkan_context.instance().clone(), - vulkan_context.device().clone(), - vulkan_context.graphics_queue().clone(), - vulkan_context.compute_queue().clone(), - vulkan_context.transfer_queue().cloned(), - vulkan_context.memory_allocator().clone(), - ) - }; - - Self { - instance, - device, - graphics_queue, - compute_queue, - transfer_queue, - memory_allocator, - command_buffer_allocator, - descriptor_set_allocator, - window_size: renderer.window_size(), - aspect_ratio: renderer.aspect_ratio(), - swapchain_image_view: renderer.swapchain_image_view(), - depth_stencil_image_view: renderer.get_additional_image_view(DEPTH_IMAGE_ID), - input_manager: input_manager.clone(), - timer: timer.clone(), - event_loop_proxy, - window_id, - } - } -} diff --git a/src/core/scene/manager.rs b/src/core/scene/manager.rs index e44765d..9ff65ac 100644 --- a/src/core/scene/manager.rs +++ b/src/core/scene/manager.rs @@ -1,52 +1,84 @@ use std::error::Error; -use super::{Scene, SceneContext}; +use crate::core::app::context::ApplicationContext; + +use super::Scene; pub struct SceneManager { - current_scene: Option>, + scenes: Vec>, + current_scene_index: Option, } impl SceneManager { pub fn new() -> Self { Self { - current_scene: None, + scenes: Vec::new(), + current_scene_index: None, + } + } + + pub fn load_scene(&mut self, scene: Box) { + self.scenes.push(scene); + self.current_scene_index = Some(self.scenes.len() - 1); + } + + pub fn replace_current_scene(&mut self, scene: Box) { + if let Some(index) = self.current_scene_index { + if index < self.scenes.len() { + self.scenes[index].unload(); + self.scenes[index] = scene; + } + } else { + self.load_scene(scene); + } + } + + pub fn current_scene(&self) -> Option<&Box> { + if let Some(index) = self.current_scene_index { + self.scenes.get(index) + } else { + None + } + } + + pub fn current_scene_mut(&mut self) -> Option<&mut Box> { + log::debug!( + "current_scene_mut called - index: {:?}, scenes len: {}", + self.current_scene_index, + self.scenes.len() + ); + if let Some(index) = self.current_scene_index { + log::debug!("Getting scene at index {}", index); + self.scenes.get_mut(index) + } else { + log::debug!("No current scene index set"); + None } } pub fn load_scene_if_not_loaded( &mut self, - scene_context: &SceneContext, + app_context: &mut ApplicationContext, ) -> Result<(), Box> { - if let Some(current_scene) = self.current_scene.as_mut() { - if !current_scene.loaded() { - current_scene.load(scene_context)?; + log::debug!("SceneManager::load_scene_if_not_loaded called"); + log::debug!( + "Current scene index: {:?}, scenes count: {}", + self.current_scene_index, + self.scenes.len() + ); + + if let Some(scene) = self.current_scene_mut() { + log::debug!("Scene found, checking if loaded: {}", scene.loaded()); + if !scene.loaded() { + log::debug!("Scene not loaded, loading..."); + scene.load(app_context)?; + log::debug!("Scene loaded successfully"); + } else { + log::debug!("Scene already loaded"); } + } else { + log::warn!("No scene found in SceneManager!"); } Ok(()) } - - pub fn load_scene(&mut self, scene: Box) { - if let Some(current_scene) = self.current_scene.as_mut() { - current_scene.unload(); - } - self.current_scene = Some(scene); - } - - pub fn current_scene(&self) -> Option<&Box> { - if let Some(current_scene) = self.current_scene.as_ref() { - if current_scene.loaded() { - return Some(current_scene); - } - } - None - } - - pub fn current_scene_mut(&mut self) -> Option<&mut Box> { - if let Some(current_scene) = self.current_scene.as_mut() { - if current_scene.loaded() { - return Some(current_scene); - } - } - None - } } diff --git a/src/core/scene/mod.rs b/src/core/scene/mod.rs index 8642224..58a75df 100644 --- a/src/core/scene/mod.rs +++ b/src/core/scene/mod.rs @@ -1,23 +1,19 @@ -use std::{error::Error, sync::Arc}; +use std::error::Error; -use egui_winit_vulkano::Gui; -use vulkano::{image::view::ImageView, sync::GpuFuture}; +use vulkano::sync::GpuFuture; -mod context; -pub use context::SceneContext; +use crate::core::app::context::ApplicationContext; -mod manager; -pub use manager::SceneManager; +pub mod manager; pub trait Scene { fn loaded(&self) -> bool; - fn load(&mut self, scene_context: &SceneContext) -> Result<(), Box>; - fn update(&mut self, scene_context: &SceneContext) -> Result<(), Box>; + fn load(&mut self, app_context: &mut ApplicationContext) -> Result<(), Box>; + fn update(&mut self, app_context: &mut ApplicationContext) -> Result<(), Box>; fn render( &mut self, acquire_future: Box, - scene_context: &SceneContext, - gui: &mut Gui, + app_context: &mut ApplicationContext, ) -> Result, Box>; fn unload(&mut self); } diff --git a/src/game/scenes/main_scene.rs b/src/game/scenes/main_scene.rs index bad12d0..6461289 100644 --- a/src/game/scenes/main_scene.rs +++ b/src/game/scenes/main_scene.rs @@ -1,23 +1,21 @@ -use std::{error::Error, sync::Arc}; +use std::error::Error; +use super::settings_scene::SettingsScene; +use crate::core::app::DEPTH_IMAGE_ID; +use crate::core::app::context::ApplicationContext; 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::render_pass_manager::{RenderPassConfig, RenderPassManager}; use crate::core::render::texture::Texture; use crate::core::scene::Scene; -use crate::core::scene::SceneContext; use crate::game::assets::square::Square; -use egui_winit_vulkano::{Gui, egui}; +use egui_winit_vulkano::egui; use glam::EulerRot; use glam::Quat; use glam::Vec3; use vulkano::{ - command_buffer::{ - AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract, - RenderingAttachmentInfo, RenderingInfo, - }, - pipeline::graphics::viewport::Viewport, - render_pass::{AttachmentLoadOp, AttachmentStoreOp}, + command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract}, sync::GpuFuture, }; use winit::window::CursorGrabMode; @@ -40,12 +38,22 @@ impl Scene for MainScene { self.state.is_some() } - fn load(&mut self, scene_context: &SceneContext) -> Result<(), Box> { + fn load( + &mut self, + app_context: &mut ApplicationContext, + ) -> 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_mut(|renderer| renderer.swapchain_image_view().clone()); + let square = Square::new( - &scene_context.device, - &scene_context.memory_allocator, - scene_context.swapchain_image_view.format(), - scene_context.depth_stencil_image_view.format(), + &app_context.device, + &app_context.memory_allocator, + swapchain_image_view.format(), + depth_image_view.format(), )?; let num_instances = 100; @@ -69,29 +77,35 @@ impl Scene for MainScene { }) .collect(); - let camera = Camera3D::new( - scene_context.aspect_ratio, - std::f32::consts::FRAC_PI_2, - 0.01, - 1000.0, - ); + let texture = { + let mut uploads = AutoCommandBufferBuilder::primary( + app_context.command_buffer_allocator.clone(), + app_context.graphics_queue.queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + )?; - let mut uploads = AutoCommandBufferBuilder::primary( - scene_context.command_buffer_allocator.clone(), - scene_context.graphics_queue.queue_family_index(), - CommandBufferUsage::OneTimeSubmit, - )?; + let texture = Texture::from_file( + &app_context.device, + &app_context.memory_allocator, + &mut uploads, + "res/textures/wooden-crate.jpg", + )?; - let texture = Texture::from_file( - &scene_context.device, - &scene_context.memory_allocator, - &mut uploads, - "res/textures/wooden-crate.jpg", - )?; + let _ = uploads + .build()? + .execute(app_context.graphics_queue.clone())?; - let _ = uploads - .build()? - .execute(scene_context.graphics_queue.clone())?; + texture + }; + + 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, @@ -104,44 +118,46 @@ impl Scene for MainScene { Ok(()) } - fn update(&mut self, scene_context: &SceneContext) -> Result<(), Box> { - let state = self.state.as_mut().ok_or("State not found")?; - state.camera.update( - &scene_context.input_manager, - &scene_context.timer, - state.speed, - 10.0, - scene_context.aspect_ratio, - ); + fn update(&mut self, app_context: &mut ApplicationContext) -> 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(), + ); + }); + }); - if scene_context - .input_manager - .get_virtual_input_state("mouse_left") + if app_context + .with_input_manager(|input_manager| input_manager.get_virtual_input_state("mouse_left")) > 0.0 { - let _ = scene_context + let _ = app_context .event_loop_proxy - .send_event(UserEvent::CursorVisible(scene_context.window_id, false)); - let _ = scene_context + .send_event(UserEvent::CursorVisible(app_context.window_id, false)); + let _ = app_context .event_loop_proxy .send_event(UserEvent::CursorGrabMode( - scene_context.window_id, + app_context.window_id, CursorGrabMode::Locked, )); } - if scene_context - .input_manager - .get_virtual_input_state("mouse_right") - > 0.0 + if app_context.with_input_manager(|input_manager| { + input_manager.get_virtual_input_state("mouse_right") + }) > 0.0 { - let _ = scene_context + let _ = app_context .event_loop_proxy - .send_event(UserEvent::CursorVisible(scene_context.window_id, true)); - let _ = scene_context + .send_event(UserEvent::CursorVisible(app_context.window_id, true)); + let _ = app_context .event_loop_proxy .send_event(UserEvent::CursorGrabMode( - scene_context.window_id, + app_context.window_id, CursorGrabMode::None, )); } @@ -151,87 +167,112 @@ impl Scene for MainScene { fn render( &mut self, - acquire_future: Box, - scene_context: &SceneContext, - gui: &mut Gui, + before_future: Box, + app_context: &mut ApplicationContext, ) -> Result, Box> { - let state = self.state.as_ref().ok_or("State not found")?; + let state = self.state.as_ref().ok_or("State not loaded")?; let mut builder = AutoCommandBufferBuilder::primary( - scene_context.command_buffer_allocator.clone(), - scene_context.graphics_queue.queue_family_index(), + app_context.command_buffer_allocator.clone(), + app_context.graphics_queue.queue_family_index(), CommandBufferUsage::OneTimeSubmit, )?; { - let viewport = Viewport { - offset: [0.0, 0.0], - extent: scene_context.window_size, - depth_range: 0.0..=1.0, - }; - - builder - .begin_rendering(RenderingInfo { - color_attachments: vec![Some(RenderingAttachmentInfo { - load_op: AttachmentLoadOp::Clear, - store_op: AttachmentStoreOp::Store, - clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), - ..RenderingAttachmentInfo::image_view( - scene_context.swapchain_image_view.clone(), - ) - })], - depth_attachment: Some(RenderingAttachmentInfo { - load_op: AttachmentLoadOp::Clear, - store_op: AttachmentStoreOp::DontCare, - clear_value: Some([1.0].into()), - ..RenderingAttachmentInfo::image_view( - scene_context.depth_stencil_image_view.clone(), - ) - }), - ..Default::default() - })? - .set_viewport(0, [viewport].into_iter().collect())?; + let swapchain_image_view = + app_context.with_renderer_mut(|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(), + )?; } - let camera_uniform = state - .camera - .create_buffer(&scene_context.memory_allocator)?; + // Create camera uniform using the actual camera + let camera_uniform = state.camera.create_buffer(&app_context.memory_allocator)?; let transform_uniform = - Transform::create_buffer(&scene_context.memory_allocator, &state.instances)?; + Transform::create_buffer(&app_context.memory_allocator, &state.instances)?; state .square .render( &mut builder, - &scene_context.descriptor_set_allocator, + &app_context.descriptor_set_allocator, &camera_uniform, &transform_uniform, &state.texture, ) .unwrap(); - builder.end_rendering()?; + RenderPassManager::end_rendering(&mut builder)?; let command_buffer = builder.build()?; let render_future = - acquire_future.then_execute(scene_context.graphics_queue.clone(), command_buffer)?; + before_future.then_execute(app_context.graphics_queue.clone(), command_buffer)?; - gui.immediate_ui(|gui| { - let ctx = gui.context(); + let swapchain_image_view = + app_context.with_renderer_mut(|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::Window::new("Informations") - .vscroll(true) - .show(&ctx, |ui| { - ui.label(format!("Resolution: {:?}", scene_context.window_size)); + egui::SidePanel::left("side_panel").show(&ctx, |ui| { + ui.heading("Informations"); - ui.label(format!("{:#?}", scene_context.input_manager)); + ui.separator(); - ui.label(format!( - "Delta time: {:?}", - scene_context.timer.delta_time() - )); + 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); + + ui.label(format!("Delta time: {:?}", delta_time)); ui.label(format!( "Position: {:?}, Rotation: {:?}", @@ -239,13 +280,17 @@ impl Scene for MainScene { state.camera.get_rotation() )); }); - }); + }); - let render_future = - gui.draw_on_image(render_future, scene_context.swapchain_image_view.clone()); + let render_future = gui.draw_on_image(render_future, swapchain_image_view.clone()); + + render_future + }); Ok(render_future) } - fn unload(&mut self) {} + fn unload(&mut self) { + self.state = None; + } } diff --git a/src/game/scenes/mod.rs b/src/game/scenes/mod.rs index e5c4945..516fa6f 100644 --- a/src/game/scenes/mod.rs +++ b/src/game/scenes/mod.rs @@ -1,2 +1,2 @@ pub mod main_scene; -pub mod test_scene; +pub mod settings_scene; diff --git a/src/game/scenes/settings_scene.rs b/src/game/scenes/settings_scene.rs new file mode 100644 index 0000000..68e842b --- /dev/null +++ b/src/game/scenes/settings_scene.rs @@ -0,0 +1,135 @@ +use std::error::Error; + +use crate::core::app::DEPTH_IMAGE_ID; +use crate::core::app::context::ApplicationContext; +use crate::core::app::user_event::UserEvent; +use crate::core::render::render_pass_manager::{RenderPassConfig, RenderPassManager}; +use crate::core::scene::Scene; +use egui_winit_vulkano::egui; +use vulkano::{ + command_buffer::AutoCommandBufferBuilder, command_buffer::CommandBufferUsage, sync::GpuFuture, +}; + +use super::main_scene::MainScene; + +pub struct SettingsSceneState { + current_resolution: [f32; 2], + available_resolutions: Vec<(u32, u32)>, +} + +#[derive(Default)] +pub struct SettingsScene { + state: Option, +} + +impl Scene for SettingsScene { + fn loaded(&self) -> bool { + self.state.is_some() + } + + fn load(&mut self, app_context: &mut ApplicationContext) -> Result<(), Box> { + let current_resolution = app_context.get_window_size(); + let available_resolutions = app_context.get_available_resolutions(); + + self.state = Some(SettingsSceneState { + current_resolution, + available_resolutions, + }); + + Ok(()) + } + + fn update(&mut self, _app_context: &mut ApplicationContext) -> Result<(), Box> { + Ok(()) + } + + fn render( + &mut self, + before_future: Box, + app_context: &mut ApplicationContext, + ) -> Result, Box> { + let state = self.state.as_ref().ok_or("State not found")?; + + let mut builder = AutoCommandBufferBuilder::primary( + app_context.command_buffer_allocator.clone(), + app_context.graphics_queue.queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + )?; + + // Utiliser le RenderPassManager + { + let swapchain_image_view = + app_context.with_renderer_mut(|renderer| renderer.swapchain_image_view().clone()); + let depth_stencil_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_stencil_image_view), + app_context.get_window_size(), + )?; + } + + // Pas de géométrie dans cette scène - juste un écran de paramètres + 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_mut(|renderer| renderer.swapchain_image_view().clone()); + + let event_loop_proxy = app_context.event_loop_proxy.clone(); + let window_id = app_context.window_id; + + let render_future = app_context.with_gui_mut(|gui| { + gui.immediate_ui(|gui| { + let ctx = gui.context(); + + egui::CentralPanel::default().show(&ctx, |ui| { + ui.heading("Paramètres"); + + ui.separator(); + + ui.label(format!( + "Résolution actuelle: {:?}", + state.current_resolution + )); + + ui.separator(); + ui.label("Changer la résolution:"); + + for &(width, height) in &state.available_resolutions { + if ui.button(format!("{}x{}", width, height)).clicked() { + let _ = event_loop_proxy.send_event(UserEvent::ChangeResolution( + window_id, + width as f32, + height as f32, + )); + } + } + + ui.separator(); + + if ui.button("Retour au jeu").clicked() { + let _ = event_loop_proxy.send_event(UserEvent::ChangeScene( + window_id, + Box::new(MainScene::default()), + )); + } + }); + }); + + gui.draw_on_image(render_future, swapchain_image_view.clone()) + }); + + Ok(render_future) + } + + fn unload(&mut self) {} +} diff --git a/src/game/scenes/test_scene.rs b/src/game/scenes/test_scene.rs deleted file mode 100644 index 9ed8707..0000000 --- a/src/game/scenes/test_scene.rs +++ /dev/null @@ -1,111 +0,0 @@ -use std::error::Error; - -use crate::core::app::user_event::UserEvent; -use crate::core::scene::Scene; -use crate::core::scene::SceneContext; -use egui_winit_vulkano::{Gui, egui}; -use vulkano::command_buffer::AutoCommandBufferBuilder; -use vulkano::command_buffer::CommandBufferUsage; -use vulkano::command_buffer::RenderingAttachmentInfo; -use vulkano::command_buffer::RenderingInfo; -use vulkano::pipeline::graphics::viewport::Viewport; -use vulkano::render_pass::AttachmentLoadOp; -use vulkano::render_pass::AttachmentStoreOp; -use vulkano::sync::GpuFuture; - -use super::main_scene::MainScene; - -pub struct MainSceneState {} - -#[derive(Default)] -pub struct TestScene { - state: Option, -} - -impl Scene for TestScene { - fn loaded(&self) -> bool { - self.state.is_some() - } - - fn load(&mut self, scene_context: &SceneContext) -> Result<(), Box> { - self.state = Some(MainSceneState {}); - - Ok(()) - } - - fn update(&mut self, scene_context: &SceneContext) -> Result<(), Box> { - Ok(()) - } - - fn render( - &mut self, - acquire_future: Box, - scene_context: &SceneContext, - gui: &mut Gui, - ) -> Result, Box> { - let mut builder = AutoCommandBufferBuilder::primary( - scene_context.command_buffer_allocator.clone(), - scene_context.graphics_queue.queue_family_index(), - CommandBufferUsage::OneTimeSubmit, - )?; - - { - let viewport = Viewport { - offset: [0.0, 0.0], - extent: scene_context.window_size, - depth_range: 0.0..=1.0, - }; - - builder - .begin_rendering(RenderingInfo { - color_attachments: vec![Some(RenderingAttachmentInfo { - load_op: AttachmentLoadOp::Clear, - store_op: AttachmentStoreOp::Store, - clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), - ..RenderingAttachmentInfo::image_view( - scene_context.swapchain_image_view.clone(), - ) - })], - depth_attachment: Some(RenderingAttachmentInfo { - load_op: AttachmentLoadOp::Clear, - store_op: AttachmentStoreOp::DontCare, - clear_value: Some([1.0].into()), - ..RenderingAttachmentInfo::image_view( - scene_context.depth_stencil_image_view.clone(), - ) - }), - ..Default::default() - })? - .set_viewport(0, [viewport].into_iter().collect())?; - } - - builder.end_rendering()?; - - let command_buffer = builder.build()?; - - let render_future = - acquire_future.then_execute(scene_context.graphics_queue.clone(), command_buffer)?; - - gui.immediate_ui(|gui| { - let ctx = gui.context(); - - egui::CentralPanel::default().show(&ctx, |ui| { - if ui.button("Start Game").clicked() { - let _ = scene_context - .event_loop_proxy - .send_event(UserEvent::ChangeScene( - scene_context.window_id, - Box::new(MainScene::default()), - )); - } - }); - }); - - let render_future = - gui.draw_on_image(render_future, scene_context.swapchain_image_view.clone()); - - Ok(render_future) - } - - fn unload(&mut self) {} -}