feat: implement a more modular graphics pipeline
This commit is contained in:
1 parent
6fe8ff2af0
commit
8b76fdb6ae
15 files changed
+498
-85
No files matched your search
Generated
+1
@@ -2627,6 +2627,7 @@ dependencies = [
|
|||||||
"nalgebra",
|
"nalgebra",
|
||||||
"postcard",
|
"postcard",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"uuid",
|
"uuid",
|
||||||
"vulkano",
|
"vulkano",
|
||||||
"vulkano-shaders",
|
"vulkano-shaders",
|
||||||
|
|||||||
@@ -13,3 +13,4 @@ winit = "0.30.13"
|
|||||||
nalgebra = "*"
|
nalgebra = "*"
|
||||||
uuid = { version = "1.23.1", features = ["v4"] }
|
uuid = { version = "1.23.1", features = ["v4"] }
|
||||||
postcard = { version = "1.1", features = ["alloc"] }
|
postcard = { version = "1.1", features = ["alloc"] }
|
||||||
|
serde_json = "1.0.149"
|
||||||
+89
-59
@@ -1,3 +1,4 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
use crate::common::mesh::MeshRegistry;
|
use crate::common::mesh::MeshRegistry;
|
||||||
use crate::gpu::vertex3::Vertex3;
|
use crate::gpu::vertex3::Vertex3;
|
||||||
use crate::triangle;
|
use crate::triangle;
|
||||||
@@ -30,16 +31,23 @@ 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::{GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo};
|
use vulkano::pipeline::{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::descriptor_set::DescriptorSet;
|
||||||
|
use vulkano::descriptor_set::layout::DescriptorSetLayout;
|
||||||
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>,
|
||||||
@@ -57,6 +65,9 @@ struct VulkanContext {
|
|||||||
pipeline: Arc<GraphicsPipeline>,
|
pipeline: Arc<GraphicsPipeline>,
|
||||||
cmd_buffers: Vec<Arc<PrimaryAutoCommandBuffer>>,
|
cmd_buffers: Vec<Arc<PrimaryAutoCommandBuffer>>,
|
||||||
mesh_registry: Arc<MeshRegistry>,
|
mesh_registry: Arc<MeshRegistry>,
|
||||||
|
instance_registry: Arc<InstanceRegistry>,
|
||||||
|
camera: Arc<Camera>,
|
||||||
|
camera_resources: Arc<CameraResources>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VulkanContext {
|
impl VulkanContext {
|
||||||
@@ -156,6 +167,10 @@ impl VulkanContext {
|
|||||||
let framebuffers = Self::create_framebuffers(&images, &render_pass)?;
|
let framebuffers = Self::create_framebuffers(&images, &render_pass)?;
|
||||||
|
|
||||||
let image_extents = swapchain.image_extent();
|
let image_extents = swapchain.image_extent();
|
||||||
|
|
||||||
|
// Create the camera.
|
||||||
|
let camera = Arc::new(Camera::new(image_extents));
|
||||||
|
|
||||||
let pipeline = Self::create_pipeline(
|
let pipeline = Self::create_pipeline(
|
||||||
&device,
|
&device,
|
||||||
&vert_shader,
|
&vert_shader,
|
||||||
@@ -164,58 +179,43 @@ impl VulkanContext {
|
|||||||
[image_extents[0] as f32, image_extents[1] as f32],
|
[image_extents[0] as f32, image_extents[1] as f32],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let vertex_buffer = Arc::new(Buffer::from_iter(
|
// Get descriptor set layout
|
||||||
mem_allocator.clone(),
|
let pipeline_layout = pipeline.layout().clone();
|
||||||
BufferCreateInfo {
|
let desc_set_layout = pipeline_layout.set_layouts()[0].clone();
|
||||||
usage: BufferUsage::VERTEX_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_HOST
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
vec![
|
|
||||||
Vertex3 {
|
|
||||||
position: [0.5, 0.5, 0.0],
|
|
||||||
},
|
|
||||||
Vertex3 {
|
|
||||||
position: [-0.5, 0.5, 0.0],
|
|
||||||
},
|
|
||||||
Vertex3 {
|
|
||||||
position: [-0.5, -0.5, 0.0],
|
|
||||||
},
|
|
||||||
Vertex3 {
|
|
||||||
position: [0.5, -0.5, 0.0],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
)?);
|
|
||||||
|
|
||||||
let index_buffer = Arc::new(Buffer::from_iter(
|
// Create GPU-based camera resources
|
||||||
|
let camera_resources = CameraResources::new(
|
||||||
mem_allocator.clone(),
|
mem_allocator.clone(),
|
||||||
BufferCreateInfo {
|
desc_allocator.clone(),
|
||||||
usage: BufferUsage::INDEX_BUFFER,
|
desc_set_layout
|
||||||
..Default::default()
|
);
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
let scene = Scene::from_file_json("./test_scene.scene");
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_HOST
|
let mesh_registry = Arc::new(MeshRegistry::from_scene(
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
&scene,
|
||||||
..Default::default()
|
mem_allocator.clone()
|
||||||
},
|
));
|
||||||
vec![0, 1, 2, 0, 2, 3],
|
let instance_registry = Arc::new(InstanceRegistry::from_scene(
|
||||||
)?);
|
&scene
|
||||||
|
));
|
||||||
|
|
||||||
|
// Construct render batches each frame, starting with once here.
|
||||||
|
let batches = RenderBatch::build_batches(
|
||||||
|
mem_allocator.clone(),
|
||||||
|
mesh_registry.clone(),
|
||||||
|
instance_registry.clone()
|
||||||
|
);
|
||||||
|
|
||||||
let cmd_buffers = Self::get_command_buffers(
|
let cmd_buffers = Self::get_command_buffers(
|
||||||
&cmd_allocator,
|
&cmd_allocator,
|
||||||
&queue,
|
&queue,
|
||||||
&pipeline,
|
&pipeline,
|
||||||
&framebuffers,
|
&framebuffers,
|
||||||
&vertex_buffer,
|
camera_resources.descriptor_set(),
|
||||||
&index_buffer,
|
mesh_registry.clone(),
|
||||||
|
batches,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mesh_registry = Arc::new(MeshRegistry::new(mem_allocator.clone())?);
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
window,
|
window,
|
||||||
device,
|
device,
|
||||||
@@ -232,6 +232,9 @@ impl VulkanContext {
|
|||||||
pipeline,
|
pipeline,
|
||||||
cmd_buffers,
|
cmd_buffers,
|
||||||
mesh_registry,
|
mesh_registry,
|
||||||
|
instance_registry,
|
||||||
|
camera,
|
||||||
|
camera_resources
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,7 +367,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().definition(&vs)?;
|
let vertex_input_state = [Vertex3::per_vertex(), GpuInstance::per_instance()]
|
||||||
|
.definition(&vs)?;
|
||||||
|
|
||||||
let stages = vec![
|
let stages = vec![
|
||||||
PipelineShaderStageCreateInfo::new(vs),
|
PipelineShaderStageCreateInfo::new(vs),
|
||||||
@@ -417,8 +421,9 @@ impl VulkanContext {
|
|||||||
queue: &Arc<Queue>,
|
queue: &Arc<Queue>,
|
||||||
pipeline: &Arc<GraphicsPipeline>,
|
pipeline: &Arc<GraphicsPipeline>,
|
||||||
framebuffers: &Vec<Arc<Framebuffer>>,
|
framebuffers: &Vec<Arc<Framebuffer>>,
|
||||||
vertex_buffer: &Arc<Subbuffer<[Vertex3]>>,
|
camera_desc_set: Arc<DescriptorSet>,
|
||||||
index_buffer: &Subbuffer<[u32]>,
|
mesh_registry: Arc<MeshRegistry>,
|
||||||
|
render_batches: Vec<RenderBatch>,
|
||||||
) -> Result<Vec<Arc<PrimaryAutoCommandBuffer>>, Box<dyn Error>> {
|
) -> Result<Vec<Arc<PrimaryAutoCommandBuffer>>, Box<dyn Error>> {
|
||||||
let buffers = framebuffers
|
let buffers = framebuffers
|
||||||
.iter()
|
.iter()
|
||||||
@@ -428,7 +433,7 @@ impl VulkanContext {
|
|||||||
queue.queue_family_index(),
|
queue.queue_family_index(),
|
||||||
CommandBufferUsage::MultipleSubmit,
|
CommandBufferUsage::MultipleSubmit,
|
||||||
)
|
)
|
||||||
.ok()?;
|
.expect("Couldn't create command buffer builder");
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
builder
|
builder
|
||||||
@@ -442,18 +447,36 @@ impl VulkanContext {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.ok()?
|
.unwrap()
|
||||||
|
.bind_descriptor_sets(
|
||||||
|
PipelineBindPoint::Graphics,
|
||||||
|
pipeline.layout().clone(),
|
||||||
|
0,
|
||||||
|
camera_desc_set.clone(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
.bind_pipeline_graphics(pipeline.clone())
|
.bind_pipeline_graphics(pipeline.clone())
|
||||||
.ok()?
|
.unwrap()
|
||||||
.bind_vertex_buffers(0, vertex_buffer.as_ref().clone())
|
.bind_vertex_buffers(0, mesh_registry.vertex_buffer.as_ref().clone())
|
||||||
.ok()?
|
.unwrap()
|
||||||
.bind_index_buffer(index_buffer.clone())
|
.bind_index_buffer(mesh_registry.index_buffer.as_ref().clone())
|
||||||
.ok()?
|
.unwrap();
|
||||||
// .draw(vertex_buffer.len() as u32, 1, 0, 0).ok()?
|
|
||||||
.draw_indexed(index_buffer.len() as u32, 1, 0, 0, 0)
|
render_batches.iter().for_each(|batch| {
|
||||||
.ok()?
|
// Grab mesh information
|
||||||
|
let mesh = mesh_registry.get(&batch.mesh_id)
|
||||||
|
.expect("Mesh does not exist in registry");
|
||||||
|
|
||||||
|
builder
|
||||||
|
.bind_vertex_buffers(1, batch.instance_buffer.clone())
|
||||||
|
.unwrap()
|
||||||
|
.draw_indexed(mesh.index_count as u32, batch.instance_count, mesh.index_loc as u32, mesh.vertex_loc as i32, 0)
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
builder
|
||||||
.end_render_pass(SubpassEndInfo::default())
|
.end_render_pass(SubpassEndInfo::default())
|
||||||
.ok()?;
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.build().ok()
|
builder.build().ok()
|
||||||
@@ -496,13 +519,20 @@ impl VulkanContext {
|
|||||||
[image_extents[0] as f32, image_extents[1] as f32],
|
[image_extents[0] as f32, image_extents[1] as f32],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let batches = RenderBatch::build_batches(
|
||||||
|
self.mem_allocator.clone(),
|
||||||
|
self.mesh_registry.clone(),
|
||||||
|
self.instance_registry.clone()
|
||||||
|
);
|
||||||
|
|
||||||
let command_buffers = Self::get_command_buffers(
|
let command_buffers = Self::get_command_buffers(
|
||||||
&self.cmd_allocator,
|
&self.cmd_allocator,
|
||||||
&self.queue,
|
&self.queue,
|
||||||
&new_pipeline,
|
&new_pipeline,
|
||||||
&self.framebuffers,
|
&self.framebuffers,
|
||||||
&self.mesh_registry.vertex_buffer,
|
self.camera_resources.descriptor_set(),
|
||||||
&self.mesh_registry.index_buffer,
|
self.mesh_registry.clone(),
|
||||||
|
batches
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.pipeline = new_pipeline;
|
self.pipeline = new_pipeline;
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
use std::f32::consts::PI;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use nalgebra::{Matrix4, Point3, Vector2, Vector3};
|
||||||
|
use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer};
|
||||||
|
use vulkano::descriptor_set::allocator::DescriptorSetAllocator;
|
||||||
|
use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet};
|
||||||
|
use vulkano::descriptor_set::layout::DescriptorSetLayout;
|
||||||
|
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter};
|
||||||
|
use crate::gpu::camera::CameraUniform;
|
||||||
|
|
||||||
|
/// A camera set within 3D space.
|
||||||
|
pub struct Camera {
|
||||||
|
/// The position of the camera.
|
||||||
|
position: Vector3<f32>,
|
||||||
|
/// The forward direction of the camera.
|
||||||
|
forward: Vector3<f32>,
|
||||||
|
/// The upward direction of the camera.
|
||||||
|
up: Vector3<f32>,
|
||||||
|
/// The rightward direction of the camera.
|
||||||
|
right: Vector3<f32>,
|
||||||
|
/// The field of view, in radians.
|
||||||
|
fov: f32,
|
||||||
|
/// The extents of the viewport.
|
||||||
|
extents: Vector2<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Camera {
|
||||||
|
/// 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.
|
||||||
|
pub fn new(
|
||||||
|
extents: impl Into<Vector2<u32>>
|
||||||
|
) -> Self {
|
||||||
|
let extents = extents.into();
|
||||||
|
let position = Vector3::new(1.0, 1.0, 1.0);
|
||||||
|
let target = Vector3::zeros();
|
||||||
|
|
||||||
|
// Traveling along this vector moves us upward.
|
||||||
|
let up = Vector3::new(0.0, 1.0, 0.0);
|
||||||
|
|
||||||
|
// Traveling along this vector moves us to the right.
|
||||||
|
let right = up.cross(&(position - target)).normalize();
|
||||||
|
|
||||||
|
// Traveling along this vector moves us toward the target.
|
||||||
|
let forward = target - position;
|
||||||
|
|
||||||
|
Self {
|
||||||
|
position,
|
||||||
|
forward,
|
||||||
|
up,
|
||||||
|
right,
|
||||||
|
fov: 70.0 * PI / 180.0,
|
||||||
|
extents,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves the camera forward, or backward if given negative speed.
|
||||||
|
pub fn move_forward(&mut self, speed: f32) {
|
||||||
|
self.forward += self.forward * speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves the camera to the right, or left if given negative speed.
|
||||||
|
pub fn move_right(&mut self, speed: f32) {
|
||||||
|
self.right += self.right * speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves the camera upwards, or downward if given negative speed.
|
||||||
|
pub fn move_up(&mut self, speed: f32) {
|
||||||
|
self.up += self.up * speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the extents of the viewport.
|
||||||
|
pub fn extents(&mut self, extents: impl Into<Vector2<u32>>) {
|
||||||
|
let extents = extents.into();
|
||||||
|
|
||||||
|
self.extents = extents;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the current view matrix of the camera.
|
||||||
|
pub fn get_view(&self) -> Matrix4<f32> {
|
||||||
|
Matrix4::look_at_rh(
|
||||||
|
&Point3::from(self.position),
|
||||||
|
&Point3::from(self.forward - self.position),
|
||||||
|
&self.up
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a perspective matrix based on the current viewport aspect ratio.
|
||||||
|
pub fn get_projection(&self) -> Matrix4<f32> {
|
||||||
|
let aspect = self.extents.x as f32 / self.extents.y as f32;
|
||||||
|
|
||||||
|
Matrix4::new_perspective(
|
||||||
|
aspect,
|
||||||
|
self.fov,
|
||||||
|
1.0,
|
||||||
|
10000.0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An intermediate struct that maintains the buffer and descriptor set for the [`Camera`] struct.
|
||||||
|
/// The reason this information is not stored as part of the `Camera` itself is because tying
|
||||||
|
/// the GPU or rendering resources to the camera itself is quite awkward.
|
||||||
|
pub struct CameraResources {
|
||||||
|
buffer: Arc<Subbuffer<CameraUniform>>,
|
||||||
|
descriptor_set: Arc<DescriptorSet>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CameraResources {
|
||||||
|
/// Creates a new set of `CameraResources`. Automatically allocates a uniform buffer to store
|
||||||
|
/// [`CameraUniform`] information.
|
||||||
|
pub fn new(
|
||||||
|
alloc: Arc<dyn MemoryAllocator>,
|
||||||
|
desc_alloc: Arc<dyn DescriptorSetAllocator>,
|
||||||
|
desc_layout: Arc<DescriptorSetLayout>,
|
||||||
|
) -> Arc<Self> {
|
||||||
|
// Allocate the buffer
|
||||||
|
let buffer = Arc::new(Buffer::new_sized::<CameraUniform>(
|
||||||
|
alloc.clone(),
|
||||||
|
BufferCreateInfo {
|
||||||
|
usage: BufferUsage::UNIFORM_BUFFER,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo {
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_HOST |
|
||||||
|
MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
).expect("Couldn't allocate the camera uniform buffer"));
|
||||||
|
|
||||||
|
// Create the descriptor set
|
||||||
|
let descriptor_set = DescriptorSet::new(
|
||||||
|
desc_alloc.clone(),
|
||||||
|
desc_layout,
|
||||||
|
[WriteDescriptorSet::buffer(0, buffer.as_ref().clone())],
|
||||||
|
[]
|
||||||
|
).expect("Couldn't create camera descriptor set");
|
||||||
|
|
||||||
|
Arc::new(Self {
|
||||||
|
buffer,
|
||||||
|
descriptor_set
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the buffer with the given [`Camera`] information.
|
||||||
|
pub fn update(&self, camera: &Camera) {
|
||||||
|
// Update the buffer with the new information from the camera.
|
||||||
|
let view = camera.get_view();
|
||||||
|
let proj = camera.get_projection();
|
||||||
|
|
||||||
|
let mut buffer_data = self.buffer.write()
|
||||||
|
.unwrap();
|
||||||
|
buffer_data.proj = proj.into();
|
||||||
|
buffer_data.view = view.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to the descriptor set.
|
||||||
|
pub fn descriptor_set(&self) -> Arc<DescriptorSet> {
|
||||||
|
self.descriptor_set.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
-16
@@ -1,4 +1,5 @@
|
|||||||
use crate::common::scene::SceneInstance;
|
use std::collections::HashMap;
|
||||||
|
use crate::common::scene::{Scene, SceneInstance};
|
||||||
use nalgebra::{Matrix4, Vector3, Vector4};
|
use nalgebra::{Matrix4, Vector3, Vector4};
|
||||||
|
|
||||||
/// 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,
|
||||||
@@ -9,9 +10,8 @@ use nalgebra::{Matrix4, Vector3, Vector4};
|
|||||||
///
|
///
|
||||||
/// Before passing this instance into the shaders, construct a transformation matrix by calling
|
/// Before passing this instance into the shaders, construct a transformation matrix by calling
|
||||||
/// [`MeshInstance::transform_matrix`].
|
/// [`MeshInstance::transform_matrix`].
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct MeshInstance {
|
pub struct MeshInstance {
|
||||||
/// The ID of the mesh corresponding to this instance.
|
|
||||||
pub mesh_id: String,
|
|
||||||
/// The translation of the instance from the center of the standard space. It is possible that
|
/// The translation of the instance from the center of the standard space. It is possible that
|
||||||
/// local translation will be required in the future.
|
/// local translation will be required in the future.
|
||||||
pub translation: Vector4<f32>,
|
pub translation: Vector4<f32>,
|
||||||
@@ -35,21 +35,47 @@ impl MeshInstance {
|
|||||||
Matrix4::from_axis_angle(&Vector3::z_axis(), self.rotation.z) *
|
Matrix4::from_axis_angle(&Vector3::z_axis(), self.rotation.z) *
|
||||||
Matrix4::new_nonuniform_scaling(&self.scale)
|
Matrix4::new_nonuniform_scaling(&self.scale)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Constructs a `MeshInstance` from the given [`SceneInstance`] and mesh ID.
|
impl From<SceneInstance> for MeshInstance {
|
||||||
fn from_scene_instance(
|
fn from(instance: SceneInstance) -> Self {
|
||||||
mesh_id: String,
|
Self::from(&instance)
|
||||||
SceneInstance {
|
}
|
||||||
translation,
|
}
|
||||||
rotation,
|
|
||||||
scale,
|
impl From<&SceneInstance> for MeshInstance {
|
||||||
}: SceneInstance,
|
fn from(SceneInstance { translation, rotation, scale }: &SceneInstance) -> Self {
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
mesh_id,
|
translation: Vector3::from(*translation).to_homogeneous(),
|
||||||
translation: Vector3::from(translation).to_homogeneous(),
|
rotation: Vector3::from(*rotation).to_homogeneous(),
|
||||||
rotation: Vector3::from(rotation).to_homogeneous(),
|
scale: Vector3::from(*scale),
|
||||||
scale: Vector3::from(scale),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A registry containing a series of `MeshInstance` structs linked to a mesh ID.
|
||||||
|
pub struct InstanceRegistry {
|
||||||
|
instances: HashMap<String, Vec<MeshInstance>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstanceRegistry {
|
||||||
|
pub fn from_scene(scene: &Scene) -> Self {
|
||||||
|
// Read information about the instances from the scene.
|
||||||
|
let instances: HashMap<String, Vec<MeshInstance>> = scene.instances.iter()
|
||||||
|
.fold(HashMap::new(), |mut map, (mesh_id, instance)| {
|
||||||
|
map.entry(mesh_id.clone())
|
||||||
|
.and_modify(|v| v.push(instance.into()))
|
||||||
|
.or_insert(vec![instance.into()]);
|
||||||
|
map
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
instances
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_instances_for(&self, mesh_id: &String) -> Vec<MeshInstance> {
|
||||||
|
// We can't move these instances because we'll want them again later.
|
||||||
|
self.instances.get(mesh_id).cloned().unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-3
@@ -32,7 +32,7 @@ impl Mesh {}
|
|||||||
/// used by the rendering pipeline.
|
/// used by the rendering pipeline.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MeshRegistry {
|
pub struct MeshRegistry {
|
||||||
meshes: HashMap<String, Mesh>,
|
pub meshes: HashMap<String, Mesh>,
|
||||||
allocator: Arc<dyn MemoryAllocator>,
|
allocator: Arc<dyn MemoryAllocator>,
|
||||||
pub vertex_buffer: Arc<Subbuffer<[Vertex3]>>,
|
pub vertex_buffer: Arc<Subbuffer<[Vertex3]>>,
|
||||||
pub index_buffer: Arc<Subbuffer<[u32]>>,
|
pub index_buffer: Arc<Subbuffer<[u32]>>,
|
||||||
@@ -67,7 +67,7 @@ impl MeshRegistry {
|
|||||||
let vert_start = verts.len();
|
let vert_start = verts.len();
|
||||||
let all_verts = obj.vertices();
|
let all_verts = obj.vertices();
|
||||||
let vert_len = all_verts.len();
|
let vert_len = all_verts.len();
|
||||||
verts.extend(all_verts.map(|v| Vertex3::from(v.position())));
|
verts.extend(all_verts.map(|v| Vertex3::new(v.position(), v.normal().unwrap_or_default())));
|
||||||
|
|
||||||
let index_list = obj
|
let index_list = obj
|
||||||
.triangles()
|
.triangles()
|
||||||
@@ -156,7 +156,7 @@ impl MeshRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to find a [`Mesh`] with the given ID, returning `None` if it's not found.
|
/// Attempts to find a [`Mesh`] with the given ID, returning `None` if it's not found.
|
||||||
pub fn get(&self, uuid: &str) -> Option<&Mesh> {
|
pub fn get(&self, uuid: &String) -> Option<&Mesh> {
|
||||||
self.meshes.get(uuid)
|
self.meshes.get(uuid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
pub mod instance;
|
pub mod instance;
|
||||||
pub mod mesh;
|
pub mod mesh;
|
||||||
pub mod scene;
|
pub mod scene;
|
||||||
|
pub mod render_batch;
|
||||||
|
pub mod camera;
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
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::mesh::MeshRegistry;
|
||||||
|
use crate::gpu::instance::GpuInstance;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct RenderBatch {
|
||||||
|
pub mesh_id: String,
|
||||||
|
pub instance_buffer: Subbuffer<[GpuInstance]>,
|
||||||
|
pub instance_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderBatch {
|
||||||
|
/// Builds a `Vec<RenderBatch>` from the given list of instances and meshes. This should be done
|
||||||
|
/// once per frame.
|
||||||
|
pub fn build_batches(
|
||||||
|
allocator: Arc<dyn MemoryAllocator>,
|
||||||
|
mesh_registry: Arc<MeshRegistry>,
|
||||||
|
instance_registry: Arc<InstanceRegistry>
|
||||||
|
) -> Vec<Self> {
|
||||||
|
mesh_registry.meshes.iter()
|
||||||
|
.map(|(id, mesh)| {
|
||||||
|
let instances = instance_registry.get_instances_for(id);
|
||||||
|
|
||||||
|
let gpu_instances = instances.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|instance| {
|
||||||
|
instance.into()
|
||||||
|
})
|
||||||
|
.collect::<Vec<GpuInstance>>();
|
||||||
|
|
||||||
|
// Build instances into a RenderBatch
|
||||||
|
let instance_buffer = Buffer::from_iter(
|
||||||
|
allocator.clone(),
|
||||||
|
BufferCreateInfo {
|
||||||
|
usage: BufferUsage::VERTEX_BUFFER,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo {
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE |
|
||||||
|
MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
gpu_instances
|
||||||
|
).expect("Couldn't allocate instance buffer");
|
||||||
|
|
||||||
|
Self {
|
||||||
|
mesh_id: id.clone(),
|
||||||
|
instance_buffer,
|
||||||
|
instance_count: instances.len() as u32,
|
||||||
|
}
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
+46
-2
@@ -42,10 +42,23 @@ impl Scene {
|
|||||||
let mut contents = Vec::new();
|
let mut contents = Vec::new();
|
||||||
let _ = file.read_to_end(&mut contents).unwrap();
|
let _ = file.read_to_end(&mut contents).unwrap();
|
||||||
|
|
||||||
let mut scene: Self =
|
let scene: Self =
|
||||||
postcard::from_bytes(contents.as_slice()).expect("Couldn't deserialize scene");
|
postcard::from_bytes(contents.as_slice()).expect("Couldn't deserialize scene");
|
||||||
|
|
||||||
let base = path.parent().unwrap_or(&Path::new("."));
|
Self::resolve_paths(path, scene)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_file_json<P: AsRef<Path>>(path: P) -> Self {
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
let file = std::fs::File::open(path).unwrap();
|
||||||
|
let scene: Self = serde_json::from_reader(file).unwrap();
|
||||||
|
|
||||||
|
Self::resolve_paths(path, scene)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_paths(scene_path: &Path, mut scene: Self) -> Self {
|
||||||
|
let base = scene_path.parent().unwrap_or(&Path::new("."));
|
||||||
scene.mesh_paths = scene
|
scene.mesh_paths = scene
|
||||||
.mesh_paths
|
.mesh_paths
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -73,3 +86,34 @@ pub struct SceneInstance {
|
|||||||
pub rotation: [f32; 3],
|
pub rotation: [f32; 3],
|
||||||
pub scale: [f32; 3],
|
pub scale: [f32; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod create_files {
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io::Write;
|
||||||
|
pub use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn create_test_scene() -> Result<(), Box<dyn Error>> {
|
||||||
|
let scene = Scene {
|
||||||
|
name: "Testing Scene".to_string(),
|
||||||
|
mesh_paths: HashMap::from([
|
||||||
|
("venator".to_string(), "Venator.obj".to_string())
|
||||||
|
]),
|
||||||
|
instances: HashMap::from([
|
||||||
|
("venator".to_string(), SceneInstance {
|
||||||
|
translation: [0.0, 0.0, 0.0],
|
||||||
|
rotation: [0.0, 0.0, 0.0],
|
||||||
|
scale: [1.0, 1.0, 1.0]
|
||||||
|
})
|
||||||
|
])
|
||||||
|
};
|
||||||
|
|
||||||
|
let json = serde_json::to_string_pretty(&scene)?;
|
||||||
|
|
||||||
|
let mut file = std::fs::File::create("test_scene.scene")?;
|
||||||
|
file.write_all(json.as_bytes())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use vulkano::buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer};
|
||||||
|
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter};
|
||||||
|
use crate::common::camera::Camera;
|
||||||
|
|
||||||
|
#[derive(BufferContents)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct CameraUniform {
|
||||||
|
pub view: [[f32; 4]; 4],
|
||||||
|
pub proj: [[f32; 4]; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Camera> for CameraUniform {
|
||||||
|
fn from(camera: Camera) -> Self {
|
||||||
|
Self::from(&camera)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Camera> for CameraUniform {
|
||||||
|
fn from(camera: &Camera) -> Self {
|
||||||
|
let view = camera.get_view();
|
||||||
|
let proj = camera.get_projection();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
view: view.into(),
|
||||||
|
proj: proj.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -3,7 +3,7 @@ use vulkano::buffer::BufferContents;
|
|||||||
use vulkano::pipeline::graphics::vertex_input::Vertex;
|
use vulkano::pipeline::graphics::vertex_input::Vertex;
|
||||||
|
|
||||||
/// A transformation matrix for a mesh that can be passed into the shaders.
|
/// A transformation matrix for a mesh that can be passed into the shaders.
|
||||||
#[derive(BufferContents, Vertex)]
|
#[derive(Debug, Clone, BufferContents, Vertex)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct GpuInstance {
|
pub struct GpuInstance {
|
||||||
#[format(R32G32B32A32_SFLOAT)]
|
#[format(R32G32B32A32_SFLOAT)]
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
pub mod instance;
|
pub mod instance;
|
||||||
pub mod vertex3;
|
pub mod vertex3;
|
||||||
|
pub mod camera;
|
||||||
+9
-3
@@ -6,12 +6,18 @@ use vulkano::pipeline::graphics::vertex_input::Vertex;
|
|||||||
pub struct Vertex3 {
|
pub struct Vertex3 {
|
||||||
#[format(R32G32B32_SFLOAT)]
|
#[format(R32G32B32_SFLOAT)]
|
||||||
pub(crate) position: [f32; 3],
|
pub(crate) position: [f32; 3],
|
||||||
|
#[format(R32G32B32_SFLOAT)]
|
||||||
|
pub(crate) normal: [f32; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<[f32; 3]> for Vertex3 {
|
impl Vertex3 {
|
||||||
fn from(v: [f32; 3]) -> Self {
|
pub fn new(
|
||||||
|
position: [f32; 3],
|
||||||
|
normal: [f32; 3],
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
position: v.clone(),
|
position,
|
||||||
|
normal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
// Per-vertex information - Vertex3 struct
|
||||||
|
layout(location = 0) in vec3 position;
|
||||||
|
layout(location = 1) in vec3 normal;
|
||||||
|
|
||||||
|
// Per-instance information - GpuInstance struct
|
||||||
|
layout(location = 2) in vec4 trans_col1;
|
||||||
|
layout(location = 3) in vec4 trans_col2;
|
||||||
|
layout(location = 4) in vec4 trans_col3;
|
||||||
|
layout(location = 5) in vec4 trans_col4;
|
||||||
|
|
||||||
|
// Per-frame information - CameraUniform struct
|
||||||
|
//layout(location = 6) uniform vec4 proj_col1;
|
||||||
|
//layout(location = 7) uniform vec4 proj_col2;
|
||||||
|
//layout(location = 8) uniform vec4 proj_col3;
|
||||||
|
//layout(location = 9) uniform vec4 proj_col4;
|
||||||
|
|
||||||
|
layout(set = 0, binding = 0) uniform CameraUniform {
|
||||||
|
mat4 view;
|
||||||
|
mat4 projection;
|
||||||
|
} camera;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// We then multiply each vertex by its transform to find the correct location in the scene.
|
||||||
|
mat4 transform = mat4(trans_col1, trans_col2, trans_col3, trans_col4);
|
||||||
|
//mat4 proj = mat4(proj_col1, proj_col2, proj_col3, proj_col4);
|
||||||
|
|
||||||
|
mat4 mvp = camera.projection * camera.view * transform;
|
||||||
|
|
||||||
|
gl_Position = transform * vec4(position, 1.0f);
|
||||||
|
}
|
||||||
@@ -1,7 +1,32 @@
|
|||||||
#version 460
|
#version 460
|
||||||
|
|
||||||
|
// Per-vertex information - Vertex3 struct
|
||||||
layout(location = 0) in vec3 position;
|
layout(location = 0) in vec3 position;
|
||||||
|
layout(location = 1) in vec3 normal;
|
||||||
|
|
||||||
|
// Per-instance information - GpuInstance struct
|
||||||
|
layout(location = 2) in vec4 transform_col1;
|
||||||
|
layout(location = 3) in vec4 transform_col2;
|
||||||
|
layout(location = 4) in vec4 transform_col3;
|
||||||
|
layout(location = 5) in vec4 transform_col4;
|
||||||
|
|
||||||
|
// Per-frame information - CameraUniform struct
|
||||||
|
//layout(location = 6) uniform vec4 proj_col1;
|
||||||
|
//layout(location = 7) uniform vec4 proj_col2;
|
||||||
|
//layout(location = 8) uniform vec4 proj_col3;
|
||||||
|
//layout(location = 9) uniform vec4 proj_col4;
|
||||||
|
|
||||||
|
layout(set = 0, binding = 0) uniform CameraUniform {
|
||||||
|
mat4 view;
|
||||||
|
mat4 projection;
|
||||||
|
} camera;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = vec4(position, 1.0);
|
// We then multiply each vertex by its transform to find the correct location in the scene.
|
||||||
|
mat4 transform = mat4(transform_col1, transform_col2, transform_col3, transform_col4);
|
||||||
|
//mat4 proj = mat4(proj_col1, proj_col2, proj_col3, proj_col4);
|
||||||
|
|
||||||
|
mat4 mvp = camera.projection * camera.view * transform;
|
||||||
|
|
||||||
|
gl_Position = transform * vec4(position, 1.0f);
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user