diff --git a/engine/BUILD b/engine/BUILD index 77cb81c..6d60bce 100644 --- a/engine/BUILD +++ b/engine/BUILD @@ -11,6 +11,8 @@ rust_binary( ], srcs = [ "src/main.rs", + "src/physics/mod.rs", + "src/physics/color.rs", "src/render/mod.rs", "src/render/renderable.rs", "src/render/vulkan/data.rs", diff --git a/engine/shaders/forward.frag b/engine/shaders/forward.frag index 6b4673e..885c817 100644 --- a/engine/shaders/forward.frag +++ b/engine/shaders/forward.frag @@ -3,13 +3,9 @@ #extension GL_ARB_separate_shader_objects : enable layout(binding = 0) uniform sampler2D texSampler; - -layout(location = 0) in vec3 fragColor; -layout(location = 1) in vec2 fragTexCoord; - +layout(location = 0) in vec2 fragTexCoord; layout(location = 0) out vec4 outColor; void main() { - //outColor = vec4(fragColor, 1.0); outColor = texture(texSampler, fragTexCoord); } diff --git a/engine/shaders/forward.vert b/engine/shaders/forward.vert index 10b8572..e95d14c 100644 --- a/engine/shaders/forward.vert +++ b/engine/shaders/forward.vert @@ -6,12 +6,10 @@ layout(push_constant) uniform UniformBufferObject { } ubo; layout(location = 0) in vec3 pos; -layout(location = 1) in vec3 color; -layout(location = 2) in mat4 model; -layout(location = 6) in vec2 tex; +layout(location = 1) in mat4 model; +layout(location = 5) in vec2 tex; -layout(location = 0) out vec3 fragColor; -layout(location = 1) out vec2 fragTexCoord; +layout(location = 0) out vec2 fragTexCoord; out gl_PerVertex { vec4 gl_Position; @@ -19,7 +17,6 @@ out gl_PerVertex { void main() { gl_Position = ubo.view * model * vec4(pos, 1.0); - fragColor = color; fragTexCoord = tex; diff --git a/engine/src/main.rs b/engine/src/main.rs index 0ebb6c0..544604d 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -7,9 +7,11 @@ use cgmath as cgm; mod render; mod util; +mod physics; use render::vulkan::data; -use render::renderable::{Object, Renderable, Resource, ResourceManager, Texture, Mesh}; +use render::renderable::{Object, Renderable, Resource, ResourceManager, Material, Mesh, ImageRefOrColor}; +use physics::color; fn main() { env_logger::init(); @@ -64,9 +66,10 @@ fn main() { rm.add(Resource::Mesh(Mesh::new(vertices, indices))) }; - let path = &crate::util::file::resource_path(String::from("assets/test-128px.png")); - let image = Arc::new(image::open(path).unwrap()); - let texture_cube = rm.add(Resource::Texture(Texture::new(image))); + let material_cube = rm.add(Resource::Material(Material::new( + ImageRefOrColor::image(String::from("assets/test-128px.png")), + ImageRefOrColor::color(color::LinearF32::new(1.0)), + ))); let mut renderer = render::Renderer::initialize(); @@ -78,7 +81,7 @@ fn main() { let cube = render::renderable::Object { mesh: mesh_cube, transform: transform, - texture: texture_cube, + material: material_cube, }; cubes.push(Box::new(cube)); } diff --git a/engine/src/physics/color.rs b/engine/src/physics/color.rs new file mode 100644 index 0000000..a8275e8 --- /dev/null +++ b/engine/src/physics/color.rs @@ -0,0 +1,40 @@ +#[derive(Copy, Clone, Debug)] +pub struct XYZ { + pub x: f32, + pub y: f32, + pub z: f32, +} + +impl XYZ { + pub fn new(x: f32, y: f32, z: f32) -> Self { + Self { + x, y, z, + } + } +} + +pub struct sRGB { + pub r: u8, + pub g: u8, + pub b: u8, +} + +impl sRGB { + pub fn new(r: u8, g: u8, b: u8) -> Self { + Self { + r, g, b, + } + } +} + +pub struct LinearF32 { + pub d: f32, +} + +impl LinearF32 { + pub fn new(d: f32) -> Self { + Self { + d, + } + } +} diff --git a/engine/src/physics/mod.rs b/engine/src/physics/mod.rs new file mode 100644 index 0000000..2c53cf0 --- /dev/null +++ b/engine/src/physics/mod.rs @@ -0,0 +1 @@ +pub mod color; diff --git a/engine/src/render/renderable.rs b/engine/src/render/renderable.rs index 3ebed31..f72c172 100644 --- a/engine/src/render/renderable.rs +++ b/engine/src/render/renderable.rs @@ -14,22 +14,24 @@ use vulkano::format as vf; use vulkano::image as vm; use crate::render::vulkan::data; +use crate::physics::color; +use crate::util::file; pub struct ResourceManager { meshes: HashMap, - textures: HashMap, + materials: HashMap, } #[derive(Copy, Clone)] pub enum ResourceID { - Texture(u64), + Material(u64), Mesh(u64), } impl hash::Hash for ResourceID { fn hash(&self, state: &mut H) { match self { - ResourceID::Texture(i) => i.hash(state), + ResourceID::Material(i) => i.hash(state), ResourceID::Mesh(i) => i.hash(state), } } @@ -38,11 +40,11 @@ impl hash::Hash for ResourceID { impl PartialEq for ResourceID { fn eq(&self, other: &Self) -> bool { let this = match self { - ResourceID::Texture(i) => i, + ResourceID::Material(i) => i, ResourceID::Mesh(i) => i, }; let that = match other { - ResourceID::Texture(i) => i, + ResourceID::Material(i) => i, ResourceID::Mesh(i) => i, }; this == that @@ -52,7 +54,7 @@ impl PartialEq for ResourceID { impl Eq for ResourceID {} pub enum Resource { - Texture(Texture), + Material(Material), Mesh(Mesh), } @@ -60,16 +62,16 @@ impl<'a> ResourceManager { pub fn new() -> Self { Self { meshes: HashMap::new(), - textures: HashMap::new(), + materials: HashMap::new(), } } pub fn add(&mut self, r: Resource) -> ResourceID { match r { - Resource::Texture(t) => { + Resource::Material(t) => { let id = t.id; - self.textures.insert(id, t); - ResourceID::Texture(id) + self.materials.insert(id, t); + ResourceID::Material(id) } Resource::Mesh(t) => { let id = t.id; @@ -79,9 +81,9 @@ impl<'a> ResourceManager { } } - pub fn texture(&'a self, id: &ResourceID) -> Option<&'a Texture> { - if let ResourceID::Texture(i) = id { - return Some(self.textures.get(&i).unwrap()); + pub fn material(&'a self, id: &ResourceID) -> Option<&'a Material> { + if let ResourceID::Material(i) = id { + return Some(self.materials.get(&i).unwrap()); } return None } @@ -94,47 +96,169 @@ impl<'a> ResourceManager { } } -pub struct Texture { - image: Arc, +pub trait ChannelLayout { + fn vulkan_from_image( + image: Arc, + graphics_queue: Arc, + ) -> Arc>; + + fn vulkan_from_value( + &self, + graphics_queue: Arc, + ) -> Arc>; +} + +impl ChannelLayout for color::XYZ { + fn vulkan_from_image( + image: Arc, + graphics_queue: Arc, + ) -> Arc> { + let (width, height) = (image.width(), image.height()); + let rgba = image.to_rgba(); + // TODO(q3k): RGB -> CIE XYZ + let (image_view, future) = vm::ImmutableImage::from_iter( + rgba.into_raw().iter().cloned(), + vm::Dimensions::Dim2d{ width, height }, + vf::Format::R8G8B8A8Unorm, + graphics_queue.clone(), + ).unwrap(); + + future.flush().unwrap(); + image_view + } + + fn vulkan_from_value( + &self, + graphics_queue: Arc, + ) -> Arc> { + let mut image = image::ImageBuffer::, Vec>::new(1, 1); + image.put_pixel(0, 0, image::Rgba([self.x, self.y, self.z, 0.0])); + + let (image_view, future) = vm::ImmutableImage::from_iter( + image.into_raw().iter().cloned(), + vm::Dimensions::Dim2d{ width: 1, height: 1 }, + vf::Format::R32G32B32A32Sfloat, + graphics_queue.clone(), + ).unwrap(); + future.flush().unwrap(); + image_view + } +} + +impl ChannelLayout for color::LinearF32 { + fn vulkan_from_image( + image: Arc, + graphics_queue: Arc, + ) -> Arc> { + let (width, height) = (image.width(), image.height()); + assert!(match image.color() { + image::ColorType::L8 => true, + image::ColorType::L16 => true, + _ => false, + }, "linearf32 texture must be 8-bit grayscale"); + let gray = image.to_luma(); + let (image_view, future) = vm::ImmutableImage::from_iter( + gray.into_raw().iter().cloned(), + vm::Dimensions::Dim2d{ width, height }, + vf::Format::R8G8B8A8Unorm, + graphics_queue.clone(), + ).unwrap(); + + future.flush().unwrap(); + image_view + } + + fn vulkan_from_value( + &self, + graphics_queue: Arc, + ) -> Arc> { + let mut image = image::ImageBuffer::, Vec>::new(1, 1); + image.put_pixel(0, 0, image::Luma([self.d])); + + let (image_view, future) = vm::ImmutableImage::from_iter( + image.into_raw().iter().cloned(), + vm::Dimensions::Dim2d{ width: 1, height: 1 }, + vf::Format::R32Sfloat, + graphics_queue.clone(), + ).unwrap(); + + future.flush().unwrap(); + image_view + } +} + +pub enum ImageRefOrColor { + Color(T), + ImageRef(ImageRef), +} + +impl ImageRefOrColor { + fn vulkan_image(&self, graphics_queue: Arc) -> Arc> { + match self { + ImageRefOrColor::::Color(c) => c.vulkan_from_value(graphics_queue), + ImageRefOrColor::::ImageRef(r) => T::vulkan_from_image(r.load(), graphics_queue), + } + } + + pub fn color(color: T) -> Self { + ImageRefOrColor::::Color(color) + } + + pub fn image(name: String) -> Self { + ImageRefOrColor::::ImageRef(ImageRef{ name }) + } +} + +pub struct ImageRef { + name: String, +} + +impl ImageRef { + fn load (&self) -> Arc { + let path = &file::resource_path(self.name.clone()); + Arc::new(image::open(path).unwrap()) + } +} + +pub struct Material { + diffuse: ImageRefOrColor, + roughness: ImageRefOrColor, id: u64, // vulkan cache - vulkan: Mutex>>>, + vulkan: Mutex>, } -impl Texture { + +impl Material { pub fn new( - image: Arc, + diffuse: ImageRefOrColor, + roughness: ImageRefOrColor, ) -> Self { Self { - image, + diffuse, + roughness, + // TODO: use a better method id: time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_nanos() as u64, vulkan: Mutex::new(None), } } - pub fn vulkan_texture( + + pub fn vulkan_textures( &self, graphics_queue: Arc, - ) -> Arc> { + ) -> data::Textures { let mut cache = self.vulkan.lock().unwrap(); match &mut *cache { Some(data) => data.clone(), None => { - let width = self.image.width(); - let height = self.image.height(); - let image_rgba = self.image.to_rgba(); - let (image_view, future) = vm::ImmutableImage::from_iter( - image_rgba.into_raw().iter().cloned(), - vm::Dimensions::Dim2d{ width, height }, - vf::Format::R8G8B8A8Unorm, - graphics_queue.clone(), - ).unwrap(); - - future.flush().unwrap(); - - *cache = Some(image_view.clone()); - - image_view + let diffuse = self.diffuse.vulkan_image(graphics_queue.clone()); + let roughness = self.roughness.vulkan_image(graphics_queue.clone()); + let textures = data::Textures { + diffuse, roughness, + }; + *cache = Some(textures.clone()); + textures }, } } @@ -226,12 +350,12 @@ pub trait Renderable { pub struct Object { pub mesh: ResourceID, - pub texture: ResourceID, + pub material: ResourceID, pub transform: cgm::Matrix4, } impl Renderable for Object { fn render_data(&self) -> Option<(ResourceID, ResourceID, &cgm::Matrix4)> { - Some((self.mesh, self.texture, &self.transform)) + Some((self.mesh, self.material, &self.transform)) } } diff --git a/engine/src/render/vulkan/data.rs b/engine/src/render/vulkan/data.rs index 3b2df09..b4c2dfc 100644 --- a/engine/src/render/vulkan/data.rs +++ b/engine/src/render/vulkan/data.rs @@ -1,3 +1,8 @@ +use std::sync::Arc; + +use vulkano::image as vm; +use vulkano::format::Format; + use cgmath as cgm; #[derive(Default, Copy, Clone)] @@ -35,3 +40,11 @@ vulkano::impl_vertex!(Instance, model); pub struct UniformBufferObject { pub view: cgm::Matrix4, } + +#[derive(Clone)] +pub struct Textures { + // diffuse: RGB + pub diffuse: Arc>, + // roughness: R + pub roughness: Arc>, +} diff --git a/engine/src/render/vulkan/mod.rs b/engine/src/render/vulkan/mod.rs index 44d908d..1141e50 100644 --- a/engine/src/render/vulkan/mod.rs +++ b/engine/src/render/vulkan/mod.rs @@ -164,7 +164,7 @@ impl Instance { let mut buffers: Vec> = vec![]; - // Sort renderables by mesh and textureid. + // Sort renderables by mesh and materialid. let mut meshes: ResourceMap<(renderable::ResourceID, renderable::ResourceID), &cgm::Matrix4> = ResourceMap::new(); let ubo = data::UniformBufferObject { @@ -173,8 +173,8 @@ impl Instance { profiler.end("mgc.prep"); for r in renderables { - if let Some((mesh_id, texture_id, transform)) = r.render_data() { - meshes.add((mesh_id, texture_id), transform); + if let Some((mesh_id, material_id, transform)) = r.render_data() { + meshes.add((mesh_id, material_id), transform); } } profiler.end("mgc.sort"); @@ -185,9 +185,9 @@ impl Instance { let pipeline = self.pipeline.as_ref().unwrap().get_pipeline().clone(); - for ((mesh_id, texture_id), transforms) in meshes.resources { + for ((mesh_id, material_id), transforms) in meshes.resources { let mesh = rm.mesh(&mesh_id).unwrap(); - let texture = rm.texture(&texture_id).unwrap(); + let material = rm.material(&material_id).unwrap(); let mut builder = vc::AutoCommandBufferBuilder::secondary_graphics_one_time_submit( device.clone(), queue.family(), vf::Subpass::from(rp.clone(), 0).unwrap()).unwrap(); @@ -199,7 +199,7 @@ impl Instance { ).unwrap(); future.flush().unwrap(); - let image = texture.vulkan_texture(queue.clone()); + let image = material.vulkan_textures(queue.clone()).diffuse; let ds = self.pipeline.as_mut().unwrap().make_descriptor_set(image); let (vbuffer, ibuffer) = mesh.vulkan_buffers(queue.clone()); diff --git a/engine/src/render/vulkan/pipeline_forward.rs b/engine/src/render/vulkan/pipeline_forward.rs index dafdc9a..9f5ac08 100644 --- a/engine/src/render/vulkan/pipeline_forward.rs +++ b/engine/src/render/vulkan/pipeline_forward.rs @@ -38,25 +38,17 @@ impl Forward { name: Some(Cow::Borrowed("pos")), }, vps::ShaderInterfaceDefEntry { - location: 1..2, format: Format::R32G32B32Sfloat, - name: Some(Cow::Borrowed("color")), - }, - vps::ShaderInterfaceDefEntry { - location: 2..6, format: Format::R32G32B32A32Sfloat, + location: 1..5, format: Format::R32G32B32A32Sfloat, name: Some(Cow::Borrowed("model")), }, vps::ShaderInterfaceDefEntry { - location: 6..7, format: Format::R32G32Sfloat, + location: 5..6, format: Format::R32G32Sfloat, name: Some(Cow::Borrowed("tex")), }, ], outputs: vec![ vps::ShaderInterfaceDefEntry { - location: 0..1, format: Format::R32G32B32Sfloat, - name: Some(Cow::Borrowed("fragColor")), - }, - vps::ShaderInterfaceDefEntry { - location: 1..2, format: Format::R32G32Sfloat, + location: 0..1, format: Format::R32G32Sfloat, name: Some(Cow::Borrowed("fragTexCoord")), }, ], @@ -78,11 +70,7 @@ impl Forward { ty: vps::GraphicsShaderType::Fragment, inputs: vec![ vps::ShaderInterfaceDefEntry { - location: 0..1, format: Format::R32G32B32Sfloat, - name: Some(Cow::Borrowed("fragColor")), - }, - vps::ShaderInterfaceDefEntry { - location: 1..2, format: Format::R32G32Sfloat, + location: 0..1, format: Format::R32G32Sfloat, name: Some(Cow::Borrowed("fragTexCoord")), }, ],