diff --git a/Cargo.lock b/Cargo.lock index b49ad8f..7a6ab2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "ahash" version = "0.7.6" @@ -84,6 +90,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + [[package]] name = "bit-set" version = "0.5.3" @@ -438,6 +450,16 @@ dependencies = [ "syn", ] +[[package]] +name = "deflate" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +dependencies = [ + "adler32", + "byteorder", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -488,7 +510,7 @@ dependencies = [ "flume", "half", "lebe", - "miniz_oxide", + "miniz_oxide 0.6.2", "smallvec", "threadpool", ] @@ -500,7 +522,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.6.2", ] [[package]] @@ -605,6 +627,43 @@ dependencies = [ "web-sys", ] +[[package]] +name = "gltf" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00e0a0eace786193fc83644907097285396360e9e82e30f81a21e9b1ba836a3e" +dependencies = [ + "base64", + "byteorder", + "gltf-json", + "image 0.23.14", + "lazy_static", +] + +[[package]] +name = "gltf-derive" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd53d6e284bb2bf02a6926e4cc4984978c1990914d6cd9deae4e31cf37cd113" +dependencies = [ + "inflections", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "gltf-json" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9949836a9ec5e7f83f76fb9bbcbc77f254a577ebbdb0820867bc11979ef97cad" +dependencies = [ + "gltf-derive", + "serde", + "serde_derive", + "serde_json", +] + [[package]] name = "gpu-alloc" version = "0.5.3" @@ -698,6 +757,22 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "image" +version = "0.23.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "jpeg-decoder 0.1.22", + "num-iter", + "num-rational 0.3.2", + "num-traits", + "png 0.16.8", +] + [[package]] name = "image" version = "0.24.5" @@ -709,10 +784,10 @@ dependencies = [ "color_quant", "exr", "gif", - "jpeg-decoder", - "num-rational", + "jpeg-decoder 0.3.0", + "num-rational 0.4.1", "num-traits", - "png", + "png 0.17.7", "scoped_threadpool", "tiff", ] @@ -727,6 +802,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inflections" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" + [[package]] name = "inplace_it" version = "0.3.5" @@ -745,12 +826,24 @@ dependencies = [ "web-sys", ] +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + [[package]] name = "jni-sys" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jpeg-decoder" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" + [[package]] name = "jpeg-decoder" version = "0.3.0" @@ -889,6 +982,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + [[package]] name = "miniz_oxide" version = "0.6.2" @@ -1037,6 +1139,28 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-rational" version = "0.4.1" @@ -1193,6 +1317,18 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "miniz_oxide 0.3.7", +] + [[package]] name = "png" version = "0.17.7" @@ -1202,7 +1338,7 @@ dependencies = [ "bitflags", "crc32fast", "flate2", - "miniz_oxide", + "miniz_oxide 0.6.2", ] [[package]] @@ -1325,7 +1461,8 @@ dependencies = [ "env_logger", "fs_extra", "glob", - "image", + "gltf", + "image 0.24.5", "log", "pollster", "tobj", @@ -1339,6 +1476,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + [[package]] name = "scoped-tls" version = "1.0.1" @@ -1363,6 +1506,28 @@ version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "slotmap" version = "1.0.6" @@ -1478,7 +1643,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471" dependencies = [ "flate2", - "jpeg-decoder", + "jpeg-decoder 0.3.0", "weezl", ] diff --git a/Cargo.toml b/Cargo.toml index 53021fe..3e7ab50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ image = { version = "0.24", features = [ "png", "tga" ] } anyhow = "1.0" cgmath = "0.18" tobj = { version = "3.2.1", features = [ "async", ] } +gltf = "1.0.0" [build-dependencies] anyhow = "1.0" diff --git a/src/core/camera.rs b/src/core/camera.rs index 1f0ec8a..3406cf6 100644 --- a/src/core/camera.rs +++ b/src/core/camera.rs @@ -89,10 +89,10 @@ impl Camera { self.position += right * (controller.move_right - controller.move_left) * controller.speed * dt; self.position += up * (controller.move_up - controller.move_down) * controller.speed * dt; - println!( - "camera pos ({}, {}, {})", - self.position.x, self.position.y, self.position.z - ); + // println!( + // "camera pos ({}, {}, {})", + // self.position.x, self.position.y, self.position.z + // ); } } diff --git a/src/core/light.rs b/src/core/light.rs index f3cc9de..4f7b1fb 100644 --- a/src/core/light.rs +++ b/src/core/light.rs @@ -7,17 +7,15 @@ use super::model::{Mesh, Model}; pub struct LightUniform { pub position: [f32; 3], _padding: u32, - pub color: [f32; 3], - _padding2: u32, + pub color: [f32; 4], } impl LightUniform { - pub fn new(position: [f32; 3], color: [f32; 3]) -> Self { + pub fn new(position: [f32; 3], color: [f32; 4]) -> Self { return LightUniform { position: position, _padding: 0, color: color, - _padding2: 0, }; } } diff --git a/src/core/model.rs b/src/core/model.rs index 1e09a77..ba91138 100644 --- a/src/core/model.rs +++ b/src/core/model.rs @@ -44,9 +44,9 @@ impl Material { return Self { name: String::from(name), - diffuse_texture: diffuse_texture, - normal_texture: normal_texture, - bind_group: bind_group, + diffuse_texture, + normal_texture, + bind_group, }; } } @@ -69,12 +69,12 @@ pub trait Vertex { } #[repr(C)] -#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] +#[derive(Copy, Clone, Debug, Default, bytemuck::Pod, bytemuck::Zeroable)] pub struct ModelVertex { pub position: [f32; 3], pub tex_coords: [f32; 2], pub normal: [f32; 3], - pub tangent: [f32; 3], + pub tangent: [f32; 4], pub bitangent: [f32; 3], } diff --git a/src/core/resources.rs b/src/core/resources.rs index 11a5f08..21b149c 100644 --- a/src/core/resources.rs +++ b/src/core/resources.rs @@ -1,22 +1,27 @@ use std::io::{BufReader, Cursor}; +use std::path::PathBuf; use wgpu::util::DeviceExt; use crate::core::model::{Material, Mesh, Model, ModelVertex}; use crate::core::texture::Texture; -pub async fn load_string(file_name: &str) -> anyhow::Result { - let path = std::path::Path::new(env!("OUT_DIR")) +pub fn get_resource_path(file_name: &str) -> PathBuf { + return std::path::Path::new(env!("OUT_DIR")) .join("res") .join(file_name); +} + +pub async fn load_string(file_name: &str) -> anyhow::Result { + let path = get_resource_path(file_name); + println!("load_string: Loading from {:?}", path.to_str()); let txt = std::fs::read_to_string(path)?; return Ok(txt); } pub async fn load_binary(file_name: &str) -> anyhow::Result> { - let path = std::path::Path::new(env!("OUT_DIR")) - .join("res") - .join(file_name); + let path = get_resource_path(file_name); + println!("load_binary: Loading from {:?}", path.to_str()); let data = std::fs::read(path)?; return Ok(data); @@ -33,7 +38,7 @@ pub async fn load_texture( return Texture::from_bytes(device, queue, &data, file_name, is_normal_map); } -pub async fn load_model( +pub async fn load_model_obj( file_name: &str, device: &wgpu::Device, queue: &wgpu::Queue, @@ -102,7 +107,7 @@ pub async fn load_model( m.mesh.normals[i * 3 + 1], m.mesh.normals[i * 3 + 2], ], - tangent: [0.0; 3], + tangent: [0.0; 4], bitangent: [0.0; 3], }) .collect::>(); @@ -137,7 +142,9 @@ pub async fn load_model( for i in 0..3 { let sz = chunk[i] as usize; vertices[sz].tangent = - (tangent + cgmath::Vector3::from(vertices[sz].tangent)).into(); + (cgmath::Vector4::new(tangent.x, tangent.y, tangent.z, 0.0) + + cgmath::Vector4::from(vertices[sz].tangent)) + .into(); vertices[sz].bitangent = (bitangent + cgmath::Vector3::from(vertices[sz].bitangent)).into(); triangles_included[sz] += 1; @@ -148,7 +155,7 @@ pub async fn load_model( for (i, n) in triangles_included.into_iter().enumerate() { let denom = 1.0 / n as f32; let mut v = &mut vertices[i]; - v.tangent = (cgmath::Vector3::from(v.tangent) * denom).into(); + v.tangent = (cgmath::Vector4::from(v.tangent) * denom).into(); v.bitangent = (cgmath::Vector3::from(v.bitangent) * denom).into(); } @@ -175,3 +182,283 @@ pub async fn load_model( return Ok(Model { meshes, materials }); } + +pub async fn load_model_gltf( + file_name: &str, + device: &wgpu::Device, + queue: &wgpu::Queue, + layout: &wgpu::BindGroupLayout, +) -> anyhow::Result { + let mut materials = Vec::new(); + let mut meshes = Vec::new(); + + println!("gltf: Loading file {}", file_name); + let (document, buffers, mut images) = gltf::import(get_resource_path(file_name))?; + + println!("gltf: Loading meshes"); + for mesh in document.meshes() { + let primitives = mesh.primitives(); + primitives.for_each(|primitive| { + let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()])); + + let mut vertices = Vec::new(); + let mut indices = Vec::new(); + + if let Some(vertex_attribute) = reader.read_positions() { + vertex_attribute.for_each(|vertex| { + // dbg!(vertex); + vertices.push(ModelVertex { + position: vertex, + ..Default::default() + }) + }); + } else { + panic!(); + } + + if let Some(normal_attribute) = reader.read_normals() { + let mut normal_index = 0; + normal_attribute.for_each(|normal| { + // dbg!(normal); + vertices[normal_index].normal = normal; + normal_index += 1; + }); + } else { + panic!(); + } + + // if let Some(tangent_attribute) = reader.read_tangents() { + // let mut tangent_index = 0; + // tangent_attribute.for_each(|tangent| { + // // dbg!(tangent); + // vertices[tangent_index].tangent = tangent; + // tangent_index += 1; + // }); + // } + + // tangents and bitangents from triangles + let mut triangles_included = vec![0; vertices.len()]; + for chunk in indices.chunks(3) { + let v0 = vertices[chunk[0] as usize]; + let v1 = vertices[chunk[1] as usize]; + let v2 = vertices[chunk[2] as usize]; + + let pos0: cgmath::Vector3 = v0.position.into(); + let pos1: cgmath::Vector3 = v1.position.into(); + let pos2: cgmath::Vector3 = v2.position.into(); + + let uv0: cgmath::Vector2 = v0.tex_coords.into(); + let uv1: cgmath::Vector2 = v1.tex_coords.into(); + let uv2: cgmath::Vector2 = v2.tex_coords.into(); + + let delta_pos1 = pos1 - pos0; + let delta_pos2 = pos2 - pos0; + + let delta_uv1 = uv1 - uv0; + let delta_uv2 = uv2 - uv0; + + let r = 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x); + let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r; + let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * -r; + + for i in 0..3 { + let sz = chunk[i] as usize; + vertices[sz].tangent = + (cgmath::Vector4::new(tangent.x, tangent.y, tangent.z, 0.0) + + cgmath::Vector4::from(vertices[sz].tangent)) + .into(); + vertices[sz].bitangent = + (bitangent + cgmath::Vector3::from(vertices[sz].bitangent)).into(); + triangles_included[sz] += 1; + } + } + + // Average the tangents/bitangents + for (i, n) in triangles_included.into_iter().enumerate() { + let denom = 1.0 / n as f32; + let mut v = &mut vertices[i]; + v.tangent = (cgmath::Vector4::from(v.tangent) * denom).into(); + v.bitangent = (cgmath::Vector3::from(v.bitangent) * denom).into(); + } + + if let Some(tex_coord_attribute) = reader.read_tex_coords(0).map(|v| v.into_f32()) { + let mut tex_coord_index = 0; + tex_coord_attribute.for_each(|tex_coord| { + // dbg!(tex_coord); + vertices[tex_coord_index].tex_coords = tex_coord; + tex_coord_index += 1; + }); + } else { + panic!(); + } + + if let Some(indices_raw) = reader.read_indices() { + // dbg!(indices_raw); + indices.append(&mut indices_raw.into_u32().collect::>()); + } else { + panic!(); + } + // dbg!(indices); + + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some(&format!("{:?} Vertex Buffer", file_name)), + contents: bytemuck::cast_slice(&vertices), + usage: wgpu::BufferUsages::VERTEX, + }); + let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some(&format!("{:?} Index Buffer", file_name)), + contents: bytemuck::cast_slice(&indices), + usage: wgpu::BufferUsages::INDEX, + }); + + meshes.push(Mesh { + name: file_name.to_string(), + vertex_buffer, + index_buffer, + num_elements: indices.len() as u32, + material: primitive.material().index().unwrap_or(0), + }); + }); + } + + println!("gltf: Loading materials"); + for material in document.materials() { + let pbr = material.pbr_metallic_roughness(); + + // diffuse + let diffuse_index = pbr + .base_color_texture() + .map(|tex| { + println!("Grabbing diffuse tex"); + tex.texture().source().index() + }) + .unwrap_or(0); // TODO default tex + + let diffuse_data = &mut images[diffuse_index]; + + if diffuse_data.format == gltf::image::Format::R8G8B8 + || diffuse_data.format == gltf::image::Format::R16G16B16 + { + diffuse_data.pixels = + gltf_pixels_to_wgpu(diffuse_data.pixels.clone(), diffuse_data.format); + } + + let diffuse_texture = Texture::from_pixels( + device, + queue, + &diffuse_data.pixels, + (diffuse_data.width, diffuse_data.height), + gltf_image_format_stride(diffuse_data.format), + gltf_image_format_to_wgpu(diffuse_data.format, false), + Some(file_name), + ) + .unwrap(); + + // normal + let normal_index = material + .normal_texture() + .map(|tex| { + println!("Grabbing normal tex"); + tex.texture().source().index() + }) + .unwrap_or(0); // TODO default tex + + let normal_data = &mut images[normal_index]; + + if normal_data.format == gltf::image::Format::R8G8B8 + || normal_data.format == gltf::image::Format::R16G16B16 + { + normal_data.pixels = + gltf_pixels_to_wgpu(normal_data.pixels.clone(), normal_data.format); + } + + let normal_texture = Texture::from_pixels( + device, + queue, + &normal_data.pixels, + (normal_data.width, normal_data.height), + gltf_image_format_stride(normal_data.format), + gltf_image_format_to_wgpu(normal_data.format, true), + Some(file_name), + ) + .unwrap(); + + materials.push(Material::new( + device, + &material.name().unwrap_or("Default Material").to_string(), + diffuse_texture, + normal_texture, + layout, + )); + } + + println!("gltf: load done!"); + + Ok(Model { meshes, materials }) +} + +fn gltf_image_format_to_wgpu(format: gltf::image::Format, srgb: bool) -> wgpu::TextureFormat { + if srgb { + return match format { + gltf::image::Format::R8 => panic!(), + gltf::image::Format::R8G8 => panic!(), + gltf::image::Format::R8G8B8 => wgpu::TextureFormat::Rgba8UnormSrgb, // converted + gltf::image::Format::R8G8B8A8 => wgpu::TextureFormat::Rgba8UnormSrgb, + gltf::image::Format::B8G8R8 => wgpu::TextureFormat::Bgra8UnormSrgb, + gltf::image::Format::B8G8R8A8 => wgpu::TextureFormat::Bgra8UnormSrgb, + gltf::image::Format::R16 => panic!(), + gltf::image::Format::R16G16 => panic!(), + gltf::image::Format::R16G16B16 => panic!(), // converted + gltf::image::Format::R16G16B16A16 => panic!(), + }; + } + + match format { + gltf::image::Format::R8 => wgpu::TextureFormat::R8Unorm, + gltf::image::Format::R8G8 => wgpu::TextureFormat::Rg8Unorm, + gltf::image::Format::R8G8B8 => wgpu::TextureFormat::Rgba8Unorm, // converted + gltf::image::Format::R8G8B8A8 => wgpu::TextureFormat::Rgba8Unorm, + gltf::image::Format::B8G8R8 => wgpu::TextureFormat::Bgra8Unorm, + gltf::image::Format::B8G8R8A8 => wgpu::TextureFormat::Bgra8Unorm, + gltf::image::Format::R16 => wgpu::TextureFormat::R16Unorm, + gltf::image::Format::R16G16 => wgpu::TextureFormat::Rg16Unorm, + gltf::image::Format::R16G16B16 => wgpu::TextureFormat::Rgba16Unorm, // converted + gltf::image::Format::R16G16B16A16 => wgpu::TextureFormat::Rgba16Unorm, + } +} + +fn gltf_image_format_stride(format: gltf::image::Format) -> u32 { + match format { + gltf::image::Format::R8 => 1, + gltf::image::Format::R8G8 => 2, + gltf::image::Format::R8G8B8 => 4, // converted + gltf::image::Format::R8G8B8A8 => 4, + gltf::image::Format::B8G8R8 => 3, + gltf::image::Format::B8G8R8A8 => 4, + gltf::image::Format::R16 => 2, + gltf::image::Format::R16G16 => 4, + gltf::image::Format::R16G16B16 => 8, // converted + gltf::image::Format::R16G16B16A16 => 8, + } +} + +// Add alpha if needed +fn gltf_pixels_to_wgpu(mut bytes: Vec, format: gltf::image::Format) -> Vec { + if format == gltf::image::Format::R8G8B8 { + let pixels = bytes.len() / 3; + bytes.reserve_exact(pixels); + bytes = bytes + .chunks_exact(3) + .flat_map(|s| [s[0], s[1], s[2], 255]) + .collect(); + } else if format == gltf::image::Format::R16G16B16 { + let pixels = bytes.len() / 6; + bytes.reserve_exact(pixels); + bytes = bytes + .chunks_exact(6) + .flat_map(|s| [s[0], s[1], s[2], s[3], s[4], s[5], 255, 255]) + .collect(); + } + + return bytes; +} diff --git a/src/core/state.rs b/src/core/state.rs index 3017314..5ad9473 100644 --- a/src/core/state.rs +++ b/src/core/state.rs @@ -29,7 +29,7 @@ pub struct State { instances: Vec, instance_buffer: wgpu::Buffer, depth_texture: Texture, - obj_model: Model, + model: Model, light_uniform: LightUniform, light_buffer: wgpu::Buffer, light_render_pipeline: wgpu::RenderPipeline, @@ -76,7 +76,7 @@ impl State { // Camera let camera = Camera::new( - (0.0, 4.0, -4.0).into(), + (0.0, 0.0, 0.0).into(), 0.0, 0.0, 60.0, @@ -116,7 +116,7 @@ impl State { let camera_controller = CameraController::new(1.0, 2.0); - let light_uniform = LightUniform::new([900.0, 60.0, 200.0], [1.0, 1.0, 1.0]); + let light_uniform = LightUniform::new([100.0, 60.0, 0.0], [1.0, 1.0, 1.0, 10000.0]); // We'll want to update our lights position, so we use COPY_DST let light_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { @@ -193,7 +193,7 @@ impl State { }); let obj_model = - resources::load_model("sponza.obj", &device, &queue, &texture_bind_group_layout) + resources::load_model_gltf("sponza/Sponza.gltf", &device, &queue, &texture_bind_group_layout) .await .unwrap(); @@ -300,7 +300,7 @@ impl State { instances, instance_buffer, depth_texture, - obj_model, + model: obj_model, light_uniform, light_buffer, light_render_pipeline, @@ -346,7 +346,7 @@ impl State { // Update the light let old_position: cgmath::Vector3<_> = self.light_uniform.position.into(); self.light_uniform.position = - (cgmath::Quaternion::from_angle_y(cgmath::Deg(1.0)) * old_position).into(); + (cgmath::Quaternion::from_angle_y(cgmath::Deg(90.0 * dt.as_secs_f32())) * old_position).into(); self.queue.write_buffer( &self.light_buffer, 0, @@ -397,14 +397,14 @@ impl State { render_pass.set_pipeline(&self.light_render_pipeline); render_pass.draw_light_model( - &self.obj_model, + &self.model, &self.camera_bind_group, &self.light_bind_group, ); render_pass.set_pipeline(&self.render_pipeline); render_pass.draw_model_instanced( - &self.obj_model, + &self.model, 0..self.instances.len() as u32, &self.camera_bind_group, &self.light_bind_group, diff --git a/src/core/texture.rs b/src/core/texture.rs index 719c772..ec4a35b 100644 --- a/src/core/texture.rs +++ b/src/core/texture.rs @@ -72,7 +72,30 @@ impl Texture { ) -> Result { let rgba = img.to_rgba8(); let dimensions = img.dimensions(); + return Self::from_pixels( + device, + queue, + &rgba.to_vec(), + dimensions, + 4, + if is_normal_map { + wgpu::TextureFormat::Rgba8UnormSrgb + } else { + wgpu::TextureFormat::Rgba8Unorm + }, + label, + ); + } + pub fn from_pixels( + device: &wgpu::Device, + queue: &wgpu::Queue, + pixels: &[u8], + dimensions: (u32, u32), + stride: u32, + format: wgpu::TextureFormat, + label: Option<&str>, + ) -> Result { let size = wgpu::Extent3d { width: dimensions.0, height: dimensions.1, @@ -84,14 +107,12 @@ impl Texture { mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, - format: if is_normal_map { - wgpu::TextureFormat::Rgba8Unorm - } else { - wgpu::TextureFormat::Rgba8UnormSrgb - }, + format: format, usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, }); + dbg!(stride); + dbg!(dimensions); queue.write_texture( wgpu::ImageCopyTexture { aspect: wgpu::TextureAspect::All, @@ -99,10 +120,10 @@ impl Texture { mip_level: 0, origin: wgpu::Origin3d::ZERO, }, - &rgba, + &pixels, wgpu::ImageDataLayout { offset: 0, - bytes_per_row: std::num::NonZeroU32::new(4 * dimensions.0), + bytes_per_row: std::num::NonZeroU32::new(stride * dimensions.0), rows_per_image: std::num::NonZeroU32::new(dimensions.1), }, size, diff --git a/src/core/updater.rs b/src/core/updater.rs index 70f15a0..9328c1a 100644 --- a/src/core/updater.rs +++ b/src/core/updater.rs @@ -10,9 +10,6 @@ use winit::{ pub async fn run() { let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); - window.set_cursor_grab(true).unwrap(); - window.set_cursor_visible(false); - window.set_decorations(false); let mut state = State::new(&window).await; let mut last_render = Instant::now(); @@ -44,6 +41,11 @@ pub async fn run() { WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { state.resize(**new_inner_size); } + WindowEvent::Focused(focused) => { + // window.set_cursor_grab(*focused).unwrap(); + window.set_cursor_visible(!*focused); + window.set_decorations(!*focused); + } _ => {} } } diff --git a/src/shaders/test.wgsl b/src/shaders/test.wgsl index c30b700..3deb838 100644 --- a/src/shaders/test.wgsl +++ b/src/shaders/test.wgsl @@ -10,7 +10,7 @@ var camera: CameraUniform; struct Light { position: vec3, - color: vec3, + color: vec4, } @group(2) @binding(0) var light: Light; @@ -36,6 +36,8 @@ struct VertexOutput { @location(1) tangent_position: vec3, @location(2) tangent_light_position: vec3, @location(3) tangent_view_position: vec3, + @location(4) world_position: vec3, + @location(5) world_normal: vec3, } @vertex @@ -67,6 +69,10 @@ fn vs_main( out.tangent_position = tangent_matrix * world_position.xyz; out.tangent_light_position = tangent_matrix * light.position; out.tangent_view_position = tangent_matrix * camera.position.xyz; + + out.world_normal = world_normal.xyz; + out.world_position = world_position.xyz; + return out; } @@ -82,6 +88,7 @@ var t_normal: texture_2d; @group(0) @binding(3) var s_normal: sampler; +// TODO: fix using tangent space and normal texture instead of world @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { let object_color: vec4 = textureSample(t_diffuse, s_diffuse, in.tex_coords); @@ -89,21 +96,30 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { // lighting vecs let tangent_normal = object_normal.xyz * 2.0 - 1.0; - let light_dir = normalize(in.tangent_light_position - in.tangent_position); - let view_dir = normalize(in.tangent_view_position - in.tangent_position); + // let light_dir = normalize(in.tangent_light_position - in.tangent_position); + var light_dir = light.position - in.world_position; + let light_dist = length(light_dir); + light_dir = normalize(light_dir); + let coef_a = 0.0; + let coef_b = 1.25; + let light_attenuation = 1.0 / (1.0 + coef_a * light_dist + coef_b * light_dist * light_dist); + // let view_dir = normalize(in.tangent_view_position - in.tangent_position); + let view_dir = normalize(camera.position.xyz - in.world_position); let half_dir = normalize(view_dir + light_dir); // ambient let ambient_strength = 0.025; - let ambient_color = light.color * ambient_strength; + let ambient_color = vec3(1.0) * ambient_strength; // diffuse - let diffuse_strength = max(dot(tangent_normal, light_dir), 0.0); - let diffuse_color = light.color * diffuse_strength; + // let diffuse_strength = max(dot(tangent_normal, light_dir), 0.0); + let diffuse_strength = max(dot(in.world_normal, light_dir), 0.0); + let diffuse_color = diffuse_strength * light.color.xyz * light.color.w * light_attenuation; // specular - let specular_strength = pow(max(dot(tangent_normal, half_dir), 0.0), 32.0); - let specular_color = specular_strength * light.color; + // let specular_strength = pow(max(dot(tangent_normal, half_dir), 0.0), 32.0); + let specular_strength = pow(max(dot(in.world_normal, half_dir), 0.0), 32.0); + let specular_color = specular_strength * light.color.xyz * light.color.w * light_attenuation; let result = (ambient_color + diffuse_color + specular_color) * object_color.xyz;