feat: additional implementation of renderer

This commit is contained in:
alphafetus committed 2026-08-25 10:31:36 -04:00
1 parent 639875b603
commit 8683f8b565
15 files changed
+194 -192

No files matched your search

+52 -67
View File
@@ -1,4 +1,9 @@
use crate::common::camera::{Camera, CameraResources};
use crate::common::instance::InstanceRegistry;
use crate::common::mesh::MeshRegistry; use crate::common::mesh::MeshRegistry;
use crate::common::render_batch::RenderBatch;
use crate::common::scene::Scene;
use crate::gpu::instance::GpuInstance;
use crate::gpu::vertex3::Vertex3; use crate::gpu::vertex3::Vertex3;
use crate::triangle; use crate::triangle;
use std::error::Error; use std::error::Error;
@@ -15,35 +20,33 @@ use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{ use vulkano::device::{
Device, DeviceCreateInfo, DeviceExtensions, Queue, QueueCreateInfo, QueueFlags, Device, DeviceCreateInfo, DeviceExtensions, Queue, QueueCreateInfo, QueueFlags,
}; };
use vulkano::format::Format;
use vulkano::image::view::ImageView; use vulkano::image::view::ImageView;
use vulkano::image::{Image, ImageCreateInfo, ImageType, ImageUsage}; use vulkano::image::{Image, ImageCreateInfo, ImageType, ImageUsage};
use vulkano::instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}; use vulkano::instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions};
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryAllocator, StandardMemoryAllocator}; use vulkano::memory::allocator::{AllocationCreateInfo, MemoryAllocator, StandardMemoryAllocator};
use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo;
use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState}; use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState};
use vulkano::pipeline::graphics::depth_stencil::{DepthState, DepthStencilState};
use vulkano::pipeline::graphics::input_assembly::InputAssemblyState; use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
use vulkano::pipeline::graphics::multisample::MultisampleState; use vulkano::pipeline::graphics::multisample::MultisampleState;
use vulkano::pipeline::graphics::rasterization::RasterizationState; use vulkano::pipeline::graphics::rasterization::RasterizationState;
use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition}; use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition};
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
use vulkano::pipeline::layout::PipelineDescriptorSetLayoutCreateInfo; use vulkano::pipeline::layout::PipelineDescriptorSetLayoutCreateInfo;
use vulkano::pipeline::{DynamicState, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout, PipelineShaderStageCreateInfo}; use vulkano::pipeline::{
DynamicState, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout,
PipelineShaderStageCreateInfo,
};
use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass};
use vulkano::shader::ShaderModule; use vulkano::shader::ShaderModule;
use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo}; use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo};
use vulkano::sync::GpuFuture; use vulkano::sync::GpuFuture;
use vulkano::{Validated, VulkanError, VulkanLibrary, swapchain, sync}; use vulkano::{Validated, VulkanError, VulkanLibrary, swapchain, sync};
use vulkano::format::Format;
use vulkano::pipeline::graphics::depth_stencil::{DepthState, DepthStencilState};
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::event::WindowEvent; use winit::event::WindowEvent;
use winit::event_loop::ActiveEventLoop; use winit::event_loop::ActiveEventLoop;
use winit::window::{Window, WindowAttributes, WindowId}; use winit::window::{Window, WindowAttributes, WindowId};
use crate::common::camera::{Camera, CameraResources};
use crate::common::instance::InstanceRegistry;
use crate::common::render_batch::RenderBatch;
use crate::common::scene::Scene;
use crate::gpu::instance::GpuInstance;
struct VulkanContext { struct VulkanContext {
window: Arc<Window>, window: Arc<Window>,
@@ -165,23 +168,14 @@ impl VulkanContext {
let frag_shader = triangle::load_fragment(device.clone()) let frag_shader = triangle::load_fragment(device.clone())
.map_err(|e| format!("Failed to load fragment shader: {e}"))?; .map_err(|e| format!("Failed to load fragment shader: {e}"))?;
let framebuffers = Self::create_framebuffers( let framebuffers = Self::create_framebuffers(mem_allocator.clone(), &images, &render_pass)?;
mem_allocator.clone(),
&images,
&render_pass
)?;
let image_extents = swapchain.image_extent(); let image_extents = swapchain.image_extent();
// Create the camera. // Create the camera.
let camera = Camera::new(image_extents); let camera = Camera::new(image_extents);
let pipeline = Self::create_pipeline( let pipeline = Self::create_pipeline(&device, &vert_shader, &frag_shader, &render_pass)?;
&device,
&vert_shader,
&frag_shader,
&render_pass,
)?;
// Get descriptor set layout // Get descriptor set layout
let pipeline_layout = pipeline.layout().clone(); let pipeline_layout = pipeline.layout().clone();
@@ -191,17 +185,12 @@ impl VulkanContext {
let camera_resources = CameraResources::new( let camera_resources = CameraResources::new(
mem_allocator.clone(), mem_allocator.clone(),
desc_allocator.clone(), desc_allocator.clone(),
desc_set_layout desc_set_layout,
); );
let scene = Scene::from_file_json("./test_scene.scene"); let scene = Scene::from_file_json("./test_scene.scene");
let mesh_registry = Arc::new(MeshRegistry::from_scene( let mesh_registry = Arc::new(MeshRegistry::from_scene(&scene, mem_allocator.clone()));
&scene, let instance_registry = Arc::new(InstanceRegistry::from_scene(&scene));
mem_allocator.clone()
));
let instance_registry = Arc::new(InstanceRegistry::from_scene(
&scene
));
Ok(Self { Ok(Self {
window, window,
@@ -220,7 +209,7 @@ impl VulkanContext {
mesh_registry, mesh_registry,
instance_registry, instance_registry,
camera, camera,
camera_resources camera_resources,
}) })
} }
@@ -260,16 +249,13 @@ impl VulkanContext {
RenderBatch::build_batches( RenderBatch::build_batches(
self.mem_allocator.clone(), self.mem_allocator.clone(),
self.mesh_registry.clone(), self.mesh_registry.clone(),
self.instance_registry.clone() self.instance_registry.clone(),
) ),
)?; )?;
let future = sync::now(self.device.clone()) let future = sync::now(self.device.clone())
.join(acquire_future) .join(acquire_future)
.then_execute( .then_execute(self.queue.clone(), cmd_buffer.clone())?
self.queue.clone(),
cmd_buffer.clone(),
)?
.then_swapchain_present( .then_swapchain_present(
self.queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(self.swapchain.clone(), image_idx), SwapchainPresentInfo::swapchain_image_index(self.swapchain.clone(), image_idx),
@@ -339,19 +325,17 @@ impl VulkanContext {
images: &Vec<Arc<Image>>, images: &Vec<Arc<Image>>,
render_pass: &Arc<RenderPass>, render_pass: &Arc<RenderPass>,
) -> Result<Vec<Arc<Framebuffer>>, Box<dyn Error>> { ) -> Result<Vec<Arc<Framebuffer>>, Box<dyn Error>> {
let depth_buffer = ImageView::new_default( let depth_buffer = ImageView::new_default(Image::new(
Image::new( alloc.clone(),
alloc.clone(), ImageCreateInfo {
ImageCreateInfo { image_type: ImageType::Dim2d,
image_type: ImageType::Dim2d, format: Format::D16_UNORM,
format: Format::D16_UNORM, extent: images[0].extent(),
extent: images[0].extent(), usage: ImageUsage::DEPTH_STENCIL_ATTACHMENT | ImageUsage::TRANSIENT_ATTACHMENT,
usage: ImageUsage::DEPTH_STENCIL_ATTACHMENT | ImageUsage::TRANSIENT_ATTACHMENT, ..Default::default()
..Default::default() },
}, AllocationCreateInfo::default(),
AllocationCreateInfo::default() )?)?;
)?
)?;
let framebuffers = images let framebuffers = images
.iter() .iter()
@@ -388,8 +372,8 @@ impl VulkanContext {
.entry_point("main") .entry_point("main")
.ok_or("Failed to find fragment shader entry point")?; .ok_or("Failed to find fragment shader entry point")?;
let vertex_input_state = [Vertex3::per_vertex(), GpuInstance::per_instance()] let vertex_input_state =
.definition(&vs)?; [Vertex3::per_vertex(), GpuInstance::per_instance()].definition(&vs)?;
let stages = vec![ let stages = vec![
PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(vs),
@@ -453,15 +437,12 @@ impl VulkanContext {
queue.queue_family_index(), queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit, CommandBufferUsage::OneTimeSubmit,
) )
.expect("Couldn't create command buffer builder"); .expect("Couldn't create command buffer builder");
builder builder
.begin_render_pass( .begin_render_pass(
RenderPassBeginInfo { RenderPassBeginInfo {
clear_values: vec![ clear_values: vec![Some([0.1, 0.1, 0.1, 0.5].into()), Some(1f32.into())],
Some([0.1, 0.1, 0.1, 0.5].into()),
Some(1f32.into()),
],
..RenderPassBeginInfo::framebuffer(framebuffer.clone()) ..RenderPassBeginInfo::framebuffer(framebuffer.clone())
}, },
SubpassBeginInfo { SubpassBeginInfo {
@@ -469,10 +450,7 @@ impl VulkanContext {
..Default::default() ..Default::default()
}, },
)? )?
.set_viewport( .set_viewport(0, [camera.viewport()].into_iter().collect())?
0,
[camera.viewport()].into_iter().collect()
)?
.bind_descriptor_sets( .bind_descriptor_sets(
PipelineBindPoint::Graphics, PipelineBindPoint::Graphics,
pipeline.layout().clone(), pipeline.layout().clone(),
@@ -486,13 +464,20 @@ impl VulkanContext {
unsafe { unsafe {
render_batches.iter().for_each(|batch| { render_batches.iter().for_each(|batch| {
// Grab mesh information // Grab mesh information
let mesh = mesh_registry.get(&batch.mesh_id) let mesh = mesh_registry
.get(&batch.mesh_id)
.expect("Mesh does not exist in registry"); .expect("Mesh does not exist in registry");
builder builder
.bind_vertex_buffers(1, batch.instance_buffer.clone()) .bind_vertex_buffers(1, batch.instance_buffer.clone())
.unwrap() .unwrap()
.draw_indexed(mesh.index_count as u32, batch.instance_count, mesh.index_loc as u32, mesh.vertex_loc as i32, 0) .draw_indexed(
mesh.index_count as u32,
batch.instance_count,
mesh.index_loc as u32,
mesh.vertex_loc as i32,
0,
)
.unwrap(); .unwrap();
}); });
} }
@@ -524,12 +509,11 @@ impl VulkanContext {
self.swapchain_images = new_images; self.swapchain_images = new_images;
// Recreate the framebuffers, which depend on the swapchain. // Recreate the framebuffers, which depend on the swapchain.
let new_framebuffers = let new_framebuffers = Self::create_framebuffers(
Self::create_framebuffers( self.mem_allocator.clone(),
self.mem_allocator.clone(), &self.swapchain_images,
&self.swapchain_images, &self.render_pass,
&self.render_pass )?;
)?;
self.framebuffers = new_framebuffers; self.framebuffers = new_framebuffers;
@@ -541,7 +525,8 @@ impl VulkanContext {
&self.render_pass, &self.render_pass,
)?; )?;
self.camera.extents([image_extents.width, image_extents.height]); self.camera
.extents([image_extents.width, image_extents.height]);
self.pipeline = new_pipeline; self.pipeline = new_pipeline;
} }
+26 -30
View File
@@ -1,13 +1,13 @@
use crate::gpu::camera::CameraUniform;
use nalgebra::{Matrix4, Point3, Vector2, Vector3};
use std::f32::consts::PI; use std::f32::consts::PI;
use std::sync::Arc; use std::sync::Arc;
use nalgebra::{Matrix4, Point3, Vector2, Vector3};
use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}; use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer};
use vulkano::descriptor_set::allocator::DescriptorSetAllocator; use vulkano::descriptor_set::allocator::DescriptorSetAllocator;
use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet};
use vulkano::descriptor_set::layout::DescriptorSetLayout; use vulkano::descriptor_set::layout::DescriptorSetLayout;
use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet};
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter}; use vulkano::memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter};
use vulkano::pipeline::graphics::viewport::Viewport; use vulkano::pipeline::graphics::viewport::Viewport;
use crate::gpu::camera::CameraUniform;
/// A camera set within 3D space. /// A camera set within 3D space.
pub struct Camera { pub struct Camera {
@@ -28,9 +28,7 @@ pub struct Camera {
impl Camera { impl Camera {
/// Creates a new camera. This default camera is positioned at (1, 1, 1) and is looking at /// Creates a new camera. This default camera is positioned at (1, 1, 1) and is looking at
/// (0, 0, 0), and a default FOV of 70 degrees. /// (0, 0, 0), and a default FOV of 70 degrees.
pub fn new( pub fn new(extents: impl Into<Vector2<u32>>) -> Self {
extents: impl Into<Vector2<u32>>
) -> Self {
let extents = extents.into(); let extents = extents.into();
let position = Vector3::new(-7.0, 7.0, -7.0); let position = Vector3::new(-7.0, 7.0, -7.0);
let target = Vector3::zeros(); let target = Vector3::zeros();
@@ -84,7 +82,7 @@ impl Camera {
Matrix4::look_at_rh( Matrix4::look_at_rh(
&Point3::from(self.position), &Point3::from(self.position),
&Point3::from(self.forward - self.position), &Point3::from(self.forward - self.position),
&self.up &self.up,
) )
} }
@@ -92,12 +90,7 @@ impl Camera {
pub fn get_projection(&self) -> Matrix4<f32> { pub fn get_projection(&self) -> Matrix4<f32> {
let aspect = self.extents.x as f32 / self.extents.y as f32; let aspect = self.extents.x as f32 / self.extents.y as f32;
let mut proj = Matrix4::new_perspective( let mut proj = Matrix4::new_perspective(aspect, self.fov, 1.0, 10000.0);
aspect,
self.fov,
1.0,
10000.0
);
// Flip the y-axis, since Vulkan uses a different coordinate system. // Flip the y-axis, since Vulkan uses a different coordinate system.
proj[(1, 1)] *= -1.0; proj[(1, 1)] *= -1.0;
@@ -133,30 +126,34 @@ impl CameraResources {
desc_layout: Arc<DescriptorSetLayout>, desc_layout: Arc<DescriptorSetLayout>,
) -> Arc<Self> { ) -> Arc<Self> {
// Allocate the buffer // Allocate the buffer
let buffer = Arc::new(Buffer::new_sized::<CameraUniform>( let buffer = Arc::new(
alloc.clone(), Buffer::new_sized::<CameraUniform>(
BufferCreateInfo { alloc.clone(),
usage: BufferUsage::UNIFORM_BUFFER, BufferCreateInfo {
..Default::default() usage: BufferUsage::UNIFORM_BUFFER,
}, ..Default::default()
AllocationCreateInfo { },
memory_type_filter: MemoryTypeFilter::PREFER_HOST | AllocationCreateInfo {
MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, memory_type_filter: MemoryTypeFilter::PREFER_HOST
..Default::default() | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
} ..Default::default()
).expect("Couldn't allocate the camera uniform buffer")); },
)
.expect("Couldn't allocate the camera uniform buffer"),
);
// Create the descriptor set // Create the descriptor set
let descriptor_set = DescriptorSet::new( let descriptor_set = DescriptorSet::new(
desc_alloc.clone(), desc_alloc.clone(),
desc_layout, desc_layout,
[WriteDescriptorSet::buffer(0, buffer.as_ref().clone())], [WriteDescriptorSet::buffer(0, buffer.as_ref().clone())],
[] [],
).expect("Couldn't create camera descriptor set"); )
.expect("Couldn't create camera descriptor set");
Arc::new(Self { Arc::new(Self {
buffer, buffer,
descriptor_set descriptor_set,
}) })
} }
@@ -166,8 +163,7 @@ impl CameraResources {
let view = camera.get_view(); let view = camera.get_view();
let proj = camera.get_projection(); let proj = camera.get_projection();
let mut buffer_data = self.buffer.write() let mut buffer_data = self.buffer.write().unwrap();
.unwrap();
buffer_data.proj = proj.into(); buffer_data.proj = proj.into();
buffer_data.view = view.into(); buffer_data.view = view.into();
} }
+19 -12
View File
@@ -1,6 +1,6 @@
use std::collections::HashMap;
use crate::common::scene::{Scene, SceneInstance}; use crate::common::scene::{Scene, SceneInstance};
use nalgebra::{Matrix4, Vector3, Vector4}; use nalgebra::{Matrix4, Vector3, Vector4};
use std::collections::HashMap;
/// Contains all information about an instance of a mesh, specifically its translation, rotation, /// Contains all information about an instance of a mesh, specifically its translation, rotation,
/// and scale. Note that the rotation is a [`Vector4`], and not a `Quaternion`. /// and scale. Note that the rotation is a [`Vector4`], and not a `Quaternion`.
@@ -44,7 +44,13 @@ impl From<SceneInstance> for MeshInstance {
} }
impl From<&SceneInstance> for MeshInstance { impl From<&SceneInstance> for MeshInstance {
fn from(SceneInstance { translation, rotation, scale }: &SceneInstance) -> Self { fn from(
SceneInstance {
translation,
rotation,
scale,
}: &SceneInstance,
) -> Self {
Self { Self {
translation: Vector3::from(*translation).to_homogeneous(), translation: Vector3::from(*translation).to_homogeneous(),
rotation: Vector3::from(*rotation).to_homogeneous(), rotation: Vector3::from(*rotation).to_homogeneous(),
@@ -61,17 +67,18 @@ pub struct InstanceRegistry {
impl InstanceRegistry { impl InstanceRegistry {
pub fn from_scene(scene: &Scene) -> Self { pub fn from_scene(scene: &Scene) -> Self {
// Read information about the instances from the scene. // Read information about the instances from the scene.
let instances: HashMap<String, Vec<MeshInstance>> = scene.instances.iter() let instances: HashMap<String, Vec<MeshInstance>> =
.fold(HashMap::new(), |mut map, (mesh_id, instance)| { scene
map.entry(mesh_id.clone()) .instances
.and_modify(|v| v.push(instance.into())) .iter()
.or_insert(vec![instance.into()]); .fold(HashMap::new(), |mut map, (mesh_id, instance)| {
map map.entry(mesh_id.clone())
}); .and_modify(|v| v.push(instance.into()))
.or_insert(vec![instance.into()]);
map
});
Self { Self { instances }
instances
}
} }
pub fn get_instances_for(&self, mesh_id: &String) -> Vec<MeshInstance> { pub fn get_instances_for(&self, mesh_id: &String) -> Vec<MeshInstance> {
+2 -2
View File
@@ -1,13 +1,13 @@
use std::collections::HashMap; use std::collections::HashMap;
pub struct MaterialRegistry { pub struct MaterialRegistry {
materials: HashMap<String, Material> materials: HashMap<String, Material>,
} }
impl MaterialRegistry { impl MaterialRegistry {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
materials: HashMap::new() materials: HashMap::new(),
} }
} }
+11 -4
View File
@@ -1,11 +1,11 @@
use crate::common::scene::Scene; use crate::common::scene::Scene;
use crate::gpu::vertex3::Vertex3; use crate::gpu::vertex3::Vertex3;
use crate::loaders::mesh_loader::{MeshInfo, MeshLoader};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}; use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer};
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter}; use vulkano::memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter};
use wavefront::Obj; use wavefront::Obj;
use crate::loaders::mesh_loader::{MeshInfo, MeshLoader};
/// This struct is slightly misleading - it doesn't actually contain the mesh information, but /// This struct is slightly misleading - it doesn't actually contain the mesh information, but
/// the locations of the mesh's information in the overall application's vertex buffer, index buffer, /// the locations of the mesh's information in the overall application's vertex buffer, index buffer,
@@ -56,7 +56,9 @@ impl MeshRegistry {
.map(|(id, path)| { .map(|(id, path)| {
( (
id.clone(), id.clone(),
mesh_loader.load_mesh(path).expect("Couldn't read wavefront file"), mesh_loader
.load_mesh(path)
.expect("Couldn't read wavefront file"),
) )
}) })
.collect::<HashMap<String, MeshInfo>>(); .collect::<HashMap<String, MeshInfo>>();
@@ -71,7 +73,9 @@ impl MeshRegistry {
let all_verts = obj.vertices; let all_verts = obj.vertices;
let all_norms = obj.normals; let all_norms = obj.normals;
let obj_num_verts = all_verts.len(); let obj_num_verts = all_verts.len();
all_verts.iter().zip(all_norms.iter()) all_verts
.iter()
.zip(all_norms.iter())
.zip(obj.uvs.iter()) .zip(obj.uvs.iter())
.for_each(|((pos, norm), uv)| { .for_each(|((pos, norm), uv)| {
buf_verts.push(Vertex3::new(*pos, *norm, *uv)); buf_verts.push(Vertex3::new(*pos, *norm, *uv));
@@ -81,7 +85,10 @@ impl MeshRegistry {
let obj_num_idx = obj.indices.len(); let obj_num_idx = obj.indices.len();
buf_indices.extend(obj.indices); buf_indices.extend(obj.indices);
println!("Loaded mesh \"{}\" with {} vertices and {} indices", id, obj_num_verts, obj_num_idx); println!(
"Loaded mesh \"{}\" with {} vertices and {} indices",
id, obj_num_verts, obj_num_idx
);
// TODO Create bounding boxes for each mesh and associate them with the struct. // TODO Create bounding boxes for each mesh and associate them with the struct.
mesh_list.insert( mesh_list.insert(
+4 -4
View File
@@ -1,6 +1,6 @@
pub mod instance;
pub mod mesh;
pub mod scene;
pub mod render_batch;
pub mod camera; pub mod camera;
pub mod instance;
mod material; mod material;
pub mod mesh;
pub mod render_batch;
pub mod scene;
+17 -14
View File
@@ -1,9 +1,9 @@
use std::sync::Arc;
use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer};
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter};
use crate::common::instance::InstanceRegistry; use crate::common::instance::InstanceRegistry;
use crate::common::mesh::MeshRegistry; use crate::common::mesh::MeshRegistry;
use crate::gpu::instance::GpuInstance; use crate::gpu::instance::GpuInstance;
use std::sync::Arc;
use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer};
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct RenderBatch { pub struct RenderBatch {
@@ -18,9 +18,11 @@ impl RenderBatch {
pub fn build_batches( pub fn build_batches(
allocator: Arc<dyn MemoryAllocator>, allocator: Arc<dyn MemoryAllocator>,
mesh_registry: Arc<MeshRegistry>, mesh_registry: Arc<MeshRegistry>,
instance_registry: Arc<InstanceRegistry> instance_registry: Arc<InstanceRegistry>,
) -> Vec<Self> { ) -> Vec<Self> {
mesh_registry.meshes.iter() mesh_registry
.meshes
.iter()
.filter_map(|(id, mesh)| { .filter_map(|(id, mesh)| {
let instances = instance_registry.get_instances_for(id); let instances = instance_registry.get_instances_for(id);
@@ -29,11 +31,10 @@ impl RenderBatch {
return None; return None;
} }
let gpu_instances = instances.iter() let gpu_instances = instances
.iter()
.cloned() .cloned()
.map(|instance| { .map(|instance| instance.into())
instance.into()
})
.collect::<Vec<GpuInstance>>(); .collect::<Vec<GpuInstance>>();
// Build instances into a RenderBatch // Build instances into a RenderBatch
@@ -44,18 +45,20 @@ impl RenderBatch {
..Default::default() ..Default::default()
}, },
AllocationCreateInfo { AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE | memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default() ..Default::default()
}, },
gpu_instances gpu_instances,
).expect("Couldn't allocate instance buffer"); )
.expect("Couldn't allocate instance buffer");
Some(Self { Some(Self {
mesh_id: id.clone(), mesh_id: id.clone(),
instance_buffer, instance_buffer,
instance_count: instances.len() as u32, instance_count: instances.len() as u32,
}) })
}).collect() })
.collect()
} }
} }
+8 -9
View File
@@ -89,24 +89,23 @@ pub struct SceneInstance {
#[cfg(test)] #[cfg(test)]
pub mod create_files { pub mod create_files {
pub use super::*;
use std::error::Error; use std::error::Error;
use std::io::Write; use std::io::Write;
pub use super::*;
#[test] #[test]
pub fn create_test_scene() -> Result<(), Box<dyn Error>> { pub fn create_test_scene() -> Result<(), Box<dyn Error>> {
let scene = Scene { let scene = Scene {
name: "Testing Scene".to_string(), name: "Testing Scene".to_string(),
mesh_paths: HashMap::from([ mesh_paths: HashMap::from([("venator".to_string(), "Venator.obj".to_string())]),
("venator".to_string(), "Venator.obj".to_string()) instances: HashMap::from([(
]), "venator".to_string(),
instances: HashMap::from([ SceneInstance {
("venator".to_string(), SceneInstance {
translation: [0.0, 0.0, 0.0], translation: [0.0, 0.0, 0.0],
rotation: [0.0, 0.0, 0.0], rotation: [0.0, 0.0, 0.0],
scale: [1.0, 1.0, 1.0] scale: [1.0, 1.0, 1.0],
}) },
]) )]),
}; };
let json = serde_json::to_string_pretty(&scene)?; let json = serde_json::to_string_pretty(&scene)?;
+1 -1
View File
@@ -1,5 +1,5 @@
use vulkano::buffer::BufferContents;
use crate::common::camera::Camera; use crate::common::camera::Camera;
use vulkano::buffer::BufferContents;
#[derive(BufferContents)] #[derive(BufferContents)]
#[repr(C)] #[repr(C)]
+1 -1
View File
@@ -1,3 +1,3 @@
pub mod camera;
pub mod instance; pub mod instance;
pub mod vertex3; pub mod vertex3;
pub mod camera;
+2 -6
View File
@@ -13,15 +13,11 @@ pub struct Vertex3 {
} }
impl Vertex3 { impl Vertex3 {
pub fn new( pub fn new(position: [f32; 3], normal: [f32; 3], uv: [f32; 3]) -> Self {
position: [f32; 3],
normal: [f32; 3],
uv: [f32; 3],
) -> Self {
Self { Self {
position, position,
normal, normal,
uv uv,
} }
} }
} }
+3 -3
View File
@@ -1,6 +1,6 @@
use std::path::Path;
use anyhow::Result;
use crate::loaders::mesh_loader::{MeshFileLoader, MeshInfo}; use crate::loaders::mesh_loader::{MeshFileLoader, MeshInfo};
use anyhow::Result;
use std::path::Path;
pub struct GltfLoader; pub struct GltfLoader;
@@ -51,7 +51,7 @@ impl MeshFileLoader for GltfLoader {
vertices, vertices,
indices, indices,
normals, normals,
uvs uvs,
}) })
} }
} }
+19 -11
View File
@@ -1,9 +1,9 @@
use super::gltf_loader::*;
use crate::loaders::obj_loader::ObjLoader;
use anyhow::Result;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path; use std::path::Path;
use anyhow::Result;
use thiserror::Error; use thiserror::Error;
use crate::loaders::obj_loader::ObjLoader;
use super::gltf_loader::*;
pub struct MeshInfo { pub struct MeshInfo {
/// The mesh's vertices. /// The mesh's vertices.
@@ -21,7 +21,7 @@ pub trait MeshFileLoader {
} }
pub struct MeshLoader { pub struct MeshLoader {
loaders: HashMap<String, Box<dyn MeshFileLoader>> loaders: HashMap<String, Box<dyn MeshFileLoader>>,
} }
#[derive(Error, Debug)] #[derive(Error, Debug)]
@@ -29,29 +29,37 @@ pub enum MeshLoaderError {
#[error("file extension is missing")] #[error("file extension is missing")]
MissingExtension, MissingExtension,
#[error("extension {0} is not supported")] #[error("extension {0} is not supported")]
ExtensionNotSupported(String) ExtensionNotSupported(String),
} }
impl MeshLoader { impl MeshLoader {
pub fn new() -> Self { pub fn new() -> Self {
let mut loaders = HashMap::new(); let mut loaders = HashMap::new();
loaders.insert("gltf".into(), Box::new(GltfLoader) as Box<dyn MeshFileLoader>); loaders.insert(
"gltf".into(),
Box::new(GltfLoader) as Box<dyn MeshFileLoader>,
);
loaders.insert(
"glb".into(),
Box::new(GltfLoader) as Box<dyn MeshFileLoader>,
);
loaders.insert("obj".into(), Box::new(ObjLoader)); loaders.insert("obj".into(), Box::new(ObjLoader));
Self { Self { loaders }
loaders
}
} }
/// Loads a mesh and returns a `Result` containing the information. /// Loads a mesh and returns a `Result` containing the information.
pub fn load_mesh<P: AsRef<Path>>(&self, path: &P) -> Result<MeshInfo> { pub fn load_mesh<P: AsRef<Path>>(&self, path: &P) -> Result<MeshInfo> {
let ext = path.as_ref().extension() let ext = path
.as_ref()
.extension()
.and_then(|ext| ext.to_str()) .and_then(|ext| ext.to_str())
.ok_or(MeshLoaderError::MissingExtension)?; .ok_or(MeshLoaderError::MissingExtension)?;
println!("Found extension {}, continuing with loading", ext); println!("Found extension {}, continuing with loading", ext);
self.loaders.get(ext) self.loaders
.get(ext)
.ok_or(MeshLoaderError::ExtensionNotSupported(ext.to_string()))? .ok_or(MeshLoaderError::ExtensionNotSupported(ext.to_string()))?
.load_mesh(path.as_ref()) .load_mesh(path.as_ref())
} }
+4 -5
View File
@@ -1,14 +1,13 @@
use std::path::Path;
use wavefront::Obj;
use crate::gpu::vertex3::Vertex3; use crate::gpu::vertex3::Vertex3;
use crate::loaders::mesh_loader::{MeshFileLoader, MeshInfo}; use crate::loaders::mesh_loader::{MeshFileLoader, MeshInfo};
use std::path::Path;
use wavefront::Obj;
pub struct ObjLoader; pub struct ObjLoader;
impl MeshFileLoader for ObjLoader { impl MeshFileLoader for ObjLoader {
fn load_mesh(&self, path: &Path) -> anyhow::Result<MeshInfo> { fn load_mesh(&self, path: &Path) -> anyhow::Result<MeshInfo> {
let obj_info = Obj::from_file(path) let obj_info = Obj::from_file(path).expect("Couldn't read wavefront file");
.expect("Couldn't read wavefront file");
let index_list = obj_info let index_list = obj_info
.triangles() .triangles()
@@ -23,7 +22,7 @@ impl MeshFileLoader for ObjLoader {
vertices: obj_info.positions().to_vec(), vertices: obj_info.positions().to_vec(),
normals: obj_info.normals().to_vec(), normals: obj_info.normals().to_vec(),
indices: index_list, indices: index_list,
uvs: obj_info.uvs().to_vec() uvs: obj_info.uvs().to_vec(),
}) })
} }
} }
+25 -23
View File
@@ -1,26 +1,28 @@
{ {
"name": "Testing Scene", "name": "Testing Scene",
"mesh_paths": { "mesh_paths": {
"gunship": "meshes/laat_gunship/scene.obj", "gunship": "meshes/laat_gunship/scene.obj",
"trooper": "meshes/clone_trooper/scene.gltf" "trooper": "meshes/clone_trooper/scene.gltf",
}, "destroyer": "../meshes/sw_isd.glb",
"instances": { "interceptor": "../meshes/sw_tie_interceptor.glb"
"trooper": { },
"translation": [ "instances": {
0.0, "interceptor": {
0.0, "translation": [
0.0 0.0,
], 0.0,
"rotation": [ 0.0
0.0, ],
0.0, "rotation": [
0.0 0.0,
], 0.0,
"scale": [ 0.0
1.0, ],
1.0, "scale": [
1.0 1.0,
] 1.0,
1.0
]
}
} }
}
} }