Refactor texture loading
This commit is contained in:
parent
f91c0792b2
commit
a32cf6c747
19 changed files with 1499 additions and 337 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -2090,6 +2090,7 @@ dependencies = [
|
|||
"image",
|
||||
"rand 0.9.1",
|
||||
"thiserror 2.0.12",
|
||||
"tobj",
|
||||
"tracing",
|
||||
"tracing-log",
|
||||
"tracing-subscriber",
|
||||
|
@ -2466,6 +2467,15 @@ dependencies = [
|
|||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tobj"
|
||||
version = "4.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04aca6092e5978e708ee784e8ab9b5cf3cdb598b28f99a2f257446e7081a7025"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.22"
|
||||
|
|
|
@ -28,3 +28,6 @@ tracing-tracy = "0.11"
|
|||
|
||||
# Random
|
||||
rand = "0.9"
|
||||
|
||||
# OBJ loader
|
||||
tobj = "4.0"
|
||||
|
|
BIN
res/objects/cube-diffuse.jpg
Normal file
BIN
res/objects/cube-diffuse.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
BIN
res/objects/cube-normal.png
Normal file
BIN
res/objects/cube-normal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 117 KiB |
1143
res/objects/cube.obj
Normal file
1143
res/objects/cube.obj
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
|||
pub mod material_manager;
|
||||
pub mod primitives;
|
||||
pub mod render_pass_manager;
|
||||
pub mod texture;
|
||||
pub mod resources;
|
||||
pub mod vulkan_context;
|
||||
|
|
|
@ -14,10 +14,9 @@ mod command;
|
|||
|
||||
pub mod camera;
|
||||
pub mod mvp;
|
||||
pub mod resource;
|
||||
pub mod transform;
|
||||
pub mod vertex;
|
||||
|
||||
pub mod resource;
|
||||
pub use buffer::{AsBindableBuffer, AsIndexBuffer, AsUniformBuffer, AsVertexBuffer};
|
||||
pub use command::{AsRecordable, AsRenderableMesh, AsRenderableMeshInstance};
|
||||
|
||||
|
|
5
src/core/render/resources/meshes/mod.rs
Normal file
5
src/core/render/resources/meshes/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
mod square;
|
||||
pub use square::SquareMesh;
|
||||
|
||||
mod obj;
|
||||
pub use obj::ObjMesh;
|
76
src/core/render/resources/meshes/obj.rs
Normal file
76
src/core/render/resources/meshes/obj.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
use std::{error::Error, path::PathBuf, sync::Arc};
|
||||
|
||||
use vulkano::{buffer::Subbuffer, memory::allocator::StandardMemoryAllocator};
|
||||
|
||||
use crate::core::render::primitives::{
|
||||
AsIndexBuffer, AsRenderableMesh, AsVertexBuffer, vertex::Vertex3D,
|
||||
};
|
||||
|
||||
pub struct ObjMesh {
|
||||
vertex_buffer: Subbuffer<[Vertex3D]>,
|
||||
index_buffer: Subbuffer<[u32]>,
|
||||
}
|
||||
|
||||
impl ObjMesh {
|
||||
pub fn new(
|
||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
||||
file_path: impl Into<PathBuf>,
|
||||
) -> Result<Vec<Self>, Box<dyn Error>> {
|
||||
let (models, _) = tobj::load_obj(
|
||||
&file_path.into(),
|
||||
&tobj::LoadOptions {
|
||||
single_index: true,
|
||||
triangulate: true,
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
let meshes = models
|
||||
.into_iter()
|
||||
.map(|model| {
|
||||
let vertices = (0..model.mesh.positions.len() / 3)
|
||||
.map(|i| Vertex3D {
|
||||
position: [
|
||||
model.mesh.positions[i * 3],
|
||||
model.mesh.positions[i * 3 + 1],
|
||||
model.mesh.positions[i * 3 + 2],
|
||||
],
|
||||
uv: [model.mesh.texcoords[i * 2], model.mesh.texcoords[i * 2 + 1]],
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let indices = model.mesh.indices;
|
||||
|
||||
let vertex_buffer = Vertex3D::create_vertex_buffer(memory_allocator, &vertices)
|
||||
.expect("Failed to create vertex buffer");
|
||||
let index_buffer = u32::create_index_buffer(memory_allocator, &indices)
|
||||
.expect("Failed to create index buffer");
|
||||
|
||||
Self {
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(meshes)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRenderableMesh<Vertex3D, Subbuffer<[u32]>> for ObjMesh {
|
||||
fn vertex_buffer(&self) -> &Subbuffer<[Vertex3D]> {
|
||||
&self.vertex_buffer
|
||||
}
|
||||
|
||||
fn index_buffer(&self) -> Option<&Subbuffer<[u32]>> {
|
||||
Some(&self.index_buffer)
|
||||
}
|
||||
|
||||
fn vertex_count(&self) -> u32 {
|
||||
self.vertex_buffer.len() as u32
|
||||
}
|
||||
|
||||
fn index_count(&self) -> u32 {
|
||||
self.index_buffer.len() as u32
|
||||
}
|
||||
}
|
|
@ -27,12 +27,12 @@ const VERTICES: [Vertex3D; 4] = [
|
|||
|
||||
const INDICES: [u32; 6] = [0, 2, 1, 2, 3, 1];
|
||||
|
||||
pub struct Square {
|
||||
pub struct SquareMesh {
|
||||
vertex_buffer: Subbuffer<[Vertex3D]>,
|
||||
index_buffer: Subbuffer<[u32]>,
|
||||
}
|
||||
|
||||
impl Square {
|
||||
impl SquareMesh {
|
||||
pub fn new(memory_allocator: &Arc<StandardMemoryAllocator>) -> Result<Self, Box<dyn Error>> {
|
||||
let vertex_buffer = Vertex3D::create_vertex_buffer(memory_allocator, &VERTICES)?;
|
||||
let index_buffer = u32::create_index_buffer(memory_allocator, &INDICES)?;
|
||||
|
@ -44,7 +44,7 @@ impl Square {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsRenderableMesh<Vertex3D, Subbuffer<[u32]>> for Square {
|
||||
impl AsRenderableMesh<Vertex3D, Subbuffer<[u32]>> for SquareMesh {
|
||||
fn vertex_buffer(&self) -> &Subbuffer<[Vertex3D]> {
|
||||
&self.vertex_buffer
|
||||
}
|
2
src/core/render/resources/mod.rs
Normal file
2
src/core/render/resources/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod meshes;
|
||||
pub mod texture;
|
127
src/core/render/resources/texture/loader.rs
Normal file
127
src/core/render/resources/texture/loader.rs
Normal file
|
@ -0,0 +1,127 @@
|
|||
use std::{collections::HashMap, error::Error, sync::Arc};
|
||||
|
||||
use vulkano::{
|
||||
command_buffer::{
|
||||
AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract,
|
||||
allocator::StandardCommandBufferAllocator,
|
||||
},
|
||||
device::{Device, Queue},
|
||||
format::Format,
|
||||
image::sampler::SamplerCreateInfo,
|
||||
memory::allocator::StandardMemoryAllocator,
|
||||
};
|
||||
|
||||
use crate::core::app::context::WindowContext;
|
||||
|
||||
use super::Texture;
|
||||
|
||||
pub enum TextureSourceKind {
|
||||
File(String),
|
||||
Buffer(Vec<u8>),
|
||||
}
|
||||
|
||||
pub struct TextureLoadInfo {
|
||||
pub source: TextureSourceKind,
|
||||
pub sampler_create_info: SamplerCreateInfo,
|
||||
pub image_format: Format,
|
||||
}
|
||||
|
||||
pub struct TextureLoader {
|
||||
loaded_textures: HashMap<String, Texture>,
|
||||
pending_textures: HashMap<String, TextureLoadInfo>,
|
||||
device: Arc<Device>,
|
||||
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||
memory_allocator: Arc<StandardMemoryAllocator>,
|
||||
queue: Arc<Queue>,
|
||||
}
|
||||
|
||||
impl TextureLoader {
|
||||
pub fn new(app_context: &WindowContext) -> Self {
|
||||
Self {
|
||||
loaded_textures: HashMap::new(),
|
||||
pending_textures: HashMap::new(),
|
||||
device: app_context.device.clone(),
|
||||
command_buffer_allocator: app_context.command_buffer_allocator.clone(),
|
||||
memory_allocator: app_context.memory_allocator.clone(),
|
||||
queue: Self::select_best_suitable_queue(app_context),
|
||||
}
|
||||
}
|
||||
|
||||
fn select_best_suitable_queue(app_context: &WindowContext) -> Arc<Queue> {
|
||||
app_context
|
||||
.transfer_queue
|
||||
.as_ref()
|
||||
.map(|queue| {
|
||||
tracing::trace!(
|
||||
"Selected transfer queue for texture loading with family index: {:?}",
|
||||
queue.queue_family_index()
|
||||
);
|
||||
queue.clone()
|
||||
})
|
||||
.or_else(|| {
|
||||
tracing::trace!(
|
||||
"Selected graphics queue for texture loading with family index: {:?}",
|
||||
app_context.graphics_queue.queue_family_index()
|
||||
);
|
||||
Some(app_context.graphics_queue.clone())
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn add_texture(&mut self, name: String, load_info: TextureLoadInfo) {
|
||||
self.pending_textures.insert(name, load_info);
|
||||
}
|
||||
|
||||
pub fn load_pending_textures(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
let _span = tracing::info_span!("load_pending_textures");
|
||||
let mut uploads = AutoCommandBufferBuilder::primary(
|
||||
self.command_buffer_allocator.clone(),
|
||||
self.queue.queue_family_index(),
|
||||
CommandBufferUsage::OneTimeSubmit,
|
||||
)?;
|
||||
|
||||
let mut loading_textures = HashMap::new();
|
||||
|
||||
tracing::trace!("Pending textures count: {}", self.pending_textures.len());
|
||||
|
||||
for (name, info) in self.pending_textures.iter() {
|
||||
let texture = match &info.source {
|
||||
TextureSourceKind::File(path) => Texture::from_file(
|
||||
&self.device,
|
||||
&self.memory_allocator,
|
||||
&mut uploads,
|
||||
path.as_str(),
|
||||
info,
|
||||
)?,
|
||||
TextureSourceKind::Buffer(buffer) => Texture::from_bytes(
|
||||
&self.device,
|
||||
&self.memory_allocator,
|
||||
&mut uploads,
|
||||
&buffer,
|
||||
info,
|
||||
)?,
|
||||
};
|
||||
|
||||
loading_textures.insert(name.clone(), texture);
|
||||
tracing::trace!("Loaded texture: {}", name);
|
||||
}
|
||||
|
||||
let _ = uploads.build()?.execute(self.queue.clone())?;
|
||||
|
||||
self.loaded_textures.extend(loading_textures);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_texture(&self, name: &str) -> Option<&Texture> {
|
||||
self.loaded_textures.get(name)
|
||||
}
|
||||
|
||||
pub fn pending_textures_count(&self) -> usize {
|
||||
self.pending_textures.len()
|
||||
}
|
||||
|
||||
pub fn loaded_textures_count(&self) -> usize {
|
||||
self.loaded_textures.len()
|
||||
}
|
||||
}
|
5
src/core/render/resources/texture/mod.rs
Normal file
5
src/core/render/resources/texture/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
mod texture;
|
||||
pub use texture::Texture;
|
||||
|
||||
mod loader;
|
||||
pub use loader::{TextureLoadInfo, TextureLoader, TextureSourceKind};
|
|
@ -7,22 +7,20 @@ use vulkano::{
|
|||
command_buffer::{AutoCommandBufferBuilder, CopyBufferToImageInfo, PrimaryAutoCommandBuffer},
|
||||
descriptor_set::{
|
||||
DescriptorSet, WriteDescriptorSet,
|
||||
allocator::{DescriptorSetAllocator, StandardDescriptorSetAllocator},
|
||||
allocator::StandardDescriptorSetAllocator,
|
||||
layout::{DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorType},
|
||||
},
|
||||
device::Device,
|
||||
format::Format,
|
||||
image::{
|
||||
Image, ImageCreateInfo, ImageType, ImageUsage,
|
||||
sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo},
|
||||
view::ImageView,
|
||||
},
|
||||
image::{Image, ImageCreateInfo, ImageType, ImageUsage, sampler::Sampler, view::ImageView},
|
||||
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator},
|
||||
shader::ShaderStages,
|
||||
};
|
||||
|
||||
use crate::core::render::primitives::AsBindableDescriptorSet;
|
||||
|
||||
use super::TextureLoadInfo;
|
||||
|
||||
pub struct Texture {
|
||||
texture: Arc<ImageView>,
|
||||
sampler: Arc<Sampler>,
|
||||
|
@ -33,37 +31,39 @@ impl Texture {
|
|||
Self { texture, sampler }
|
||||
}
|
||||
|
||||
pub fn from_file(
|
||||
pub(super) fn from_file(
|
||||
device: &Arc<Device>,
|
||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
||||
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
||||
path: &str,
|
||||
load_info: &TextureLoadInfo,
|
||||
) -> Result<Self, Box<dyn Error>> {
|
||||
let _span = tracing::info_span!("texture_load_from_file", path = path);
|
||||
|
||||
let bytes = std::fs::read(path)?;
|
||||
Self::from_bytes(device, memory_allocator, builder, &bytes)
|
||||
Self::from_bytes(device, memory_allocator, builder, &bytes, load_info)
|
||||
}
|
||||
|
||||
pub fn from_bytes(
|
||||
pub(super) fn from_bytes(
|
||||
device: &Arc<Device>,
|
||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
||||
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
||||
bytes: &[u8],
|
||||
load_info: &TextureLoadInfo,
|
||||
) -> Result<Self, Box<dyn Error>> {
|
||||
let image = image::load_from_memory(bytes)?;
|
||||
Self::from_dynamic_image(device, memory_allocator, builder, image)
|
||||
Self::from_dynamic_image(device, memory_allocator, builder, image, load_info)
|
||||
}
|
||||
|
||||
pub fn from_dynamic_image(
|
||||
fn from_dynamic_image(
|
||||
device: &Arc<Device>,
|
||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
||||
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
||||
image: DynamicImage,
|
||||
load_info: &TextureLoadInfo,
|
||||
) -> Result<Self, Box<dyn Error>> {
|
||||
let _span = tracing::info_span!("texture_from_dynamic_image");
|
||||
|
||||
let image_data = image.to_rgba8();
|
||||
let image_data = match load_info.image_format {
|
||||
Format::R8G8B8A8_SRGB => image.to_rgba8(),
|
||||
_ => return Err("Unsupported format".into()),
|
||||
};
|
||||
let image_dimensions = image_data.dimensions();
|
||||
let image_data = image_data.into_raw();
|
||||
|
||||
|
@ -90,7 +90,7 @@ impl Texture {
|
|||
memory_allocator.clone(),
|
||||
ImageCreateInfo {
|
||||
image_type: ImageType::Dim2d,
|
||||
format: Format::R8G8B8A8_SRGB,
|
||||
format: load_info.image_format,
|
||||
extent: [image_dimensions.0, image_dimensions.1, 1],
|
||||
array_layers: 1,
|
||||
usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED,
|
||||
|
@ -104,30 +104,12 @@ impl Texture {
|
|||
image.clone(),
|
||||
))?;
|
||||
|
||||
let sampler = Sampler::new(
|
||||
device.clone(),
|
||||
SamplerCreateInfo {
|
||||
mag_filter: Filter::Linear,
|
||||
min_filter: Filter::Linear,
|
||||
address_mode: [SamplerAddressMode::Repeat; 3],
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
let sampler = Sampler::new(device.clone(), load_info.sampler_create_info.clone())?;
|
||||
|
||||
let image_view = ImageView::new_default(image)?;
|
||||
|
||||
tracing::trace!("Texture loaded with dimensions {:?}", image_dimensions);
|
||||
|
||||
Ok(Self::new(image_view, sampler))
|
||||
}
|
||||
|
||||
pub fn get_texture(&self) -> &Arc<ImageView> {
|
||||
&self.texture
|
||||
}
|
||||
|
||||
pub fn get_sampler(&self) -> &Arc<Sampler> {
|
||||
&self.sampler
|
||||
}
|
||||
}
|
||||
|
||||
impl AsBindableDescriptorSet<Texture> for Texture {
|
|
@ -1 +0,0 @@
|
|||
pub mod square;
|
|
@ -1,2 +1 @@
|
|||
pub mod meshs;
|
||||
pub mod pipelines;
|
||||
|
|
|
@ -31,7 +31,7 @@ use crate::core::render::{
|
|||
AsBindableDescriptorSet, AsRecordable, AsRenderableMesh, AsRenderableMeshInstance,
|
||||
mvp::Mvp, transform::TransformRaw, vertex::Vertex3D,
|
||||
},
|
||||
texture::Texture,
|
||||
resources::texture::Texture,
|
||||
};
|
||||
|
||||
pub struct SimplePipelineRenderData<'a> {
|
||||
|
|
|
@ -1,255 +0,0 @@
|
|||
use std::{error::Error, sync::Arc};
|
||||
|
||||
use vulkano::{
|
||||
buffer::Subbuffer,
|
||||
command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer},
|
||||
descriptor_set::{
|
||||
DescriptorSet, allocator::StandardDescriptorSetAllocator,
|
||||
layout::DescriptorSetLayoutCreateInfo,
|
||||
},
|
||||
device::Device,
|
||||
format::Format,
|
||||
memory::allocator::StandardMemoryAllocator,
|
||||
pipeline::{
|
||||
DynamicState, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout,
|
||||
PipelineShaderStageCreateInfo,
|
||||
graphics::{
|
||||
GraphicsPipelineCreateInfo,
|
||||
color_blend::{ColorBlendAttachmentState, ColorBlendState},
|
||||
depth_stencil::{DepthState, DepthStencilState},
|
||||
input_assembly::InputAssemblyState,
|
||||
multisample::MultisampleState,
|
||||
rasterization::RasterizationState,
|
||||
subpass::PipelineRenderingCreateInfo,
|
||||
vertex_input::{Vertex, VertexDefinition},
|
||||
viewport::ViewportState,
|
||||
},
|
||||
layout::{PipelineDescriptorSetLayoutCreateInfo, PipelineLayoutCreateFlags},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::core::render::{
|
||||
primitives::{
|
||||
AsBindableDescriptorSet, AsDrawable, AsIndexBuffer, AsRecordable, AsVertexBuffer, mvp::Mvp,
|
||||
transform::TransformRaw, vertex::Vertex3D,
|
||||
},
|
||||
texture::Texture,
|
||||
};
|
||||
|
||||
const VERTICES: [Vertex3D; 4] = [
|
||||
Vertex3D {
|
||||
position: [-0.5, -0.5, 0.0],
|
||||
uv: [0.0, 0.0],
|
||||
},
|
||||
Vertex3D {
|
||||
position: [-0.5, 0.5, 0.0],
|
||||
uv: [0.0, 1.0],
|
||||
},
|
||||
Vertex3D {
|
||||
position: [0.5, -0.5, 0.0],
|
||||
uv: [1.0, 0.0],
|
||||
},
|
||||
Vertex3D {
|
||||
position: [0.5, 0.5, 0.0],
|
||||
uv: [1.0, 1.0],
|
||||
},
|
||||
];
|
||||
|
||||
const INDICES: [u32; 6] = [0, 2, 1, 2, 3, 1];
|
||||
|
||||
pub mod shaders {
|
||||
pub mod vs {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "vertex",
|
||||
path: r"res/shaders/vertex.vert",
|
||||
generate_structs: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod fs {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "fragment",
|
||||
path: r"res/shaders/vertex.frag",
|
||||
generate_structs: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Square {
|
||||
vertex_buffer: Subbuffer<[Vertex3D]>,
|
||||
index_buffer: Subbuffer<[u32]>,
|
||||
pipeline: Arc<GraphicsPipeline>,
|
||||
}
|
||||
|
||||
/// Structure pour encapsuler les données de rendu du Square
|
||||
pub struct SquareRenderData<'a> {
|
||||
pub square: &'a Square,
|
||||
pub mvp_uniform: &'a Subbuffer<[Mvp]>,
|
||||
pub transform_uniform: &'a Subbuffer<[TransformRaw]>,
|
||||
pub descriptor_sets: &'a [Arc<DescriptorSet>],
|
||||
pub texture: &'a Texture,
|
||||
}
|
||||
|
||||
impl Square {
|
||||
pub fn new(
|
||||
device: &Arc<Device>,
|
||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
||||
swapchain_format: Format,
|
||||
depth_format: Format,
|
||||
) -> Result<Self, Box<dyn Error>> {
|
||||
let vertex_buffer = Vertex3D::create_vertex_buffer(memory_allocator, &VERTICES)?;
|
||||
|
||||
let index_buffer = u32::create_index_buffer(memory_allocator, &INDICES)?;
|
||||
|
||||
let vs = shaders::vs::load(device.clone())?
|
||||
.entry_point("main")
|
||||
.ok_or("Failed find main entry point of vertex shader".to_string())?;
|
||||
|
||||
let fs = shaders::fs::load(device.clone())?
|
||||
.entry_point("main")
|
||||
.ok_or("Failed find main entry point of fragment shader".to_string())?;
|
||||
|
||||
let vertex_input_state =
|
||||
[Vertex3D::per_vertex(), TransformRaw::per_instance()].definition(&vs)?;
|
||||
|
||||
let stages = [
|
||||
PipelineShaderStageCreateInfo::new(vs),
|
||||
PipelineShaderStageCreateInfo::new(fs),
|
||||
];
|
||||
|
||||
let vertex_bindings = Mvp::as_descriptor_set_layout_bindings();
|
||||
let texture_bindings = Texture::as_descriptor_set_layout_bindings();
|
||||
|
||||
let vertex_descriptor_set_layout = DescriptorSetLayoutCreateInfo {
|
||||
bindings: vertex_bindings,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let fragment_descriptor_set_layout = DescriptorSetLayoutCreateInfo {
|
||||
bindings: texture_bindings,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let create_info = PipelineDescriptorSetLayoutCreateInfo {
|
||||
set_layouts: vec![vertex_descriptor_set_layout, fragment_descriptor_set_layout],
|
||||
flags: PipelineLayoutCreateFlags::default(),
|
||||
push_constant_ranges: vec![],
|
||||
}
|
||||
.into_pipeline_layout_create_info(device.clone())?;
|
||||
|
||||
let layout = PipelineLayout::new(device.clone(), create_info)?;
|
||||
|
||||
let subpass = PipelineRenderingCreateInfo {
|
||||
color_attachment_formats: vec![Some(swapchain_format)],
|
||||
depth_attachment_format: Some(depth_format),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let pipeline = GraphicsPipeline::new(
|
||||
device.clone(),
|
||||
None,
|
||||
GraphicsPipelineCreateInfo {
|
||||
stages: stages.into_iter().collect(),
|
||||
vertex_input_state: Some(vertex_input_state),
|
||||
input_assembly_state: Some(InputAssemblyState::default()),
|
||||
viewport_state: Some(ViewportState::default()),
|
||||
rasterization_state: Some(RasterizationState::default()),
|
||||
multisample_state: Some(MultisampleState::default()),
|
||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||
subpass.color_attachment_formats.len() as u32,
|
||||
ColorBlendAttachmentState::default(),
|
||||
)),
|
||||
depth_stencil_state: Some(DepthStencilState {
|
||||
depth: Some(DepthState::simple()),
|
||||
..Default::default()
|
||||
}),
|
||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
||||
subpass: Some(subpass.into()),
|
||||
..GraphicsPipelineCreateInfo::layout(layout)
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
pipeline,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn render(
|
||||
&self,
|
||||
command_buffer: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
||||
descriptor_set_allocator: &Arc<StandardDescriptorSetAllocator>,
|
||||
mvp_uniform: &Subbuffer<[Mvp]>,
|
||||
transform_uniform: &Subbuffer<[TransformRaw]>,
|
||||
texture: &Texture,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let layouts = self.pipeline.layout().set_layouts();
|
||||
|
||||
let uniform_descriptor_set =
|
||||
Mvp::as_descriptor_set(descriptor_set_allocator, &layouts[0], mvp_uniform)?;
|
||||
|
||||
let texture_descriptor_set =
|
||||
Texture::as_descriptor_set(descriptor_set_allocator, &layouts[1], texture)?;
|
||||
|
||||
let render_data = SquareRenderData {
|
||||
square: self,
|
||||
mvp_uniform,
|
||||
transform_uniform,
|
||||
texture,
|
||||
descriptor_sets: &[uniform_descriptor_set, texture_descriptor_set],
|
||||
};
|
||||
|
||||
// Utiliser les nouveaux traits pour le rendu
|
||||
Self::record_render_commands(command_buffer, &self.pipeline, &render_data)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsRecordable<SquareRenderData<'a>> for Square {
|
||||
fn record_render_commands(
|
||||
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
||||
pipeline: &Arc<GraphicsPipeline>,
|
||||
data: &SquareRenderData<'a>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
// Bind pipeline
|
||||
builder.bind_pipeline_graphics(pipeline.clone())?;
|
||||
|
||||
// Bind descriptor sets
|
||||
builder.bind_descriptor_sets(
|
||||
PipelineBindPoint::Graphics,
|
||||
pipeline.layout().clone(),
|
||||
0,
|
||||
data.descriptor_sets.iter().cloned().collect::<Vec<_>>(),
|
||||
)?;
|
||||
|
||||
// Bind buffers
|
||||
builder.bind_vertex_buffers(
|
||||
0,
|
||||
(
|
||||
Self::vertex_buffer(data).clone(),
|
||||
data.transform_uniform.clone(),
|
||||
),
|
||||
)?;
|
||||
|
||||
if let Some(index_buffer) = Self::index_buffer(data) {
|
||||
builder.bind_index_buffer(index_buffer.clone())?;
|
||||
unsafe {
|
||||
builder.draw_indexed(
|
||||
Self::index_count(data),
|
||||
Self::instance_count(data),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
builder.draw(Self::vertex_count(data), Self::instance_count(data), 0, 0)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -4,19 +4,22 @@ 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::AsRecordable;
|
||||
use crate::core::render::primitives::camera::Camera3D;
|
||||
use crate::core::render::primitives::mvp::Mvp;
|
||||
use crate::core::render::primitives::transform::Transform;
|
||||
use crate::core::render::primitives::{AsBindableDescriptorSet, AsRecordable};
|
||||
use crate::core::render::render_pass_manager::{RenderPassConfig, RenderPassManager};
|
||||
use crate::core::render::texture::Texture;
|
||||
use crate::core::render::resources::meshes::{ObjMesh, SquareMesh};
|
||||
use crate::core::render::resources::texture::{
|
||||
Texture, TextureLoadInfo, TextureLoader, TextureSourceKind,
|
||||
};
|
||||
use crate::core::scene::Scene;
|
||||
use crate::game::assets::meshs::square::Square;
|
||||
use crate::game::assets::pipelines::simple::{SimplePipeline, SimplePipelineRenderData};
|
||||
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, PrimaryCommandBufferAbstract},
|
||||
sync::GpuFuture,
|
||||
|
@ -24,11 +27,13 @@ use vulkano::{
|
|||
use winit::window::CursorGrabMode;
|
||||
|
||||
pub struct MainSceneState {
|
||||
square: Square,
|
||||
texture_loader: TextureLoader,
|
||||
square: SquareMesh,
|
||||
obj: ObjMesh,
|
||||
simple_pipeline: SimplePipeline,
|
||||
instances: Vec<Transform>,
|
||||
square_instances: Vec<Transform>,
|
||||
obj_instances: Vec<Transform>,
|
||||
camera: Camera3D,
|
||||
texture: Texture,
|
||||
speed: f32,
|
||||
}
|
||||
|
||||
|
@ -50,7 +55,41 @@ impl Scene for MainScene {
|
|||
let swapchain_image_view =
|
||||
app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone());
|
||||
|
||||
let square = Square::new(&app_context.memory_allocator)?;
|
||||
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 simple_pipeline = SimplePipeline::new(
|
||||
&app_context.device,
|
||||
swapchain_image_view.format(),
|
||||
|
@ -61,7 +100,7 @@ impl Scene for MainScene {
|
|||
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 instances: Vec<Transform> = (0..num_instances)
|
||||
let square_instances: Vec<Transform> = (0..num_instances)
|
||||
.map(|i| {
|
||||
Transform::new(
|
||||
Vec3::new(
|
||||
|
@ -75,26 +114,26 @@ impl Scene for MainScene {
|
|||
})
|
||||
.collect();
|
||||
|
||||
let texture = {
|
||||
let mut uploads = AutoCommandBufferBuilder::primary(
|
||||
app_context.command_buffer_allocator.clone(),
|
||||
app_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 _ = uploads
|
||||
.build()?
|
||||
.execute(app_context.graphics_queue.clone())?;
|
||||
|
||||
texture
|
||||
};
|
||||
let obj_instances: Vec<Transform> = (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(
|
||||
|
@ -107,11 +146,13 @@ impl Scene for MainScene {
|
|||
|
||||
self.state = Some(MainSceneState {
|
||||
square,
|
||||
obj,
|
||||
simple_pipeline,
|
||||
instances,
|
||||
square_instances,
|
||||
obj_instances,
|
||||
camera,
|
||||
texture,
|
||||
speed: 50.0,
|
||||
texture_loader,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
|
@ -132,7 +173,14 @@ impl Scene for MainScene {
|
|||
});
|
||||
|
||||
let delta_time = app_context.get_delta_time();
|
||||
for (i, instance) in state.instances.iter_mut().enumerate() {
|
||||
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);
|
||||
|
||||
|
@ -203,21 +251,40 @@ impl Scene for MainScene {
|
|||
|
||||
// Create camera uniform using the actual camera
|
||||
let camera_uniform = state.camera.create_buffer(&app_context.memory_allocator)?;
|
||||
let transform_uniform =
|
||||
Transform::create_buffer(&app_context.memory_allocator, &state.instances)?;
|
||||
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)?;
|
||||
|
||||
SimplePipeline::record_bind_commands(
|
||||
&mut builder,
|
||||
&app_context.descriptor_set_allocator,
|
||||
state.simple_pipeline.pipeline(),
|
||||
&state.square,
|
||||
&transform_uniform,
|
||||
&square_transform_uniform,
|
||||
&SimplePipelineRenderData {
|
||||
mvp_uniform: &camera_uniform,
|
||||
texture: &state.texture,
|
||||
texture: &state.texture_loader.get_texture("wooden-crate").unwrap(),
|
||||
},
|
||||
)?;
|
||||
SimplePipeline::record_draw_commands(&mut builder, &state.square, &transform_uniform)?;
|
||||
SimplePipeline::record_draw_commands(
|
||||
&mut builder,
|
||||
&state.square,
|
||||
&square_transform_uniform,
|
||||
)?;
|
||||
|
||||
SimplePipeline::record_bind_commands(
|
||||
&mut builder,
|
||||
&app_context.descriptor_set_allocator,
|
||||
state.simple_pipeline.pipeline(),
|
||||
&state.obj,
|
||||
&obj_transform_uniform,
|
||||
&SimplePipelineRenderData {
|
||||
mvp_uniform: &camera_uniform,
|
||||
texture: &state.texture_loader.get_texture("cube-diffuse").unwrap(),
|
||||
},
|
||||
)?;
|
||||
SimplePipeline::record_draw_commands(&mut builder, &state.obj, &obj_transform_uniform)?;
|
||||
|
||||
RenderPassManager::end_rendering(&mut builder)?;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue