diff --git a/Cargo.toml b/Cargo.toml index bde140b..9594f54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ syn = { version = "2.0.67", features = ["extra-traits", "full"] } quote = "1.0.35" proc-macro2 = "1.0.86" downcast-rs = "1.2.1" +plexus = { version = "0.0.11", default-features = false } utilities = { git = "https://gavania.de/hodasemi/utilities.git" } vulkan-rs = { git = "https://gavania.de/hodasemi/vulkan_lib.git" } diff --git a/engine/src/scene/rendering/rasterizer/deferred/mod.rs b/engine/src/scene/rendering/rasterizer/deferred/mod.rs index e8c8df9..0abed00 100644 --- a/engine/src/scene/rendering/rasterizer/deferred/mod.rs +++ b/engine/src/scene/rendering/rasterizer/deferred/mod.rs @@ -218,7 +218,7 @@ impl Rasterizer { ) .add_sub_pass( SubPass::builder(width, height) - .set_prepared_targets(images, 0, [0.1, 0.1, 0.1, 1.0], true) + .set_prepared_targets(images, 0, None) .build(device)?, ) .build(device)?; diff --git a/engine/src/scene/rendering/rasterizer/traditional/mod.rs b/engine/src/scene/rendering/rasterizer/traditional/mod.rs index ca8d877..7dcb59e 100644 --- a/engine/src/scene/rendering/rasterizer/traditional/mod.rs +++ b/engine/src/scene/rendering/rasterizer/traditional/mod.rs @@ -7,16 +7,16 @@ use utilities::prelude::cgmath::{Vector3, Zero}; use super::{ super::{ + ExtensionCheck, RenderingFrontEnd, shared::{ bufferhandler::*, position_buffer_reader::PositionBuffer, safecommandbuffer::SafeCommandBuffer, }, - ExtensionCheck, RenderingFrontEnd, }, + SceneInfo, lights::light_wrapper::{LightData, LightHandler}, pipelines::RasterizerPipelines, rasterizershader::RasterizerShader, - SceneInfo, }; use std::sync::{Arc, Mutex}; @@ -126,7 +126,10 @@ impl TraditionalRasterizer { render_scale: f32, sample_count: VkSampleCountFlags, ) -> Result<(RenderTarget, usize)> { - assert_eq!(render_scale, 1.0, "Traditional Rasterizer: render_scale needs an in between image to render to, then blit this into swapchain image"); + assert_eq!( + render_scale, 1.0, + "Traditional Rasterizer: render_scale needs an in between image to render to, then blit this into swapchain image" + ); let width = (screen_width * render_scale) as u32; let height = (screen_height * render_scale) as u32; @@ -142,7 +145,7 @@ impl TraditionalRasterizer { .add_sub_pass( SubPass::builder(width, height) // swapchain color target - .set_prepared_targets(images, 0, [0.0, 0.0, 0.0, 0.0], true) + .set_prepared_targets(images, 0, None) // position target .add_target_info(CustomTarget { usage: VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT diff --git a/skybox/Cargo.toml b/skybox/Cargo.toml index 57c71e1..b220e36 100644 --- a/skybox/Cargo.toml +++ b/skybox/Cargo.toml @@ -6,6 +6,9 @@ edition = "2024" [dependencies] anyhow.workspace = true +plexus.workspace = true +utilities.workspace = true ecs = { path = "../ecs" } context = { path = "../context" } +engine = { path = "../engine" } diff --git a/skybox/build.rs b/skybox/build.rs new file mode 100644 index 0000000..55b785f --- /dev/null +++ b/skybox/build.rs @@ -0,0 +1,53 @@ +use std::{fs, path::Path, process::Command}; + +const FILE_ENDINGS: &'static [&'static str] = &[ + "vert", "frag", "geom", "comp", "rchit", "rmiss", "rgen", "rahit", +]; + +fn find_shader_files(path: impl AsRef) -> Vec { + let mut v = Vec::new(); + + if !path.as_ref().is_dir() { + panic!("path ({:?}) is not a directory!", path.as_ref()); + } + + for entry in fs::read_dir(path).unwrap() { + let child_path = entry.unwrap().path(); + + if child_path.is_dir() { + v.extend(find_shader_files(child_path)); + } else if child_path.is_file() { + for ending in FILE_ENDINGS.iter() { + if child_path.extension().unwrap() == *ending { + v.push(child_path.to_str().unwrap().to_string()); + + break; + } + } + } + } + + v +} + +fn compile_shader(shader_files: &[String]) { + Command::new("glslangValidator") + .arg("--help") + .output() + .expect("Failed to execute glslangValidator. Maybe you need to install it first?"); + + for shader in shader_files { + Command::new("glslangValidator") + .arg("-V") + .arg(shader) + .arg("-o") + .arg(&format!("{}.spv", shader)) + .output() + .expect(&format!("Failed to compile {}", shader)); + } +} + +fn main() { + let shader_files = find_shader_files("shader"); + compile_shader(&shader_files); +} diff --git a/skybox/shader/skybox.frag b/skybox/shader/skybox.frag new file mode 100644 index 0000000..251f024 --- /dev/null +++ b/skybox/shader/skybox.frag @@ -0,0 +1,12 @@ +#version 450 + +layout (binding = 1) uniform samplerCube sampler_cube_map; + +layout (location = 0) in vec3 in_uvw; + +layout (location = 0) out vec4 out_frag_color; + +void main() +{ + out_frag_color = texture(sampler_cube_map, in_uvw); +} \ No newline at end of file diff --git a/skybox/shader/skybox.vert b/skybox/shader/skybox.vert new file mode 100644 index 0000000..20fab41 --- /dev/null +++ b/skybox/shader/skybox.vert @@ -0,0 +1,27 @@ +#version 450 + +layout (location = 0) in vec3 in_position; + +layout (set = 0, binding = 0) uniform CameraProperties +{ + mat4 view; + mat4 proj; + mat4 vp; + + mat4 inv_view; + mat4 inv_proj; +} globals; + +layout (location = 0) out vec3 out_uvw; + +void main() +{ + out_uvw = in_position; + + // Convert cubemap coordinates into Vulkan coordinate space + out_uvw.xy *= -1.0; + + // Remove translation from view matrix + mat4 view_mat = mat4(mat3(globals.view)); + gl_Position = globals.proj * view_mat * vec4(in_position, 1.0); +} \ No newline at end of file diff --git a/skybox/src/lib.rs b/skybox/src/lib.rs index a351e14..09c93e8 100644 --- a/skybox/src/lib.rs +++ b/skybox/src/lib.rs @@ -1,8 +1,16 @@ +mod vertex; + use std::{path::PathBuf, sync::Arc}; use anyhow::Result; -use context::prelude::*; use ecs::*; +use engine::prelude::{shader_type::*, *}; +use plexus::primitive::{ + cube::{Bounds, Cube}, + decompose::Triangulate, + generate::PolygonsWithPosition, +}; +use vertex::VertexPoint; pub struct SkyBoxImages { left: PathBuf, @@ -29,15 +37,29 @@ impl> From for SkyBoxImages { } pub struct SkyBox { - cube_map: Arc, + _cube_map: Arc, + cube_buffer: Arc>, + + vertex_shader: Arc>, + fragment_shader: Arc>, + + render_target: TargetMode, + pipeline: TargetMode>, + descriptor_set: Arc, } impl SkyBox { pub fn new(world: &mut WorldBuilder, images: impl Into) -> Result<()> { - let images = images.into(); - let context = world.resources.get_mut::(); + let sample_count = world + .resources + .get::() + .graphics_info()? + .sample_count; + + let context = world.resources.get_mut_unchecked::(); context.render_core_mut().add_render_routine::(1); + let images = images.into(); let cube_map = Image::cube_map([ images.left.try_into()?, images.right.try_into()?, @@ -51,22 +73,170 @@ impl SkyBox { .attach_pretty_sampler(context.device())? .build(context.device(), context.queue())?; - let me = Self { cube_map }; + let descriptor_set_layout = DescriptorSetLayout::builder() + .add_layout_binding( + 0, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + VK_SHADER_STAGE_VERTEX_BIT, + 0, + ) + .add_layout_binding( + 1, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + VK_SHADER_STAGE_FRAGMENT_BIT, + 0, + ) + .build(context.device().clone())?; + + let render_target = Self::create_render_target(context, sample_count)?; + + let pipeline_layout = PipelineLayout::builder() + .add_descriptor_set_layout(&descriptor_set_layout) + .build(context.device().clone())?; + + let pipeline = + Self::create_pipeline(context, sample_count, &render_target, &pipeline_layout)?; + + let descriptor_pool = DescriptorPool::builder() + .set_layout(descriptor_set_layout) + .build(context.device().clone())?; + let descriptor_set = descriptor_pool.prepare_set().allocate()?; + + let scene = world.resources.get::(); + let view = scene.view(); + + descriptor_set.update(&[ + DescriptorWrite::uniform_buffers(0, &[view.buffer()]), + DescriptorWrite::combined_samplers(1, &[&cube_map]), + ])?; + + let cube_mesh = Cube::new() + .polygons_with_position_from(Bounds::unit_radius()) + .triangulate() + .map(|d| { + vec![ + [d.a.0.into_inner(), d.a.1.into_inner(), d.a.2.into_inner()], + [d.b.0.into_inner(), d.b.1.into_inner(), d.b.2.into_inner()], + [d.c.0.into_inner(), d.c.1.into_inner(), d.c.2.into_inner()], + ] + }) + .flatten() + .map(|t| VertexPoint::new(t[0] as f32, t[1] as f32, t[2] as f32)) + .collect::>(); + + let cube_buffer = Buffer::builder() + .set_memory_usage(MemoryUsage::GpuOnly) + .set_sharing_mode(VK_SHARING_MODE_EXCLUSIVE) + .set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) + .set_data(&cube_mesh) + .build(context.device().clone())?; + + let me = Self { + _cube_map: cube_map, + cube_buffer, + + render_target, + pipeline, + descriptor_set, + }; world.resources.insert(me); Ok(()) } + + fn create_render_target( + context: &Context, + sample_count: VkSampleCountFlags, + ) -> Result> { + context.images().execute(|images| { + let first = images.first().unwrap(); + + let width = first.width(); + let height = first.height(); + let format = first.vk_format(); + + if sample_count == VK_SAMPLE_COUNT_1_BIT { + RenderTarget::builder() + .add_sub_pass( + SubPass::builder(width, height) + // render directly into swapchain target if there is no multi sampling + .set_prepared_targets(images, 0, None) + .use_queue(context.queue().clone()) + .build(context.device())?, + ) + .build(context.device()) + } else { + RenderTarget::builder() + .add_sub_pass( + SubPass::builder(width, height) + // render into multi sampled images + .add_target_info(CustomTarget { + usage: VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT.into(), + format, + clear_on_load: true, + store_on_save: true, + attach_sampler: false, + use_as_input: false, + clear_value: ClearValue::Color([0.0, 0.0, 0.0, 0.0]), + }) + .set_sample_count(sample_count) + // resolve multi sampling into swapchain image + .add_resolve_targets((images, false)) + .use_queue(context.queue().clone()) + .build(context.device())?, + ) + .build(context.device()) + } + }) + } + + fn create_pipeline( + context: &Context, + sample_count: VkSampleCountFlags, + render_target: &TargetMode, + pipeline_layout: &Arc, + ) -> Result>> { + render_target.execute(|render_target| { + Pipeline::new_graphics() + .default_multisample(sample_count) + .default_rasterization(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE) + .build( + context.device().clone(), + pipeline_layout, + render_target.render_pass(), + 0, + ) + }) + } } impl TScene for SkyBox { fn process( &mut self, buffer_recorder: &mut CommandBufferRecorder<'_>, - images: &TargetMode>>, + _images: &TargetMode>>, indices: &TargetMode, - world: &World, + _world: &World, ) -> Result<()> { - todo!() + self.render_target + .chain(indices) + .chain(&self.pipeline) + .unfold() + .execute(|(render_target, index, pipeline)| { + render_target.begin(buffer_recorder, VK_SUBPASS_CONTENTS_INLINE, ***index); + + buffer_recorder.bind_pipeline(pipeline)?; + buffer_recorder.bind_descriptor_sets_minimal(&[&self.descriptor_set]); + + buffer_recorder.bind_vertex_buffer(&self.cube_buffer); + buffer_recorder.draw_complete_single_instance(self.cube_buffer.size() as u32); + + render_target.end(buffer_recorder); + + Ok(()) + })?; + + Ok(()) } fn resize( diff --git a/skybox/src/vertex.rs b/skybox/src/vertex.rs new file mode 100644 index 0000000..ebacc1e --- /dev/null +++ b/skybox/src/vertex.rs @@ -0,0 +1,31 @@ +use engine::prelude::*; +use utilities::impl_reprc; + +impl_reprc!( + pub struct VertexPoint { + #[assume_reprc] + v: [f32; 4], + } +); + +impl VertexPoint { + pub fn new(x: impl Into, y: impl Into, z: impl Into) -> Self { + VertexPoint { + v: [x.into(), y.into(), z.into(), 1.0], + } + } +} + +impl VertexInputDescription for VertexPoint { + fn attributes() -> Vec { + vec![ + // position + VkVertexInputAttributeDescription { + location: 0, + binding: 0, + format: VK_FORMAT_R32G32B32_SFLOAT, + offset: 0, + }, + ] + } +}