You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
MineClone/src/render/cube.rs

402 lines
11 KiB

use std::collections::HashMap;
use ash::vk;
use crate::{Game, LoadingWorld, RenderCtx, render::MemType};
use super::loading_world::WorldComponent;
pub struct Cube {
mesh_buf: vk::Buffer,
pipeline: vk::Pipeline,
pipe_layout: vk::PipelineLayout,
}
const MESH_SIZE: u64 = 36 * 2 * 4 * 4;
#[rustfmt::skip]
const POSITIONS: [f32; 36 * 2 * 4] = [
// BOTTOM
-0.5, 0.5, 1.0, 1.0,
0.0, 1.0, 0.0, 0.0,
0.5, 0.5, 2.0, 1.0,
0.0, 1.0, 0.0, 0.0,
-0.5, 0.5, 2.0, 1.0,
0.0, 1.0, 0.0, 0.0,
0.5, 0.5, 1.0, 1.0,
0.0, 1.0, 0.0, 0.0,
0.5, 0.5, 2.0, 1.0,
0.0, 1.0, 0.0, 0.0,
-0.5, 0.5, 1.0, 1.0,
0.0, 1.0, 0.0, 0.0,
// TOP
0.5, -0.5, 2.0, 1.0,
0.0, -1.0, 0.0, 0.0,
-0.5, -0.5, 1.0, 1.0,
0.0, -1.0, 0.0, 0.0,
-0.5, -0.5, 2.0, 1.0,
0.0, -1.0, 0.0, 0.0,
0.5, -0.5, 2.0, 1.0,
0.0, -1.0, 0.0, 0.0,
0.5, -0.5, 1.0, 1.0,
0.0, -1.0, 0.0, 0.0,
-0.5, -0.5, 1.0, 1.0,
0.0, -1.0, 0.0, 0.0,
// FRONT
-0.5, -0.5, 1.0, 1.0,
0.0, 0.0, -1.0, 0.0,
0.5, 0.5, 1.0, 1.0,
0.0, 0.0, -1.0, 0.0,
-0.5, 0.5, 1.0, 1.0,
0.0, 0.0, -1.0, 0.0,
0.5, -0.5, 1.0, 1.0,
0.0, 0.0, -1.0, 0.0,
0.5, 0.5, 1.0, 1.0,
0.0, 0.0, -1.0, 0.0,
-0.5, -0.5, 1.0, 1.0,
0.0, 0.0, -1.0, 0.0,
// BACK
0.5, 0.5, 2.0, 1.0,
0.0, 0.0, 1.0, 0.0,
-0.5, -0.5, 2.0, 1.0,
0.0, 0.0, 1.0, 0.0,
-0.5, 0.5, 2.0, 1.0,
0.0, 0.0, 1.0, 0.0,
0.5, 0.5, 2.0, 1.0,
0.0, 0.0, 1.0, 0.0,
0.5, -0.5, 2.0, 1.0,
0.0, 0.0, 1.0, 0.0,
-0.5, -0.5, 2.0, 1.0,
0.0, 0.0, 1.0, 0.0,
// LEFT
-0.5, -0.5, 1.0, 1.0,
-1.0, 0.0, 0.0, 0.0,
-0.5, 0.5, 2.0, 1.0,
-1.0, 0.0, 0.0, 0.0,
-0.5, -0.5, 2.0, 1.0,
-1.0, 0.0, 0.0, 0.0,
-0.5, 0.5, 1.0, 1.0,
-1.0, 0.0, 0.0, 0.0,
-0.5, 0.5, 2.0, 1.0,
-1.0, 0.0, 0.0, 0.0,
-0.5, -0.5, 1.0, 1.0,
-1.0, 0.0, 0.0, 0.0,
// RIGHT
0.5, 0.5, 2.0, 1.0,
1.0, 0.0, 0.0, 0.0,
0.5, -0.5, 1.0, 1.0,
1.0, 0.0, 0.0, 0.0,
0.5, -0.5, 2.0, 1.0,
1.0, 0.0, 0.0, 0.0,
0.5, 0.5, 2.0, 1.0,
1.0, 0.0, 0.0, 0.0,
0.5, 0.5, 1.0, 1.0,
1.0, 0.0, 0.0, 0.0,
0.5, -0.5, 1.0, 1.0,
1.0, 0.0, 0.0, 0.0,
];
impl WorldComponent for Cube {
fn descriptors(&self) -> std::collections::HashMap<vk::DescriptorType, u32> {
let mut map = HashMap::new();
map.insert(vk::DescriptorType::COMBINED_IMAGE_SAMPLER, 1);
map.insert(vk::DescriptorType::STORAGE_BUFFER, 1);
map
}
fn desc_layout(&self, ctx: &RenderCtx) -> vk::DescriptorSetLayout {
setup_desc_layout(ctx)
}
fn write_desc_set(
&self,
ctx: &RenderCtx,
desc_set: vk::DescriptorSet,
skybox: (vk::ImageView, vk::Sampler),
) {
let buf_info = vk::DescriptorBufferInfo::default()
.buffer(self.mesh_buf)
.offset(0)
.range(vk::WHOLE_SIZE);
let buf_infos = [buf_info];
let buf_desc = vk::WriteDescriptorSet::default()
.dst_set(desc_set)
.dst_binding(0)
.dst_array_element(0)
.descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
.buffer_info(&buf_infos);
let img_info = vk::DescriptorImageInfo::default()
.image_layout(vk::ImageLayout::GENERAL)
.image_view(skybox.0)
.sampler(skybox.1);
let img_infos = &[img_info];
let img_desc = vk::WriteDescriptorSet::default()
.dst_set(desc_set)
.dst_binding(1)
.dst_array_element(0)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.image_info(img_infos);
unsafe {
ctx.dev.update_descriptor_sets(&[buf_desc, img_desc], &[]);
}
}
fn pipeline(&self) -> (vk::Pipeline, vk::PipelineLayout) {
(self.pipeline, self.pipe_layout)
}
fn push_constants(
&self,
camera: &crate::game::Camera,
res: [u32; 2],
base_color: [f32; 4],
) -> Vec<(vk::ShaderStageFlags, u32, Vec<u8>)> {
let mut constants = Vec::new();
let cam_data: Vec<u8> = (Vec::<f32>::from([
camera.origin.x,
camera.origin.y,
camera.origin.z,
0.0,
camera.rotation.x,
camera.rotation.y,
camera.rotation.z,
]))
.iter()
.map(|f| f.to_ne_bytes())
.collect::<Vec<_>>()
.into_flattened();
constants.push((vk::ShaderStageFlags::VERTEX, 0, cam_data));
let screen_res: Vec<u8> = res
.iter()
.map(|f| f.to_ne_bytes())
.collect::<Vec<_>>()
.into_flattened();
constants.push((vk::ShaderStageFlags::VERTEX, 32, screen_res));
let base_color: Vec<u8> = (Vec::<f32>::from([0.0, 1.0, 1.0, 1.0]))
.iter()
.map(|f| f.to_ne_bytes())
.collect::<Vec<_>>()
.into_flattened();
constants.push((vk::ShaderStageFlags::FRAGMENT, 48, base_color));
constants
}
fn num_verticies(&self) -> u32 {
36
}
}
impl RenderCtx {
fn create_cube(&self, pass: vk::RenderPass) -> Cube {
let mesh_mem = self.mem_alloc(MemType::HostVisibile, MESH_SIZE);
let mesh_buf = self.alloc_buf(
mesh_mem,
MESH_SIZE,
vk::BufferUsageFlags::TRANSFER_SRC | vk::BufferUsageFlags::STORAGE_BUFFER,
);
let mesh_mem = self.buf_to_ptr(mesh_mem, mesh_buf);
unsafe {
std::ptr::copy(POSITIONS.as_ptr(), mesh_mem as *mut f32, POSITIONS.len());
}
let pipe_layout = setup_pipe_layout(self);
let pipeline = setup_pipeline(self, &pass, pipe_layout);
Cube {
mesh_buf,
pipeline,
pipe_layout,
}
}
}
impl Game<LoadingWorld> {
/// Create a cube to be rendered in world, this can then be
/// added using [`Game<LoadingWorld>::add_component`]
pub fn create_cube(&self) -> Cube {
let Self {
state: LoadingWorld { ctx, pass, .. },
} = self;
ctx.create_cube(*pass)
}
}
fn load_cube_shaders(ctx: &RenderCtx) -> (vk::ShaderModule, vk::ShaderModule) {
let shader_vert_shader = ctx.shader_mod_from_file(
"shaders/cube_vert.spv",
vk::ShaderModuleCreateFlags::empty(),
);
let shader_frag_shader = ctx.shader_mod_from_file(
"shaders/cube_frag.spv",
vk::ShaderModuleCreateFlags::empty(),
);
(shader_vert_shader, shader_frag_shader)
}
fn setup_desc_layout(ctx: &RenderCtx) -> vk::DescriptorSetLayout {
let storage_binding = vk::DescriptorSetLayoutBinding::default()
.binding(0)
.descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
.descriptor_count(1)
.stage_flags(vk::ShaderStageFlags::VERTEX);
let image_binding = vk::DescriptorSetLayoutBinding::default()
.binding(1)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.descriptor_count(1)
.stage_flags(vk::ShaderStageFlags::FRAGMENT);
let layouts = [storage_binding, image_binding];
let layout_info = vk::DescriptorSetLayoutCreateInfo::default().bindings(&layouts);
let layout = unsafe {
ctx.dev
.create_descriptor_set_layout(&layout_info, None)
.expect("Failed to create descriptor set layout")
};
layout
}
fn setup_pipe_layout(ctx: &RenderCtx) -> vk::PipelineLayout {
let layout = setup_desc_layout(ctx);
let push_range: [vk::PushConstantRange; 2] = [
vk::PushConstantRange::default()
.stage_flags(vk::ShaderStageFlags::VERTEX)
.offset(0)
.size(48), // vec4 camera_orig, camera_rot; uvec2 screen_res
vk::PushConstantRange::default()
.stage_flags(vk::ShaderStageFlags::FRAGMENT)
.offset(48)
.size(16), // vec4 base_color
];
let layouts = &[layout];
let pipe_layout = vk::PipelineLayoutCreateInfo::default()
.set_layouts(layouts.as_ref())
.push_constant_ranges(&push_range);
let pipe_layout = unsafe {
ctx.dev
.create_pipeline_layout(&pipe_layout, None)
.expect("Failed to create pipeline layout")
};
pipe_layout
}
fn setup_pipeline(
ctx: &RenderCtx,
pass: &vk::RenderPass,
layout: vk::PipelineLayout,
) -> vk::Pipeline {
let (vert_shader, frag_shader) = load_cube_shaders(ctx);
let shader_stages = ctx.setup_simple_shader_stage(vert_shader, frag_shader);
let vert_input = vk::PipelineVertexInputStateCreateInfo::default();
let input_assembly = vk::PipelineInputAssemblyStateCreateInfo::default()
.topology(vk::PrimitiveTopology::TRIANGLE_LIST);
let rasterization = vk::PipelineRasterizationStateCreateInfo::default()
.polygon_mode(vk::PolygonMode::FILL)
.cull_mode(vk::CullModeFlags::BACK)
.front_face(vk::FrontFace::COUNTER_CLOCKWISE)
.line_width(1.0);
let multisample = vk::PipelineMultisampleStateCreateInfo::default()
.rasterization_samples(vk::SampleCountFlags::TYPE_1);
let depth_stencil = vk::PipelineDepthStencilStateCreateInfo::default()
.depth_test_enable(true)
.depth_write_enable(true)
.depth_compare_op(vk::CompareOp::LESS)
.depth_bounds_test_enable(true)
.stencil_test_enable(false)
.min_depth_bounds(0.0)
.max_depth_bounds(1.0);
let blend = vk::PipelineColorBlendAttachmentState::default()
.src_color_blend_factor(vk::BlendFactor::SRC_COLOR)
.dst_color_blend_factor(vk::BlendFactor::SRC_COLOR)
.color_blend_op(vk::BlendOp::ADD)
.src_alpha_blend_factor(vk::BlendFactor::SRC_COLOR)
.dst_alpha_blend_factor(vk::BlendFactor::SRC_COLOR)
.alpha_blend_op(vk::BlendOp::ADD)
.color_write_mask(
vk::ColorComponentFlags::R
| vk::ColorComponentFlags::G
| vk::ColorComponentFlags::B
| vk::ColorComponentFlags::A,
);
let blend_attachments = [blend];
let color_blend = vk::PipelineColorBlendStateCreateInfo::default()
.attachments(blend_attachments.as_ref())
.logic_op_enable(false)
.logic_op(vk::LogicOp::COPY)
.blend_constants([1.0, 1.0, 1.0, 1.0]);
let dynamic = vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&[
vk::DynamicState::VIEWPORT_WITH_COUNT,
vk::DynamicState::SCISSOR_WITH_COUNT,
]);
let pipe_info = vk::GraphicsPipelineCreateInfo::default()
.stages(&shader_stages)
.vertex_input_state(&vert_input)
.input_assembly_state(&input_assembly)
.rasterization_state(&rasterization)
.multisample_state(&multisample)
.depth_stencil_state(&depth_stencil)
.color_blend_state(&color_blend)
.layout(layout)
.dynamic_state(&dynamic)
.render_pass(*pass)
.subpass(0);
let pipe = unsafe {
ctx.dev
.create_graphics_pipelines(vk::PipelineCache::null(), &[pipe_info], None)
.expect("Failed to create graphics pipeline")
.remove(0)
};
println!("Created pipeline");
pipe
}