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 { 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)> { let mut constants = Vec::new(); let cam_data: Vec = (Vec::::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::>() .into_flattened(); constants.push((vk::ShaderStageFlags::VERTEX, 0, cam_data)); let screen_res: Vec = res .iter() .map(|f| f.to_ne_bytes()) .collect::>() .into_flattened(); constants.push((vk::ShaderStageFlags::VERTEX, 32, screen_res)); let base_color: Vec = (Vec::::from([0.0, 1.0, 1.0, 1.0])) .iter() .map(|f| f.to_ne_bytes()) .collect::>() .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 { /// Create a cube to be rendered in world, this can then be /// added using [`Game::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 }