From 4f261fa7086a8bafa1ffa0b3d7b88cfcfd29e80a Mon Sep 17 00:00:00 2001 From: Till Date: Fri, 2 Aug 2019 23:50:37 +0200 Subject: [PATCH] textures!!! --- shaders/triangle.frag | 6 ++-- shaders/triangle.vert | 5 +-- src/main.rs | 18 +++++++--- src/mesh.rs | 22 ++++++++---- src/vulkan.rs | 84 +++++++++++++++++++++++++++++++++++++------ 5 files changed, 108 insertions(+), 27 deletions(-) diff --git a/shaders/triangle.frag b/shaders/triangle.frag index 9c7d850..d7cec41 100644 --- a/shaders/triangle.frag +++ b/shaders/triangle.frag @@ -1,9 +1,11 @@ #version 450 -layout(location = 0) in vec3 pos; +layout(location = 0) in vec2 tex_coords; layout(location = 0) out vec4 f_color; +layout(set = 0, binding = 0) uniform sampler2D tex; + void main() { - f_color = vec4(.3, (pos.y + 5.) / 10., .8, 1.0); + f_color = texture(tex, tex_coords); } \ No newline at end of file diff --git a/shaders/triangle.vert b/shaders/triangle.vert index 55fc202..91d68b8 100644 --- a/shaders/triangle.vert +++ b/shaders/triangle.vert @@ -2,8 +2,9 @@ #extension GL_ARB_separate_shader_objects : enable layout(location = 0) in vec3 position; +layout(location = 1) in vec2 uv; -layout(location = 0) out vec3 pos; +layout(location = 0) out vec2 tex_coords; layout(push_constant) uniform PushConstants { float time; @@ -14,5 +15,5 @@ layout(push_constant) uniform PushConstants { void main() { gl_Position = push.projection * push.view * push.model * vec4(position, 1.0); - pos = gl_Position.xyz; + tex_coords = uv; } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index ea2889a..9263886 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ struct TestGame { input: InputState, cam_position: Vector3, cam_rotation: Quaternion, - cube_mesh: Option, + test_meshes: Vec<(MeshHandle, usize)>, cubes: Vec, log_config: LogConfig, } @@ -29,8 +29,16 @@ impl Game for TestGame { impl TestGame { fn game_start(self: &mut Self, renderer: &mut VulkanRenderer) { - self.cube_mesh = Some(renderer.upload_mesh(mesh::load_mesh("models/box.gltf", self.log_config.mesh_load_info).unwrap().into_iter().nth(0).unwrap())); - println!("Game started."); + let (meshes, textures) = mesh::load_mesh("models/iski51.gltf", self.log_config.mesh_load_info).unwrap(); + self.test_meshes = meshes.into_iter().map(|m| { + let id = match m.texture_index { + Some(tex_id) => tex_id + 1, + None => 0, + }; + (renderer.upload_mesh(m), id) + }).collect(); + textures.iter().for_each(|tex| renderer.upload_texture(tex)); + println!("Game loaded!"); } fn update(self: &mut Self, renderer: &mut VulkanRenderer) { @@ -55,7 +63,7 @@ impl TestGame { if self.input.button_just_pressed("test") { println!("test"); - self.cubes.push(renderer.add_game_object(GameObject::new(self.cube_mesh.unwrap()))); + self.cubes = self.test_meshes.iter().map(|(mesh, tex_id)| renderer.add_game_object(GameObject::new(*mesh, *tex_id, renderer))).collect(); } self.cam_rotation = self.cam_rotation * Quaternion::from_angle_z(Deg(self.input.get_axis("look_horizontal") * 0.05)); @@ -93,7 +101,7 @@ fn main() { input: InputState::new("config/input.toml", log_config), cam_rotation: Quaternion::one(), cam_position: Vector3::new(0.0, 0.0, -10.0), - cube_mesh: None, + test_meshes: vec![], cubes: vec![], log_config }; diff --git a/src/mesh.rs b/src/mesh.rs index 7034fa7..304ade1 100644 --- a/src/mesh.rs +++ b/src/mesh.rs @@ -3,11 +3,13 @@ use crate::vulkan::Vertex; use std::time::SystemTime; pub struct CPUMesh { - pub(crate) vertices: Vec, - pub(crate) indices: Vec, + pub vertices: Vec, + pub indices: Vec, + pub texture_index: Option, + pub name: Option, } -pub fn load_mesh(mesh_path: &str, print_status: bool) -> Result, gltf::Error> { +pub fn load_mesh(mesh_path: &str, print_status: bool) -> Result<(Vec, Vec), gltf::Error> { let mut start_time = None; let mut total_vertices = 0; let mut total_indices = 0; @@ -16,14 +18,15 @@ pub fn load_mesh(mesh_path: &str, print_status: bool) -> Result, gl start_time = Some(SystemTime::now()); println!("Loading mesh file {}", mesh_path); } - let (document, buffers, _textures) = gltf::import(mesh_path)?; + let (document, buffers, textures) = gltf::import(mesh_path)?; let mut meshes = vec![]; if print_status { println!("Mesh file loaded after {} seconds, processing...", start_time.unwrap().elapsed().unwrap().as_secs()); } - for mesh in document.meshes() { for primitive in mesh.primitives() { + let texture_index = primitive.material().pbr_metallic_roughness().base_color_texture().map(|tex_info| tex_info.texture().index()); + let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()])); match (reader.read_indices(), reader.read_positions(), reader.read_tex_coords(0), reader.read_normals()) { (Some(indices), Some(positions), Some(tex_coords), Some(normals)) => { @@ -32,7 +35,12 @@ pub fn load_mesh(mesh_path: &str, print_status: bool) -> Result, gl uv: t, normal: n }).collect(); - let cpu_mesh = CPUMesh { vertices, indices: indices.into_u32().collect() }; + let cpu_mesh = CPUMesh { + vertices, + indices: indices.into_u32().collect(), + texture_index, + name: mesh.name().map(|n| n.to_owned()), + }; if print_status { let vert_count = cpu_mesh.vertices.len(); let index_count = cpu_mesh.indices.len(); @@ -60,5 +68,5 @@ pub fn load_mesh(mesh_path: &str, print_status: bool) -> Result, gl println!("Finished loading file, total vertices: {}, total indices: {}", total_vertices, total_indices); } - Ok(meshes) + Ok((meshes, textures)) } \ No newline at end of file diff --git a/src/vulkan.rs b/src/vulkan.rs index 06e2084..6e5feb7 100644 --- a/src/vulkan.rs +++ b/src/vulkan.rs @@ -2,7 +2,7 @@ use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer}; use vulkano::command_buffer::{AutoCommandBufferBuilder, DynamicState}; use vulkano::device::{Device, DeviceExtensions, Queue}; use vulkano::framebuffer::{Framebuffer, FramebufferAbstract, Subpass, RenderPassAbstract}; -use vulkano::image::{SwapchainImage, AttachmentImage, ImageUsage}; +use vulkano::image::{SwapchainImage, AttachmentImage, ImageUsage, ImmutableImage, Dimensions}; use vulkano::instance::{Instance, PhysicalDevice, ApplicationInfo, Version, InstanceExtensions}; use vulkano::pipeline::{GraphicsPipeline, GraphicsPipelineAbstract}; use vulkano::pipeline::shader::{GraphicsShaderType, ShaderModule}; @@ -14,6 +14,9 @@ use vulkano::sync; use vulkano::format::{Format, ClearValue}; use vulkano::instance::debug::{DebugCallback, MessageTypes}; use vulkano::memory::pool::{PotentialDedicatedAllocation, StdMemoryPoolAlloc}; +use vulkano::descriptor::descriptor_set::PersistentDescriptorSet; +use vulkano::descriptor::DescriptorSet; +use vulkano::sampler::{Sampler, Filter, MipmapMode, SamplerAddressMode}; use vulkano_win::VkSurfaceBuild; @@ -28,14 +31,15 @@ use cgmath::{Matrix4, SquareMatrix}; use shade_runner; use shade_runner::{CompiledShaders, Entry}; - use shaderc; + use vs::ty::PushConstants; use line_vs::ty::LinePushConstants; - -use crate::mesh::CPUMesh; +use crate::mesh::{CPUMesh}; use crate::vulkan::RenderLoopResult::Quit; +use image::{ImageFormat, ConvertBuffer, ImageBuffer, Rgb, Rgba}; + const VALIDATION_LAYERS: &[&str] = &[ "VK_LAYER_LUNARG_standard_validation" ]; @@ -66,6 +70,8 @@ pub struct Mesh { pub struct GameObject { pub mesh_index: usize, + pub texture_index: usize, + pub descriptor_set: Arc, pub model_matrix: Matrix4, } @@ -82,12 +88,15 @@ pub struct GameData { pub shutdown: bool, pub game_objects: Vec, pub meshes: Vec, + pub textures: Vec>>, } pub struct VulkanRenderer { pub game_data: GameData, pub device: Arc, pub framebuffers: Vec>, + pub sampler: Arc, + pub default_descriptor_set: Arc, pub dynamic_state: DynamicState, pub pipeline: Arc, pub line_pipeline: Arc, @@ -130,6 +139,7 @@ impl VulkanRenderer { dimensions: [0, 0], meshes: vec![], game_objects: vec![], + textures: vec![], }; if enable_validation_layers { @@ -268,11 +278,36 @@ impl VulkanRenderer { } ).unwrap()); + let sampler = Sampler::new(device.clone(), Filter::Linear, Filter::Linear, + MipmapMode::Nearest, SamplerAddressMode::Repeat, SamplerAddressMode::Repeat, + SamplerAddressMode::Repeat, 0.0, 1.0, 0.0, 0.0).unwrap(); + let pipeline: Arc = create_pipeline::(device.clone(), render_pass.clone(), "shaders/triangle.vert", "shaders/triangle.frag", false).unwrap(); let line_pipeline: Arc = create_pipeline::(device.clone(), render_pass.clone(), "shaders/line.vert", "shaders/line.frag", true).unwrap(); + + let (default_tex, default_tex_future) = { + let image = image::load_from_memory_with_format(include_bytes!("../models/Eye.png"), + ImageFormat::PNG).unwrap().to_rgba(); + let image_data = image.into_raw().clone(); + + ImmutableImage::from_iter( + image_data.iter().cloned(), + Dimensions::Dim2d { width: 4096, height: 4096 }, + Format::R8G8B8A8Unorm, + queue.clone(), + ).unwrap() + }; + + let default_descriptor_set = Arc::new(PersistentDescriptorSet::start(pipeline.clone(), 0) + .add_sampled_image(default_tex.clone(), sampler.clone()).unwrap() + .build().unwrap() + ); + + data.textures.push(default_tex); + // Dynamic viewports allow us to recreate just the viewport when the window is resized // Otherwise we would have to recreate the whole pipeline. let mut dynamic_state = DynamicState { line_width: None, viewports: None, scissors: None }; @@ -289,7 +324,9 @@ impl VulkanRenderer { // that, we store the submission of the previous frame here. let previous_frame_end = Some(Box::new(sync::now(device.clone())) as Box); - VulkanRenderer { game_data: data, device, framebuffers, dynamic_state, pipeline, line_pipeline, + VulkanRenderer { game_data: data, device, framebuffers, sampler, + default_descriptor_set: default_descriptor_set, + dynamic_state, pipeline, line_pipeline, surface, swapchain, render_pass, queue, line_vertex_buffer, events_loop, recreate_swapchain: false, debug_callback, previous_frame_end } } @@ -362,10 +399,12 @@ impl VulkanRenderer { let game_object = &self.game_data.game_objects[i]; let mesh = &self.game_data.meshes[game_object.mesh_index]; self.game_data.push_constants.model = game_object.model_matrix.into(); - cbb = cbb.draw_indexed(self.pipeline.clone(), &self.dynamic_state, + cbb = cbb.draw_indexed(self.pipeline.clone(), + &self.dynamic_state, vec![mesh.vertex_buffer.clone()], mesh.index_buffer.clone(), - (), self.game_data.push_constants.clone()).unwrap(); + game_object.descriptor_set.clone(), + self.game_data.push_constants.clone()).unwrap(); } cbb = cbb.draw(self.line_pipeline.clone(), &self.dynamic_state, vec![self.line_vertex_buffer.clone()], (), self.game_data.line_push_constants.clone()).unwrap() @@ -423,6 +462,22 @@ impl VulkanRenderer { self.game_data.meshes.len() - 1 } + pub fn upload_texture(self: &mut Self, texture_data: &gltf::image::Data) { + let buffer: ImageBuffer, Vec> = image::ImageBuffer::from_raw(texture_data.width, texture_data.height, texture_data.pixels.clone()).unwrap(); + let new_buffer: ImageBuffer, Vec> = buffer.convert(); + + let (image_view, future) = ImmutableImage::from_iter( + new_buffer.iter().cloned(), + Dimensions::Dim2d { width: texture_data.width, height: texture_data.height }, + Format::R8G8B8A8Unorm, + self.queue.clone(), + ).unwrap(); + + future.flush().unwrap(); + + self.game_data.textures.push(image_view); + } + pub fn add_game_object(self: &mut Self, game_object: GameObject) -> usize { self.game_data.game_objects.push(game_object); self.game_data.game_objects.len() - 1 @@ -523,9 +578,10 @@ fn create_pipeline(device: Arc, re .vertex_shader(vertex_shader_entry.clone(), ()) .triangle_list() .viewports_dynamic_scissors_irrelevant(1) - .depth_stencil_simple_depth() - .cull_mode_back() .fragment_shader(fragment_shader_entry.clone(), ()) + .depth_stencil_simple_depth() + .blend_alpha_blending() + .cull_mode_back() .render_pass(sub_pass.clone()) .build(device.clone()) .unwrap()); @@ -538,8 +594,14 @@ fn create_pipeline(device: Arc, re } impl GameObject { - pub fn new(mesh: MeshHandle) -> GameObject { - GameObject { mesh_index: mesh, model_matrix: Matrix4::identity() } + pub fn new(mesh: MeshHandle, texture_index: usize, renderer: &VulkanRenderer) -> GameObject { + println!("Texid: {}", texture_index); + let descriptor_set = Arc::new(PersistentDescriptorSet::start(renderer.pipeline.clone(), 0) + .add_sampled_image(renderer.game_data.textures[texture_index].clone(), renderer.sampler.clone()).unwrap() + .build().unwrap() + ); + + GameObject { mesh_index: mesh, texture_index, descriptor_set, model_matrix: Matrix4::identity() } } }