453 lines
16 KiB
Rust
453 lines
16 KiB
Rust
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<MainSceneState>,
|
|
}
|
|
|
|
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<dyn std::error::Error>> {
|
|
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::<SimplePipeline>()?;
|
|
pipeline_loader.load_pending_pipelines()?;
|
|
|
|
let mut texture_loader = world.resource_mut::<TextureLoader>();
|
|
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<dyn Error>> {
|
|
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<dyn GpuFuture>,
|
|
world: &mut World,
|
|
window_context: &mut WindowContext,
|
|
) -> Result<Box<dyn GpuFuture>, Box<dyn Error>> {
|
|
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<Square>>()
|
|
.iter(&world)
|
|
.cloned()
|
|
.collect::<Vec<Transform>>();
|
|
let square_transform_uniform = Transform::create_buffer(
|
|
VulkanMemoryAllocator::get_from_world(world),
|
|
&square_transforms,
|
|
)?;
|
|
let cube_transforms = world
|
|
.query_filtered::<&Transform, With<Cube>>()
|
|
.iter(&world)
|
|
.cloned()
|
|
.collect::<Vec<Transform>>();
|
|
let cube_transform_uniform = Transform::create_buffer(
|
|
VulkanMemoryAllocator::get_from_world(world),
|
|
&cube_transforms,
|
|
)?;
|
|
|
|
let texture_loader = world.resource::<TextureLoader>();
|
|
|
|
state
|
|
.pipeline_loader
|
|
.with_pipeline::<SimplePipeline, _>(|pipeline| {
|
|
SimplePipeline::record_commands(
|
|
&mut builder,
|
|
&VulkanDescriptorSetAllocator::get_from_world(world),
|
|
pipeline,
|
|
&state.square,
|
|
&square_transform_uniform,
|
|
vec![
|
|
camera_uniform.clone() as Arc<dyn AsDescriptorSet>,
|
|
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<dyn AsDescriptorSet>,
|
|
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::<Vec<_>>();
|
|
|
|
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::<Vec<_>>();
|
|
|
|
world.spawn_batch(cube_instances);
|
|
}
|
|
}
|
|
|
|
fn update_velocity_system(mut query: Query<(&mut Transform, &Velocity)>, time: Res<Timer>) {
|
|
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>) {
|
|
timer.update();
|
|
}
|