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::velocity::Velocity; use crate::core::render::primitives::vulkan_resource::{ VulkanCommandBufferAllocator, VulkanDescriptorSetAllocator, VulkanDevice, VulkanGraphicsQueue, VulkanMemoryAllocator, }; 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::AsScene; use crate::core::timer::Timer; use crate::game::assets::pipelines::simple::SimplePipeline; use bevy_ecs::prelude::*; use bevy_ecs::schedule::Schedule; use bevy_ecs::world::World; 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; #[derive(Component)] pub struct Square; #[derive(Component)] pub struct Cube; pub struct MainSceneState { pipeline_loader: PipelineLoader, square: SquareMesh, obj: ObjMesh, camera: Camera3D, speed: f32, scheduler: Schedule, } #[derive(Default)] pub struct MainScene { state: Option, } impl AsScene for MainScene { fn loaded(&self) -> bool { self.state.is_some() } fn load( &mut self, world: &mut World, window_context: &mut WindowContext, ) -> Result<(), Box> { let depth_image_view = window_context.with_renderer_mut(|renderer| { renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone() }); let swapchain_image_view = window_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); let mut pipeline_loader = PipelineLoader::new( VulkanDevice::get_from_world(world).clone(), swapchain_image_view.format(), depth_image_view.format(), ); pipeline_loader.register::()?; pipeline_loader.load_pending_pipelines()?; let mut texture_loader = world.resource_mut::(); 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(VulkanMemoryAllocator::get_from_world(world))?; let obj = { let obj = ObjMesh::new( VulkanMemoryAllocator::get_from_world(world), "res/objects/cube.obj", )?; obj.into_iter().next().unwrap() }; let camera = window_context.with_renderer(|renderer| { Camera3D::new( renderer.aspect_ratio(), std::f32::consts::FRAC_PI_2, 0.01, 1000.0, ) }); let mut scheduler = Schedule::default(); scheduler.add_systems(update_velocity_system); scheduler.add_systems(update_timer_system); world.insert_resource(Timer::new()); Self::create_entities(world, 100, 10.0, 10.0); self.state = Some(MainSceneState { square, obj, pipeline_loader, camera, speed: 50.0, scheduler, }); Ok(()) } fn update( &mut self, world: &mut World, window_context: &WindowContext, ) -> Result<(), Box> { let state = self.state.as_mut().unwrap(); window_context.with_input_manager(|input_manager| { window_context.with_timer(|timer| { state.camera.update( input_manager, timer, state.speed, 10.0, window_context.get_aspect_ratio(), ); }); }); state.scheduler.run(world); if window_context .with_input_manager(|input_manager| input_manager.get_virtual_input_state("mouse_left")) > 0.0 { let _ = window_context .event_loop_proxy .send_event(UserEvent::CursorVisible(window_context.window_id, false)); let _ = window_context .event_loop_proxy .send_event(UserEvent::CursorGrabMode( window_context.window_id, CursorGrabMode::Locked, )); } if window_context.with_input_manager(|input_manager| { input_manager.get_virtual_input_state("mouse_right") }) > 0.0 { let _ = window_context .event_loop_proxy .send_event(UserEvent::CursorVisible(window_context.window_id, true)); let _ = window_context .event_loop_proxy .send_event(UserEvent::CursorGrabMode( window_context.window_id, CursorGrabMode::None, )); } Ok(()) } fn render( &mut self, before_future: Box, world: &mut World, window_context: &mut WindowContext, ) -> Result, Box> { let state = self.state.as_mut().ok_or("State not loaded")?; let mut builder = AutoCommandBufferBuilder::primary( VulkanCommandBufferAllocator::get_from_world(world).clone(), VulkanGraphicsQueue::get_from_world(world).queue_family_index(), CommandBufferUsage::OneTimeSubmit, )?; { let swapchain_image_view = window_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); let depth_image_view = window_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), window_context.get_window_size(), )?; } // Create camera uniform using the actual camera let camera_uniform = Arc::new( state .camera .create_buffer(VulkanMemoryAllocator::get_from_world(world))?, ); let square_transforms = world .query_filtered::<&Transform, With>() .iter(&world) .cloned() .collect::>(); let square_transform_uniform = Transform::create_buffer( VulkanMemoryAllocator::get_from_world(world), &square_transforms, )?; let cube_transforms = world .query_filtered::<&Transform, With>() .iter(&world) .cloned() .collect::>(); let cube_transform_uniform = Transform::create_buffer( VulkanMemoryAllocator::get_from_world(world), &cube_transforms, )?; let texture_loader = world.resource::(); state .pipeline_loader .with_pipeline::(|pipeline| { SimplePipeline::record_commands( &mut builder, &VulkanDescriptorSetAllocator::get_from_world(world), pipeline, &state.square, &square_transform_uniform, vec![ camera_uniform.clone() as Arc, texture_loader.get_texture("wooden-crate").unwrap().clone(), ], )?; SimplePipeline::record_commands( &mut builder, &VulkanDescriptorSetAllocator::get_from_world(world), pipeline, &state.obj, &cube_transform_uniform, vec![ camera_uniform.clone() as Arc, 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( VulkanGraphicsQueue::get_from_world(world).clone(), command_buffer, )?; let swapchain_image_view = window_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); let input_manager_status = window_context.with_input_manager(|input_manager| format!("{:#?}", input_manager)); let event_loop_proxy = window_context.event_loop_proxy.clone(); let delta_time = window_context.get_delta_time(); let window_id = window_context.window_id; let window_size = window_context.get_window_size(); let render_future = window_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(Box::new(render_future)) } fn unload(&mut self) { self.state = None; } } impl MainScene { // Function to create entities in the ECS world fn create_entities( world: &mut World, num_instances: u32, instance_size: f32, instance_spacing: f32, ) { let num_instances_per_row = (num_instances as f32 / instance_spacing).ceil() as u32; let square_instances = (0..num_instances) .map(|i| { let x_index = i % num_instances_per_row; let z_index = i / num_instances_per_row; let transform = Transform::new( Vec3::new( x_index as f32 * (instance_spacing + instance_size), 0.0, z_index 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), ); let velocity = Velocity::new(Vec3::ZERO, Vec3::new(0.0, x_index as f32, 0.0)); (Square, transform, velocity) }) .collect::>(); world.spawn_batch(square_instances); let cube_instances = (0..num_instances) .map(|i| { let x_index = i % num_instances_per_row; let z_index = i / num_instances_per_row; let transform = Transform::new( Vec3::new( x_index as f32 * (instance_spacing + instance_size), 0.0, z_index 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, ), ); let velocity = Velocity::new(Vec3::ZERO, Vec3::new(0.0, x_index as f32, 0.0)); (Cube, transform, velocity) }) .collect::>(); world.spawn_batch(cube_instances); } } fn update_velocity_system(mut query: Query<(&mut Transform, &Velocity)>, time: Res) { for (mut transform, velocity) in query.iter_mut() { let delta_time = time.delta_time(); // Update linear position transform.translate(velocity.linear * delta_time); // Update angular rotation let angular_delta = velocity.angular * delta_time; let rotation_delta = Quat::from_euler( EulerRot::XYZ, angular_delta.x, angular_delta.y, angular_delta.z, ); transform.rotate(rotation_delta); } } fn update_timer_system(mut timer: ResMut) { timer.update(); }