diff --git a/crates/renderling-shader/src/array.rs b/crates/renderling-shader/src/array.rs index 6054615d..35591901 100644 --- a/crates/renderling-shader/src/array.rs +++ b/crates/renderling-shader/src/array.rs @@ -6,7 +6,9 @@ use crate::slab::Slabbed; #[repr(C)] pub struct Array { + // u32 offset in the slab index: u32, + // number of `T` elements in the array len: u32, _phantom: PhantomData, } @@ -25,11 +27,15 @@ impl Copy for Array {} impl core::fmt::Debug for Array { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("Array") - .field("index", &self.index) - .field("len", &self.len) - .field("_phantom", &self._phantom) - .finish() + f.debug_struct(if self.is_null() { + "Array (null)" + } else { + "Array" + }) + .field("index", &self.index) + .field("len", &self.len) + .field("_phantom", &self._phantom) + .finish() } } @@ -75,7 +81,7 @@ impl Default for Array { } } -impl Array { +impl Array { pub fn new(index: u32, len: u32) -> Self { Self { index, @@ -92,11 +98,18 @@ impl Array { self.len == 0 } + pub fn is_null(&self) -> bool { + self.index == u32::MAX + } + pub fn contains_index(&self, index: usize) -> bool { index >= self.index as usize && index < (self.index + self.len) as usize } - pub fn at(&self, index: usize) -> Id { + pub fn at(&self, index: usize) -> Id + where + T: Slabbed, + { if index >= self.len() { Id::NONE } else { @@ -107,4 +120,16 @@ impl Array { pub fn starting_index(&self) -> usize { self.index as usize } + + /// Convert this array into a `u32` array. + pub fn into_u32_array(self) -> Array + where + T: Slabbed, + { + Array { + index: self.index, + len: self.len * T::slab_size() as u32, + _phantom: PhantomData, + } + } } diff --git a/crates/renderling-shader/src/bits.rs b/crates/renderling-shader/src/bits.rs index dc820623..6f224353 100644 --- a/crates/renderling-shader/src/bits.rs +++ b/crates/renderling-shader/src/bits.rs @@ -2,6 +2,8 @@ use core::ops::RangeInclusive; +use crate::{id::Id, slab::Slab}; + /// Statically define a shift/mask range as a literal range of bits. pub const fn bits(range: RangeInclusive) -> (u32, u32) { let mut start = *range.start(); @@ -34,3 +36,321 @@ pub fn insert(bits: &mut u32, (shift, mask): (u32, u32), value: u32) { pub fn extract(bits: u32, (shift, mask): (u32, u32)) -> u32 { (bits >> shift) & mask } + +/// The shift/mask range for the first 8 bits of a u32. +pub const U8_0_BITS: (u32, u32) = bits(0..=7); +/// The shift/mask range for the second 8 bits of a u32. +pub const U8_1_BITS: (u32, u32) = bits(8..=15); +/// The shift/mask range for the third 8 bits of a u32. +pub const U8_2_BITS: (u32, u32) = bits(16..=23); +/// The shift/mask range for the fourth 8 bits of a u32. +pub const U8_3_BITS: (u32, u32) = bits(24..=31); + +/// The shift/mask range for the first 16 bits of a u32. +pub const U16_0_BITS: (u32, u32) = bits(0..=15); +/// The shift/mask range for the second 16 bits of a u32. +pub const U16_1_BITS: (u32, u32) = bits(16..=31); + +/// Extract 8 bits of the u32 at the given index in the slab. +/// +/// Returns the extracted value, the index of the next component and the index +/// of the next u32 in the slab. +pub fn extract_u8( + // index of the u32 in the slab + u32_index: usize, + // eg 0 for the first 8 bits, 1 for the second 8 bits, etc + byte_offset: usize, + // slab of u32s + slab: &[u32], +) -> (u32, usize, usize) { + const SHIFT_MASKS: [((u32, u32), usize); 4] = [ + (U8_0_BITS, 0), + (U8_1_BITS, 0), + (U8_2_BITS, 0), + (U8_3_BITS, 1), + ]; + let byte_mod = byte_offset % 4; + let (shift_mask, index_inc) = SHIFT_MASKS[byte_mod as usize]; + let u32_value = slab.read(Id::from(u32_index)); + let value = extract(u32_value, shift_mask); + (value, u32_index + index_inc as usize, byte_mod + 1) +} + +/// Extract 8 bits of the u32 at the given index in the slab. +/// +/// Returns the extracted value, the index of the next component and the index +/// of the next u32 in the slab. +pub fn extract_i8( + // index of the u32 in the slab + u32_index: usize, + // eg 0 for the first 8 bits, 1 for the second 8 bits, etc + byte_offset: usize, + // slab of u32s + slab: &[u32], +) -> (i32, usize, usize) { + let (value, u32_index, n) = extract_u8(u32_index, byte_offset, slab); + let value: i32 = (value as i32 & 0xFF) - ((value as i32 & 0x80) << 1); + (value, u32_index, n) +} + +/// Extract 16 bits of the u32 at the given index in the slab. +pub fn extract_u16( + // index of the u32 in the slab + u32_index: usize, + // eg 0 for the first 16 bits, 2 for the second 16 bits, etc + byte_offset: usize, + // slab of u32s + slab: &[u32], +) -> (u32, usize, usize) { + // NOTE: This should only have two entries, but we'll still handle the case where + // the extraction is not aligned to a u32 boundary by reading as if it were, and then + // re-aligning. + const SHIFT_MASKS: [((u32, u32), usize, usize); 4] = [ + (U16_0_BITS, 2, 0), + (U16_0_BITS, 2, 0), + (U16_1_BITS, 0, 1), + (U16_1_BITS, 0, 1), + ]; + let byte_mod = byte_offset % 4; + crate::println!("byte_mod: {byte_mod}"); + let (shift_mask, next_byte_offset, index_inc) = SHIFT_MASKS[byte_mod as usize]; + let u32_value = slab.read(Id::from(u32_index)); + crate::println!("u32: {:032b}", u32_value); + let value = extract(u32_value, shift_mask); + crate::println!("u16: {:016b}", value); + crate::println!("u32: {:?}", u32_value); + (value, u32_index + index_inc, next_byte_offset) +} + +/// Extract 16 bits of the u32 at the given index in the slab. +pub fn extract_i16( + // index of the u32 in the slab + u32_index: usize, + // eg 0 for the first 16 bits, 1 for the second 16 bits, etc + byte_offset: usize, + // slab of u32s + slab: &[u32], +) -> (i32, usize, usize) { + let (value, u32_index, n) = extract_u16(u32_index, byte_offset, slab); + let value: i32 = (value as i32 & 0xFFFF) - ((value as i32 & 0x8000) << 1); + (value, u32_index, n) +} + +/// Extract 32 bits of the u32 at the given index in the slab. +pub fn extract_u32( + // index of the u32 in the slab + u32_index: usize, + // ignored and always passed back as `0` + _byte_offset: usize, + // slab of u32s + slab: &[u32], +) -> (u32, usize, usize) { + (slab.read(Id::from(u32_index)), u32_index + 1, 0) +} + +/// Extract 32 bits of the u32 at the given index in the slab. +pub fn extract_i32( + // index of the u32 in the slab + u32_index: usize, + // ignored and always passed back as `0` + _byte_offset: usize, + // slab of u32s + slab: &[u32], +) -> (i32, usize, usize) { + let (value, _, _) = extract_u32(u32_index, 0, slab); + (value as i32, u32_index + 1, 0) +} + +/// Extract 32 bits of the u32 at the given index in the slab. +pub fn extract_f32( + // index of the u32 in the slab + u32_index: usize, + // ignored and always passed back as `0` + _byte_offset: usize, + // slab of u32s + slab: &[u32], +) -> (f32, usize, usize) { + let (value, _, _) = extract_u32(u32_index, 0, slab); + (f32::from_bits(value), u32_index + 1, 0) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn bits_sanity() { + let mut store = 0; + assert_eq!( + "00000000000000000000000000000000", + &format!("{:032b}", store) + ); + insert(&mut store, bits(0..=7), u8::MAX as u32); + assert_eq!( + "00000000000000000000000011111111", + &format!("{:032b}", store) + ); + store = 0; + insert(&mut store, bits(8..=15), u8::MAX as u32); + assert_eq!( + "00000000000000001111111100000000", + &format!("{:032b}", store) + ); + } + + #[test] + fn bits_u8_sanity() { + let mut bits = 0; + println!("bits: {:032b}", bits); + super::insert(&mut bits, super::U8_0_BITS, 6u8 as u32); + println!("bits: {:032b}", bits); + assert_eq!(super::extract(bits, super::U8_0_BITS), 6); + super::insert(&mut bits, super::U8_1_BITS, 5u8 as u32); + println!("bits: {:032b}", bits); + assert_eq!(super::extract(bits, super::U8_0_BITS), 6); + assert_eq!(super::extract(bits, super::U8_1_BITS), 5); + super::insert(&mut bits, super::U8_2_BITS, 4u8 as u32); + println!("bits: {:032b}", bits); + assert_eq!(super::extract(bits, super::U8_0_BITS), 6); + assert_eq!(super::extract(bits, super::U8_1_BITS), 5); + assert_eq!(super::extract(bits, super::U8_2_BITS), 4); + super::insert(&mut bits, super::U8_3_BITS, 3u8 as u32); + println!("bits: {:032b}", bits); + assert_eq!(super::extract(bits, super::U8_0_BITS), 6); + assert_eq!(super::extract(bits, super::U8_1_BITS), 5); + assert_eq!(super::extract(bits, super::U8_2_BITS), 4); + assert_eq!(super::extract(bits, super::U8_3_BITS), 3); + } + + #[test] + fn extract_u8_sanity() { + let u8_slab = [0u8, 1u8, 2u8, 3u8, 4u8, 5u8, 0u8, 0u8]; + let u32_slab: &[u32] = bytemuck::cast_slice(&u8_slab); + let index = 0; + let n = 0; + let (a, index, n) = extract_u8(index, n, u32_slab); + let (b, index, n) = extract_u8(index, n, u32_slab); + let (c, index, n) = extract_u8(index, n, u32_slab); + let (d, index, n) = extract_u8(index, n, u32_slab); + let (e, index, n) = extract_u8(index, n, u32_slab); + let (f, _, _) = extract_u8(index, n, u32_slab); + assert_eq!([0, 1, 2, 3, 4, 5], [a, b, c, d, e, f]); + } + + #[test] + fn extract_i8_sanity() { + let i8_slab = [0i8, -1i8, -2i8, -3i8, 4i8, 5i8, 0i8, 0i8]; + let u32_slab: &[u32] = bytemuck::cast_slice(&i8_slab); + let index = 0; + let n = 0; + let (a, index, n) = extract_i8(index, n, u32_slab); + let (b, index, n) = extract_i8(index, n, u32_slab); + let (c, index, n) = extract_i8(index, n, u32_slab); + let (d, index, n) = extract_i8(index, n, u32_slab); + let (e, index, n) = extract_i8(index, n, u32_slab); + let (f, _, _) = extract_i8(index, n, u32_slab); + assert_eq!([0, -1, -2, -3, 4, 5], [a, b, c, d, e, f]); + } + + #[test] + fn extract_u16_sanity() { + let u16_slab = [0u16, 1u16, 2u16, 3u16, 4u16, 5u16]; + let u32_slab: &[u32] = bytemuck::cast_slice(&u16_slab); + let index = 0; + let n = 0; + let (a, index, n) = extract_u16(index, n, u32_slab); + let (b, index, n) = extract_u16(index, n, u32_slab); + let (c, index, n) = extract_u16(index, n, u32_slab); + let (d, index, n) = extract_u16(index, n, u32_slab); + let (e, index, n) = extract_u16(index, n, u32_slab); + let (f, _, _) = extract_u16(index, n, u32_slab); + assert_eq!([0, 1, 2, 3, 4, 5], [a, b, c, d, e, f]); + } + + #[test] + fn extract_i16_sanity() { + let i16_slab = [0i16, -1i16, -2i16, -3i16, 4i16, 5i16, -12345i16, 0i16]; + let u32_slab: &[u32] = bytemuck::cast_slice(&i16_slab); + let index = 0; + let n = 0; + let (a, index, n) = extract_i16(index, n, u32_slab); + let (b, index, n) = extract_i16(index, n, u32_slab); + let (c, index, n) = extract_i16(index, n, u32_slab); + let (d, index, n) = extract_i16(index, n, u32_slab); + let (e, index, n) = extract_i16(index, n, u32_slab); + let (f, index, n) = extract_i16(index, n, u32_slab); + let (g, _, _) = extract_i16(index, n, u32_slab); + assert_eq!([0, -1, -2, -3, 4, 5, -12345], [a, b, c, d, e, f, g]); + } + + #[test] + fn extract_u32_sanity() { + let u32_slab = [0u32, 1u32, 2u32, 3u32, 4u32, 5u32]; + let u32_slab: &[u32] = bytemuck::cast_slice(&u32_slab); + let index = 0; + let n = 0; + let (a, index, n) = extract_u32(index, n, u32_slab); + let (b, index, n) = extract_u32(index, n, u32_slab); + let (c, index, n) = extract_u32(index, n, u32_slab); + let (d, index, n) = extract_u32(index, n, u32_slab); + let (e, index, n) = extract_u32(index, n, u32_slab); + let (f, _, _) = extract_u32(index, n, u32_slab); + assert_eq!([0, 1, 2, 3, 4, 5], [a, b, c, d, e, f]); + } + + #[test] + fn extract_i32_sanity() { + let i32_slab = [0i32, -1i32, -2i32, -3i32, 4i32, 5i32, -12345i32]; + let u32_slab: &[u32] = bytemuck::cast_slice(&i32_slab); + let index = 0; + let n = 0; + let (a, index, n) = extract_i32(index, n, u32_slab); + let (b, index, n) = extract_i32(index, n, u32_slab); + let (c, index, n) = extract_i32(index, n, u32_slab); + let (d, index, n) = extract_i32(index, n, u32_slab); + let (e, index, n) = extract_i32(index, n, u32_slab); + let (f, index, n) = extract_i32(index, n, u32_slab); + let (g, _, _) = extract_i32(index, n, u32_slab); + assert_eq!([0, -1, -2, -3, 4, 5, -12345], [a, b, c, d, e, f, g]); + } + + #[test] + fn extract_f32_sanity() { + let f32_slab = [ + 0.0f32, + -1.0f32, + -2.0f32, + -3.0f32, + 4.0f32, + 5.0f32, + -12345.0f32, + ]; + let u32_slab: &[u32] = bytemuck::cast_slice(&f32_slab); + let index = 0; + let n = 0; + let (a, index, n) = extract_f32(index, n, u32_slab); + let (b, index, n) = extract_f32(index, n, u32_slab); + let (c, index, n) = extract_f32(index, n, u32_slab); + let (d, index, n) = extract_f32(index, n, u32_slab); + let (e, index, n) = extract_f32(index, n, u32_slab); + let (f, index, n) = extract_f32(index, n, u32_slab); + let (g, _, _) = extract_f32(index, n, u32_slab); + assert_eq!( + [0f32, -1f32, -2f32, -3f32, 4f32, 5f32, -12345f32], + [a, b, c, d, e, f, g] + ); + } + + #[test] + fn indices_sanity() { + let slab: [u32; 20] = [ + 65536, 2, 0, 0, 0, 1065353216, 0, 0, 0, 1065353216, 0, 0, 0, 1065353216, 0, 0, + 1065353216, 0, 0, 1065353216, + ]; + let u32_index = 9usize; + let byte_offset = 0usize; + let (a, u32_index, byte_offset) = extract_u32(u32_index, byte_offset, &slab); + let (b, u32_index, byte_offset) = extract_u32(u32_index, byte_offset, &slab); + let (c, u32_index, byte_offset) = extract_u32(u32_index, byte_offset, &slab); + } +} diff --git a/crates/renderling-shader/src/gltf.rs b/crates/renderling-shader/src/gltf.rs index 76d4bdb1..86802f6b 100644 --- a/crates/renderling-shader/src/gltf.rs +++ b/crates/renderling-shader/src/gltf.rs @@ -1,14 +1,22 @@ //! Gltf types that are used in shaders. +use glam::{Vec2, Vec3, Vec4}; + use crate::{ - self as renderling_shader, array::Array, id::Id, pbr::PbrMaterial, slab::Slabbed, + self as renderling_shader, + array::Array, + id::Id, + pbr::PbrMaterial, + slab::{Slab, Slabbed}, texture::GpuTexture, }; #[repr(transparent)] +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[derive(Default, Clone, Copy, Slabbed)] pub struct GltfBuffer(pub Array); #[repr(u32)] -#[derive(Default, Clone, Copy)] +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] +#[derive(Default, Clone, Copy, PartialEq)] pub enum DataType { I8, U8, @@ -48,6 +56,7 @@ impl Slabbed for DataType { } #[repr(u32)] +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[derive(Default, Clone, Copy)] pub enum Dimensions { #[default] @@ -96,9 +105,13 @@ impl Slabbed for Dimensions { } } +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[derive(Default, Clone, Copy, Slabbed)] pub struct GltfAccessor { - // The byte size of each component that this accessor describes. + // The byte size of each element that this accessor describes. + // + /// For example, if the accessor describes a `Vec3` of F32s, then + // the size is 3 * 4 = 12. pub size: u32, pub buffer: Id, // Returns the offset relative to the start of the parent buffer view in bytes. @@ -107,19 +120,487 @@ pub struct GltfAccessor { pub view_offset: u32, // The stride in bytes between vertex attributes or other interleavable data. pub view_stride: u32, - // The number of components within the buffer view - not to be confused with the + // The number of elements within the buffer view - not to be confused with the // number of bytes in the buffer view. pub count: u32, // The data type of components in the attribute. - pub component_type: DataType, + pub data_type: DataType, // Specifies if the attribute is a scalar, vector, or matrix. pub dimensions: Dimensions, // Whether or not the attribute is normalized. pub normalized: bool, } +/// Used to access contiguous u8 components from a buffer, contained in u32s. +pub struct IncU8 { + slab_index: usize, + byte_offset: usize, +} + +impl IncU8 { + pub fn extract(self, slab: &[u32]) -> (u32, Self) { + let (value, slab_index, byte_offset) = + crate::bits::extract_u8(self.slab_index, self.byte_offset, slab); + ( + value, + Self { + slab_index, + byte_offset, + }, + ) + } +} + +/// Used to access contiguous i8 components from a buffer, contained in u32s. +pub struct IncI8 { + slab_index: usize, + byte_offset: usize, +} + +impl IncI8 { + pub fn extract(self, slab: &[u32]) -> (i32, Self) { + let (value, slab_index, byte_offset) = + crate::bits::extract_i8(self.slab_index, self.byte_offset, slab); + ( + value, + Self { + slab_index, + byte_offset, + }, + ) + } +} + +/// Used to access contiguous u16 components from a buffer, contained in u32s. +pub struct IncU16 { + slab_index: usize, + byte_offset: usize, +} + +impl IncU16 { + pub fn extract(self, slab: &[u32]) -> (u32, Self) { + let (value, slab_index, byte_offset) = + crate::bits::extract_u16(self.slab_index, self.byte_offset, slab); + crate::println!("value: {value:?}"); + ( + value, + Self { + slab_index, + byte_offset, + }, + ) + } +} + +/// Used to access contiguous i16 components from a buffer, contained in u32s. +pub struct IncI16 { + slab_index: usize, + byte_offset: usize, +} + +impl IncI16 { + pub fn extract(self, slab: &[u32]) -> (i32, Self) { + let (value, slab_index, byte_offset) = + crate::bits::extract_i16(self.slab_index, self.byte_offset, slab); + ( + value, + Self { + slab_index, + byte_offset, + }, + ) + } +} + +impl GltfAccessor { + fn slab_index_and_byte_offset(&self, element_index: usize, slab: &[u32]) -> (usize, usize) { + crate::println!("index: {element_index:?}"); + let buffer_id = self.buffer; + crate::println!("buffer_id: {buffer_id:?}"); + let buffer = slab.read(self.buffer); + crate::println!("buffer: {:?}", buffer); + let buffer_start = buffer.0.starting_index(); + crate::println!("buffer_start: {buffer_start:?}"); + let buffer_start_bytes = buffer_start * 4; + crate::println!("buffer_start_bytes: {buffer_start_bytes:?}"); + let byte_offset = buffer_start_bytes + + self.view_offset as usize + + element_index as usize + * if self.size > self.view_stride { + self.size + } else { + self.view_stride + } as usize; + crate::println!("byte_offset: {byte_offset:?}"); + let slab_index = byte_offset / 4; + crate::println!("slab_index: {slab_index:?}"); + let byte_offset = byte_offset % 4; + (slab_index, byte_offset) + } + + pub fn inc_u8(&self, index: usize, slab: &[u32]) -> IncU8 { + let (slab_index, byte_offset) = self.slab_index_and_byte_offset(index, slab); + IncU8 { + slab_index, + byte_offset, + } + } + + pub fn inc_i8(&self, index: usize, slab: &[u32]) -> IncI8 { + let (slab_index, byte_offset) = self.slab_index_and_byte_offset(index, slab); + IncI8 { + slab_index, + byte_offset, + } + } + + pub fn inc_u16(&self, index: usize, slab: &[u32]) -> IncU16 { + let (slab_index, byte_offset) = self.slab_index_and_byte_offset(index, slab); + IncU16 { + slab_index, + byte_offset, + } + } + + pub fn inc_i16(&self, index: usize, slab: &[u32]) -> IncI16 { + let (slab_index, byte_offset) = self.slab_index_and_byte_offset(index, slab); + IncI16 { + slab_index, + byte_offset, + } + } + + pub fn get_u32(&self, vertex_index: usize, slab: &[u32]) -> u32 { + let x; + match self.data_type { + DataType::I8 => { + let inc = self.inc_i8(vertex_index, slab); + let (ix, _) = inc.extract(slab); + x = ix as u32; + } + DataType::U8 => { + let inc = self.inc_u8(vertex_index, slab); + let (ix, _) = inc.extract(slab); + x = ix; + } + DataType::I16 => { + let inc = self.inc_i16(vertex_index, slab); + let (ix, _) = inc.extract(slab); + x = ix as u32; + } + DataType::U16 => { + let inc = self.inc_u16(vertex_index, slab); + let (ix, _) = inc.extract(slab); + x = ix; + } + DataType::U32 => { + let (slab_index, _) = self.slab_index_and_byte_offset(vertex_index, slab); + x = crate::bits::extract_u32(slab_index, 0, slab).0; + } + DataType::F32 => { + let (slab_index, _) = self.slab_index_and_byte_offset(vertex_index, slab); + x = crate::bits::extract_f32(slab_index, 0, slab).0 as u32; + } + } + x + } + + pub fn get_f32(&self, vertex_index: usize, slab: &[u32]) -> f32 { + let x; + match self.data_type { + DataType::I8 => { + let inc = self.inc_i8(vertex_index, slab); + let (ix, _) = inc.extract(slab); + x = ix as f32; + } + DataType::U8 => { + let inc = self.inc_u8(vertex_index, slab); + let (ix, _) = inc.extract(slab); + x = ix as f32; + } + DataType::I16 => { + let inc = self.inc_i16(vertex_index, slab); + let (ix, _) = inc.extract(slab); + x = ix as f32; + } + DataType::U16 => { + let inc = self.inc_u16(vertex_index, slab); + let (ix, _) = inc.extract(slab); + x = ix as f32; + } + DataType::U32 => { + let (slab_index, _) = self.slab_index_and_byte_offset(vertex_index, slab); + x = crate::bits::extract_u32(slab_index, 0, slab).0 as f32; + } + DataType::F32 => { + let (slab_index, _) = self.slab_index_and_byte_offset(vertex_index, slab); + x = crate::bits::extract_f32(slab_index, 0, slab).0; + } + } + x + } + + pub fn get_vec2(&self, vertex_index: usize, slab: &[u32]) -> glam::Vec2 { + let x; + let y; + match self.data_type { + DataType::I8 => { + let inc = self.inc_i8(vertex_index, slab); + let (ix, inc) = inc.extract(slab); + let (iy, _) = inc.extract(slab); + x = ix as f32; + y = iy as f32; + } + DataType::U8 => { + let inc = self.inc_u8(vertex_index, slab); + let (ix, inc) = inc.extract(slab); + let (iy, _) = inc.extract(slab); + x = ix as f32; + y = iy as f32; + } + DataType::I16 => { + let inc = self.inc_i16(vertex_index, slab); + let (ix, inc) = inc.extract(slab); + let (iy, _) = inc.extract(slab); + x = ix as f32; + y = iy as f32; + } + DataType::U16 => { + let inc = self.inc_u16(vertex_index, slab); + let (ix, inc) = inc.extract(slab); + let (iy, _) = inc.extract(slab); + x = ix as f32; + y = iy as f32; + } + DataType::U32 => { + let (slab_index, _) = self.slab_index_and_byte_offset(vertex_index, slab); + x = crate::bits::extract_u32(slab_index, 0, slab).0 as f32; + y = crate::bits::extract_u32(slab_index + 1, 0, slab).0 as f32; + } + DataType::F32 => { + let (slab_index, _) = self.slab_index_and_byte_offset(vertex_index, slab); + x = crate::bits::extract_f32(slab_index, 0, slab).0; + y = crate::bits::extract_f32(slab_index + 1, 0, slab).0; + } + } + match self.dimensions { + Dimensions::Scalar => glam::Vec2::new(x, 0.0), + _ => glam::Vec2::new(x, y), + } + } + + pub fn get_vec3(&self, vertex_index: usize, slab: &[u32]) -> glam::Vec3 { + let x; + let y; + let z; + match self.data_type { + DataType::I8 => { + let inc = self.inc_i8(vertex_index, slab); + let (ix, inc) = inc.extract(slab); + let (iy, inc) = inc.extract(slab); + let (iz, _) = inc.extract(slab); + x = ix as f32; + y = iy as f32; + z = iz as f32; + } + DataType::U8 => { + let inc = self.inc_u8(vertex_index, slab); + let (ix, inc) = inc.extract(slab); + let (iy, inc) = inc.extract(slab); + let (iz, _) = inc.extract(slab); + x = ix as f32; + y = iy as f32; + z = iz as f32; + } + DataType::I16 => { + let inc = self.inc_i16(vertex_index, slab); + let (ix, inc) = inc.extract(slab); + let (iy, inc) = inc.extract(slab); + let (iz, _) = inc.extract(slab); + x = ix as f32; + y = iy as f32; + z = iz as f32; + } + DataType::U16 => { + let inc = self.inc_u16(vertex_index, slab); + let (ix, inc) = inc.extract(slab); + let (iy, inc) = inc.extract(slab); + let (iz, _) = inc.extract(slab); + x = ix as f32; + y = iy as f32; + z = iz as f32; + } + DataType::U32 => { + let (slab_index, _) = self.slab_index_and_byte_offset(vertex_index, slab); + x = crate::bits::extract_u32(slab_index, 0, slab).0 as f32; + y = crate::bits::extract_u32(slab_index + 1, 0, slab).0 as f32; + z = crate::bits::extract_u32(slab_index + 2, 0, slab).0 as f32; + } + DataType::F32 => { + let (slab_index, _) = self.slab_index_and_byte_offset(vertex_index, slab); + x = crate::bits::extract_f32(slab_index, 0, slab).0; + y = crate::bits::extract_f32(slab_index + 1, 0, slab).0; + z = crate::bits::extract_f32(slab_index + 2, 0, slab).0; + } + } + match self.dimensions { + Dimensions::Scalar => glam::Vec3::new(x, 0.0, 0.0), + Dimensions::Vec2 => glam::Vec3::new(x, y, 0.0), + _ => glam::Vec3::new(x, y, z), + } + } + + pub fn get_vec4(&self, vertex_index: usize, slab: &[u32]) -> glam::Vec4 { + let x; + let y; + let z; + let w; + match self.data_type { + DataType::I8 => { + let inc = self.inc_i8(vertex_index, slab); + let (ix, inc) = inc.extract(slab); + let (iy, inc) = inc.extract(slab); + let (iz, inc) = inc.extract(slab); + let (iw, _) = inc.extract(slab); + x = ix as f32; + y = iy as f32; + z = iz as f32; + w = iw as f32; + } + DataType::U8 => { + let inc = self.inc_u8(vertex_index, slab); + let (ix, inc) = inc.extract(slab); + let (iy, inc) = inc.extract(slab); + let (iz, inc) = inc.extract(slab); + let (iw, _) = inc.extract(slab); + x = ix as f32; + y = iy as f32; + z = iz as f32; + w = iw as f32; + } + DataType::I16 => { + let inc = self.inc_i16(vertex_index, slab); + let (ix, inc) = inc.extract(slab); + let (iy, inc) = inc.extract(slab); + let (iz, inc) = inc.extract(slab); + let (iw, _) = inc.extract(slab); + x = ix as f32; + y = iy as f32; + z = iz as f32; + w = iw as f32; + } + DataType::U16 => { + let inc = self.inc_u16(vertex_index, slab); + let (ix, inc) = inc.extract(slab); + let (iy, inc) = inc.extract(slab); + let (iz, inc) = inc.extract(slab); + let (iw, _) = inc.extract(slab); + x = ix as f32; + y = iy as f32; + z = iz as f32; + w = iw as f32; + } + DataType::U32 => { + let (slab_index, _) = self.slab_index_and_byte_offset(vertex_index, slab); + x = crate::bits::extract_u32(slab_index, 0, slab).0 as f32; + y = crate::bits::extract_u32(slab_index + 1, 0, slab).0 as f32; + z = crate::bits::extract_u32(slab_index + 2, 0, slab).0 as f32; + w = crate::bits::extract_u32(slab_index + 3, 0, slab).0 as f32; + } + DataType::F32 => { + let (slab_index, _) = self.slab_index_and_byte_offset(vertex_index, slab); + x = crate::bits::extract_f32(slab_index, 0, slab).0; + y = crate::bits::extract_f32(slab_index + 1, 0, slab).0; + z = crate::bits::extract_f32(slab_index + 2, 0, slab).0; + w = crate::bits::extract_f32(slab_index + 3, 0, slab).0; + } + } + match self.dimensions { + Dimensions::Scalar => glam::Vec4::new(x, 0.0, 0.0, 0.0), + Dimensions::Vec2 => glam::Vec4::new(x, y, 0.0, 0.0), + Dimensions::Vec3 => glam::Vec4::new(x, y, z, 0.0), + _ => glam::Vec4::new(x, y, z, w), + } + } + + pub fn get_uvec4(&self, vertex_index: usize, slab: &[u32]) -> glam::UVec4 { + let x; + let y; + let z; + let w; + match self.data_type { + DataType::I8 => { + let inc = self.inc_i8(vertex_index, slab); + let (ix, inc) = inc.extract(slab); + let (iy, inc) = inc.extract(slab); + let (iz, inc) = inc.extract(slab); + let (iw, _) = inc.extract(slab); + x = ix as u32; + y = iy as u32; + z = iz as u32; + w = iw as u32; + } + DataType::U8 => { + let inc = self.inc_u8(vertex_index, slab); + let (ix, inc) = inc.extract(slab); + let (iy, inc) = inc.extract(slab); + let (iz, inc) = inc.extract(slab); + let (iw, _) = inc.extract(slab); + x = ix; + y = iy; + z = iz; + w = iw; + } + DataType::I16 => { + let inc = self.inc_i16(vertex_index, slab); + let (ix, inc) = inc.extract(slab); + let (iy, inc) = inc.extract(slab); + let (iz, inc) = inc.extract(slab); + let (iw, _) = inc.extract(slab); + x = ix as u32; + y = iy as u32; + z = iz as u32; + w = iw as u32; + } + DataType::U16 => { + let inc = self.inc_u16(vertex_index, slab); + let (ix, inc) = inc.extract(slab); + let (iy, inc) = inc.extract(slab); + let (iz, inc) = inc.extract(slab); + let (iw, _) = inc.extract(slab); + x = ix; + y = iy; + z = iz; + w = iw; + } + DataType::U32 => { + let (slab_index, _) = self.slab_index_and_byte_offset(vertex_index, slab); + x = crate::bits::extract_u32(slab_index, 0, slab).0; + y = crate::bits::extract_u32(slab_index + 1, 0, slab).0; + z = crate::bits::extract_u32(slab_index + 2, 0, slab).0; + w = crate::bits::extract_u32(slab_index + 3, 0, slab).0; + } + DataType::F32 => { + let (slab_index, _) = self.slab_index_and_byte_offset(vertex_index, slab); + x = crate::bits::extract_f32(slab_index, 0, slab).0 as u32; + y = crate::bits::extract_f32(slab_index + 1, 0, slab).0 as u32; + z = crate::bits::extract_f32(slab_index + 2, 0, slab).0 as u32; + w = crate::bits::extract_f32(slab_index + 3, 0, slab).0 as u32; + } + } + match self.dimensions { + Dimensions::Scalar => glam::UVec4::new(x, 0, 0, 0), + Dimensions::Vec2 => glam::UVec4::new(x, y, 0, 0), + Dimensions::Vec3 => glam::UVec4::new(x, y, z, 0), + _ => glam::UVec4::new(x, y, z, w), + } + } +} + +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[derive(Default, Clone, Copy, Slabbed)] pub struct GltfPrimitive { + pub vertex_count: u32, pub material: Id, pub indices: Id, pub positions: Id, @@ -132,12 +613,116 @@ pub struct GltfPrimitive { pub weights: Id, } +impl GltfPrimitive { + pub fn get_vertex(&self, vertex_index: usize, slab: &[u32]) -> crate::stage::Vertex { + let index = if self.indices.is_some() { + let indices = slab.read(self.indices); + let index = indices.get_u32(vertex_index, slab); + index as usize + } else { + vertex_index + }; + crate::println!("index: {index:?}"); + + let position = if self.positions.is_none() { + Vec3::ZERO + } else { + let positions = slab.read(self.positions); + crate::println!("positions: {positions:?}"); + positions.get_vec3(index, slab) + }; + crate::println!("position: {position:?}"); + + let normal = if self.normals.is_none() { + Vec3::Z + } else { + let normals = slab.read(self.normals); + crate::println!("normals: {normals:?}"); + normals.get_vec3(index, slab) + }; + crate::println!("normal: {normal:?}"); + + let tangent = if self.tangents.is_none() { + Vec4::Y + } else { + let tangents = slab.read(self.tangents); + crate::println!("tangents: {tangents:?}"); + tangents.get_vec4(index, slab) + }; + crate::println!("tangent: {tangent:?}"); + + let color = if self.colors.is_none() { + Vec4::ONE + } else { + let colors = slab.read(self.colors); + crate::println!("colors: {colors:?}"); + colors.get_vec4(index, slab) + }; + crate::println!("color: {color:?}"); + + let tex_coords0 = if self.tex_coords0.is_none() { + Vec2::ZERO + } else { + let tex_coords0 = slab.read(self.tex_coords0); + crate::println!("tex_coords0: {tex_coords0:?}"); + tex_coords0.get_vec2(index, slab) + }; + crate::println!("tex_coords0: {tex_coords0:?}"); + + let tex_coords1 = if self.tex_coords1.is_none() { + Vec2::ZERO + } else { + let tex_coords1 = slab.read(self.tex_coords1); + crate::println!("tex_coords1: {tex_coords1:?}"); + tex_coords1.get_vec2(index, slab) + }; + crate::println!("tex_coords1: {tex_coords1:?}"); + + let uv = Vec4::new(tex_coords0.x, tex_coords0.y, tex_coords1.x, tex_coords1.y); + crate::println!("uv: {uv:?}"); + + let joints = if self.joints.is_none() { + [0; 4] + } else { + let joints = slab.read(self.joints); + crate::println!("joints: {joints:?}"); + let joints = joints.get_uvec4(index, slab); + crate::println!("joints: {joints:?}"); + [joints.x, joints.y, joints.z, joints.w] + }; + crate::println!("joints: {joints:?}"); + + let weights = if self.weights.is_none() { + [0.0; 4] + } else { + let weights = slab.read(self.weights); + crate::println!("weights: {weights:?}"); + let weights = weights.get_vec4(index, slab); + crate::println!("weights: {weights:?}"); + [weights.x, weights.y, weights.z, weights.w] + }; + crate::println!("weights: {weights:?}"); + + crate::stage::Vertex { + position: position.extend(0.0), + color, + uv, + normal: normal.extend(0.0), + tangent, + joints, + weights, + } + } +} + +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[derive(Default, Clone, Copy, Slabbed)] pub struct GltfMesh { pub primitives: Array, pub weights: Array, } +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[derive(Clone, Copy)] pub enum GltfCamera { Orthographic { @@ -444,6 +1029,7 @@ pub struct GltfScene { pub nodes: Array>, } +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[derive(Default, Clone, Copy, Slabbed)] pub struct GltfBufferView { pub buffer: Id, @@ -471,3 +1057,35 @@ pub struct GltfDocument { pub textures: Array, pub views: Array, } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn indices_accessor_sanity() { + // Taken from the indices accessor in the "simple meshes" gltf sample, + // but with the buffer changed to match where we write it here. + let buffer_id = Id::new(20); + let accessor = GltfAccessor { + size: 2, + buffer: buffer_id, + view_offset: 0, + view_stride: 0, + count: 3, + data_type: DataType::U16, + dimensions: Dimensions::Scalar, + normalized: false, + }; + let buffer = GltfBuffer(Array::new(0, 11)); + let mut slab: [u32; 22] = [ + 65536, 2, 0, 0, 0, 1065353216, 0, 0, 0, 1065353216, 0, 0, 0, 1065353216, 0, 0, + 1065353216, 0, 0, 1065353216, 0, 0, + ]; + slab.write(&buffer, buffer_id.index()); + let i0 = accessor.get_u32(0, &slab); + let i1 = accessor.get_u32(1, &slab); + let i2 = accessor.get_u32(2, &slab); + assert_eq!([0, 1, 2], [i0, i1, i2]); + } +} diff --git a/crates/renderling-shader/src/lib.rs b/crates/renderling-shader/src/lib.rs index b83aa085..d15b73fa 100644 --- a/crates/renderling-shader/src/lib.rs +++ b/crates/renderling-shader/src/lib.rs @@ -25,6 +25,18 @@ pub mod tonemapping; pub mod tutorial; pub mod ui; +#[allow(unused_macros)] +macro_rules! println { + ($($arg:tt)*) => { + #[cfg(not(target_arch = "spirv"))] + { + std::println!($($arg)*); + } + } +} + +pub(crate) use println; + /// Additional methods for vector types. pub trait IsVector { /// Normalize or return zero. diff --git a/crates/renderling-shader/src/stage.rs b/crates/renderling-shader/src/stage.rs index f76d0e31..83817f4b 100644 --- a/crates/renderling-shader/src/stage.rs +++ b/crates/renderling-shader/src/stage.rs @@ -19,6 +19,7 @@ use crate::{ array::Array, bits::{bits, extract, insert}, debug::*, + gltf::{GltfMesh, GltfNode}, id::{Id, ID_NONE}, pbr::{self, PbrMaterial}, slab::{Slab, Slabbed}, @@ -136,18 +137,18 @@ impl Vertex { mat } - pub fn generate_normal(a: Vertex, b: Vertex, c: Vertex) -> Vec3 { - let ab = a.position.xyz() - b.position.xyz(); - let ac = a.position.xyz() - c.position.xyz(); + pub fn generate_normal(a: Vec3, b: Vec3, c: Vec3) -> Vec3 { + let ab = a - b; + let ac = a - c; ab.cross(ac).normalize() } - pub fn generate_tangent(a: Vertex, b: Vertex, c: Vertex) -> Vec4 { - let ab = b.position.xyz() - a.position.xyz(); - let ac = c.position.xyz() - a.position.xyz(); + pub fn generate_tangent(a: Vec3, a_uv: Vec2, b: Vec3, b_uv: Vec2, c: Vec3, c_uv: Vec2) -> Vec4 { + let ab = b - a; + let ac = c - a; let n = ab.cross(ac); - let d_uv1 = b.uv.xy() - a.uv.xy(); - let d_uv2 = c.uv.xy() - a.uv.xy(); + let d_uv1 = b_uv - a_uv; + let d_uv2 = c_uv - a_uv; let denom = d_uv1.x * d_uv2.y - d_uv2.x * d_uv1.y; let denom_sign = if denom >= 0.0 { 1.0 } else { -1.0 }; let denom = denom.abs().max(f32::EPSILON) * denom_sign; @@ -891,37 +892,145 @@ pub struct StageLegend { pub light_array: Array, } -/// A fully-computed unit of rendering, roughly meaning a mesh with model matrix -/// transformations. #[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[repr(C)] -#[derive(Clone, Copy, PartialEq, Slabbed)] -pub struct RenderUnit { +#[derive(Default, Clone, Copy, PartialEq, Slabbed)] +pub struct NativeVertexData { // Points to an array of `Vertex` in the stage's slab. pub vertices: Array, // Points to a `PbrMaterial` in the stage's slab. pub material: Id, - // Points to a `Camera` in the stage's slab. - pub camera: Id, +} - pub position: Vec3, +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] +#[repr(C)] +#[derive(Default, Clone, Copy, PartialEq, Slabbed)] +pub struct GltfVertexData { + // A path of node ids that leads to the node that contains the mesh. + pub parent_node_path: Array>, + // Points to a `GltfMesh` in the stage's slab. + pub mesh: Id, + // The index of the primitive within the mesh that this unit draws. + pub primitive_index: u32, +} + +#[repr(u32)] +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] +#[derive(Clone, Copy, PartialEq)] +pub enum VertexData { + Native(Id), + Gltf(Id), +} + +impl Default for VertexData { + fn default() -> Self { + VertexData::Native(Id::NONE) + } +} + +impl Slabbed for VertexData { + fn slab_size() -> usize { + 2 + } + + fn read_slab(&mut self, index: usize, slab: &[u32]) -> usize { + let mut proxy = 0u32; + let index = proxy.read_slab(index, slab); + match proxy { + 0 => { + let mut native = Id::default(); + let index = native.read_slab(index, slab); + *self = Self::Native(native); + index + } + 1 => { + let mut gltf = Id::default(); + let index = gltf.read_slab(index, slab); + *self = Self::Gltf(gltf); + index + } + _ => index, + } + } + + fn write_slab(&self, index: usize, slab: &mut [u32]) -> usize { + match self { + Self::Native(native) => { + let index = 0u32.write_slab(index, slab); + native.write_slab(index, slab) + } + Self::Gltf(gltf) => { + let index = 1u32.write_slab(index, slab); + gltf.write_slab(index, slab) + } + } + } +} + +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] +#[repr(C)] +#[derive(Clone, Copy, PartialEq, Slabbed)] +pub struct Transform { + pub translation: Vec3, pub rotation: Quat, pub scale: Vec3, } -impl Default for RenderUnit { +impl Default for Transform { fn default() -> Self { Self { - vertices: Default::default(), - material: Default::default(), - camera: Default::default(), - position: Vec3::ZERO, + translation: Vec3::ZERO, rotation: Quat::IDENTITY, scale: Vec3::ONE, } } } +/// A fully-computed unit of rendering, roughly meaning a mesh with model matrix +/// transformations. +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] +#[repr(C)] +#[derive(Default, Clone, Copy, PartialEq, Slabbed)] +pub struct RenderUnit { + pub vertex_data: VertexData, + // Points to a `Camera` in the stage's slab. + pub camera: Id, + // Points to a `Transform` in the stage's slab. + pub transform: Id, + // Number of vertices to draw for this unit. + pub vertex_count: u32, +} + +impl RenderUnit { + pub fn get_vertex_details( + &self, + vertex_index: u32, + slab: &[u32], + ) -> (Vertex, Transform, Id) { + let transform = slab.read(self.transform); + match self.vertex_data { + VertexData::Native(id) => { + let NativeVertexData { vertices, material } = slab.read(id); + let vertex = slab.read(vertices.at(vertex_index as usize)); + (vertex, transform, material) + } + VertexData::Gltf(id) => { + let GltfVertexData { + parent_node_path: _, + mesh, + primitive_index, + } = slab.read(id); + // TODO: check nodes for skinning + let mesh = slab.read(mesh); + let primitive = slab.read(mesh.primitives.at(primitive_index as usize)); + let material = primitive.material; + let vertex = primitive.get_vertex(vertex_index as usize, slab); + (vertex, transform, material) + } + } + } +} + #[spirv(vertex)] pub fn new_stage_vertex( // Which render unit are we rendering @@ -939,24 +1048,24 @@ pub fn new_stage_vertex( out_bitangent: &mut Vec3, // position of the vertex/fragment in world space out_pos: &mut Vec3, - #[spirv(position)] gl_pos: &mut Vec4, + #[spirv(position)] clip_pos: &mut Vec4, ) { let unit_id: Id = Id::from(instance_index); let unit = slab.read(unit_id); - let vertex = slab.read(unit.vertices.at(vertex_index as usize)); + let (vertex, tfrm, material) = unit.get_vertex_details(vertex_index, slab); let model_matrix = - Mat4::from_scale_rotation_translation(unit.scale, unit.rotation, unit.position); - *out_material = unit.material.into(); + Mat4::from_scale_rotation_translation(tfrm.scale, tfrm.rotation, tfrm.translation); + *out_material = material.into(); *out_color = vertex.color; *out_uv0 = vertex.uv.xy(); *out_uv1 = vertex.uv.zw(); - let scale2 = unit.scale * unit.scale; + let scale2 = tfrm.scale * tfrm.scale; let normal = vertex.normal.xyz().alt_norm_or_zero(); let tangent = vertex.tangent.xyz().alt_norm_or_zero(); - let normal_w = (model_matrix * (normal / scale2).extend(0.0)) + let normal_w: Vec3 = (model_matrix * (normal / scale2).extend(0.0)) .xyz() .alt_norm_or_zero(); - let tangent_w = (model_matrix * tangent.extend(0.0)) + let tangent_w: Vec3 = (model_matrix * tangent.extend(0.0)) .xyz() .alt_norm_or_zero(); let bitangent_w = normal_w.cross(tangent_w) * if vertex.tangent.w >= 0.0 { 1.0 } else { -1.0 }; @@ -967,7 +1076,7 @@ pub fn new_stage_vertex( *out_pos = view_pos.xyz(); let camera = slab.read(unit.camera); *out_camera = unit.camera.into(); - *gl_pos = camera.projection * camera.view * view_pos; + *clip_pos = camera.projection * camera.view * view_pos; } #[allow(clippy::too_many_arguments)] @@ -1278,6 +1387,23 @@ pub fn compute_cull_entities( draws[i] = call; } +#[spirv(compute(threads(32)))] +/// A shader to ensure that we can extract i8 and i16 values from a storage buffer. +pub fn test_i8_i16_extraction( + #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] slab: &mut [u32], + #[spirv(global_invocation_id)] global_id: UVec3, +) { + let index = global_id.x as usize; + let (value, _, _) = crate::bits::extract_i8(index, 2, slab); + if value > 0 { + slab[index] = value as u32; + } + let (value, _, _) = crate::bits::extract_i16(index, 2, slab); + if value > 0 { + slab[index] = value as u32; + } +} + #[cfg(test)] mod test { use crate::{self as renderling_shader, id::Id, slab::Slab}; diff --git a/crates/renderling-shader/src/tutorial.rs b/crates/renderling-shader/src/tutorial.rs index 5bbe64cd..e3ddff8d 100644 --- a/crates/renderling-shader/src/tutorial.rs +++ b/crates/renderling-shader/src/tutorial.rs @@ -99,10 +99,9 @@ pub fn slabbed_render_unit( ) { let unit_id = Id::::from(instance_index); let unit = slab.read(unit_id); - let vertex_id = unit.vertices.at(vertex_index as usize); - let vertex = slab.read(vertex_id); + let (vertex, tfrm, _) = unit.get_vertex_details(vertex_index, slab); let camera = slab.read(unit.camera); - let model = Mat4::from_scale_rotation_translation(unit.scale, unit.rotation, unit.position); + let model = Mat4::from_scale_rotation_translation(tfrm.scale, tfrm.rotation, tfrm.translation); *clip_pos = camera.projection * camera.view * model * vertex.position.xyz().extend(1.0); *out_color = vertex.color; } diff --git a/crates/renderling/src/linkage/stage-new_stage_vertex.spv b/crates/renderling/src/linkage/stage-new_stage_vertex.spv index d98b0bd1..7b194d66 100644 Binary files a/crates/renderling/src/linkage/stage-new_stage_vertex.spv and b/crates/renderling/src/linkage/stage-new_stage_vertex.spv differ diff --git a/crates/renderling/src/linkage/stage-stage_fragment.spv b/crates/renderling/src/linkage/stage-stage_fragment.spv index 03728627..e9c5b00e 100644 Binary files a/crates/renderling/src/linkage/stage-stage_fragment.spv and b/crates/renderling/src/linkage/stage-stage_fragment.spv differ diff --git a/crates/renderling/src/linkage/stage-test_i8_i16_extraction.spv b/crates/renderling/src/linkage/stage-test_i8_i16_extraction.spv new file mode 100644 index 00000000..d685467f Binary files /dev/null and b/crates/renderling/src/linkage/stage-test_i8_i16_extraction.spv differ diff --git a/crates/renderling/src/linkage/tutorial-implicit_isosceles_vertex.spv b/crates/renderling/src/linkage/tutorial-implicit_isosceles_vertex.spv index 1afdac93..d9155e82 100644 Binary files a/crates/renderling/src/linkage/tutorial-implicit_isosceles_vertex.spv and b/crates/renderling/src/linkage/tutorial-implicit_isosceles_vertex.spv differ diff --git a/crates/renderling/src/linkage/tutorial-slabbed_render_unit.spv b/crates/renderling/src/linkage/tutorial-slabbed_render_unit.spv index fe78cbc0..e904b51a 100644 Binary files a/crates/renderling/src/linkage/tutorial-slabbed_render_unit.spv and b/crates/renderling/src/linkage/tutorial-slabbed_render_unit.spv differ diff --git a/crates/renderling/src/linkage/tutorial-slabbed_vertices.spv b/crates/renderling/src/linkage/tutorial-slabbed_vertices.spv index 880e361f..960ffb21 100644 Binary files a/crates/renderling/src/linkage/tutorial-slabbed_vertices.spv and b/crates/renderling/src/linkage/tutorial-slabbed_vertices.spv differ diff --git a/crates/renderling/src/scene/gltf_support.rs b/crates/renderling/src/scene/gltf_support.rs index 8731dc8b..0c072f3a 100644 --- a/crates/renderling/src/scene/gltf_support.rs +++ b/crates/renderling/src/scene/gltf_support.rs @@ -742,13 +742,24 @@ impl GltfLoader { vertices.chunks_mut(3).for_each(|t| match t { [(a, _), (b, _), (c, _)] => { if gen_normals { - let n = Vertex::generate_normal(*a, *b, *c); + let n = Vertex::generate_normal( + a.position.xyz(), + b.position.xyz(), + c.position.xyz(), + ); a.normal = n.extend(a.normal.w); b.normal = n.extend(b.normal.w); c.normal = n.extend(c.normal.w); } if gen_tangents { - let tangent = Vertex::generate_tangent(*a, *b, *c); + let tangent = Vertex::generate_tangent( + a.position.xyz(), + a.uv.xy(), + b.position.xyz(), + b.uv.xy(), + c.position.xyz(), + c.uv.xy(), + ); debug_assert!(!tangent.w.is_nan(), "tangent is NaN"); a.tangent = tangent; b.tangent = tangent; @@ -1247,56 +1258,6 @@ mod test { img_diff::assert_img_eq("gltf_images.png", img); } - #[test] - // Ensures we can read a minimal gltf file with a simple triangle mesh. - fn minimal_mesh() { - let mut r = - Renderling::headless(20, 20).with_background_color(Vec3::splat(0.0).extend(1.0)); - let mut builder = r.new_scene(); - let _loader = builder - .gltf_load("../../gltf/gltfTutorial_003_MinimalGltfFile.gltf") - .unwrap(); - let projection = camera::perspective(20.0, 20.0); - let view = camera::look_at(Vec3::new(0.5, 0.5, 2.0), Vec3::new(0.5, 0.5, 0.0), Vec3::Y); - builder.set_camera(projection, view); - let scene = builder.build().unwrap(); - r.setup_render_graph(RenderGraphConfig { - scene: Some(scene), - with_screen_capture: true, - ..Default::default() - }); - - let img = r.render_image().unwrap(); - img_diff::assert_img_eq("gltf_minimal_mesh.png", img); - } - - #[test] - // ensures we can - // * read simple meshes - // * support multiple nodes that reference the same mesh - // * support primitives w/ positions and normal attributes - // * support transforming nodes (T * R * S) - fn simple_meshes() { - let mut r = - Renderling::headless(100, 50).with_background_color(Vec3::splat(0.0).extend(1.0)); - let mut builder = r.new_scene(); - let _loader = builder - .gltf_load("../../gltf/gltfTutorial_008_SimpleMeshes.gltf") - .unwrap(); - let projection = camera::perspective(100.0, 50.0); - let view = camera::look_at(Vec3::new(1.0, 0.5, 1.5), Vec3::new(1.0, 0.5, 0.0), Vec3::Y); - builder.set_camera(projection, view); - let scene = builder.build().unwrap(); - r.setup_render_graph(RenderGraphConfig { - scene: Some(scene), - with_screen_capture: true, - ..Default::default() - }); - - let img = r.render_image().unwrap(); - img_diff::assert_img_eq("gltf_simple_meshes.png", img); - } - #[test] fn simple_texture() { let size = 100; diff --git a/crates/renderling/src/slab.rs b/crates/renderling/src/slab.rs index 63c3c569..1a6aaa28 100644 --- a/crates/renderling/src/slab.rs +++ b/crates/renderling/src/slab.rs @@ -374,6 +374,8 @@ impl SlabBuffer { #[cfg(test)] mod test { + use renderling_shader::stage::{NativeVertexData, RenderUnit, Vertex, VertexData}; + use crate::Renderling; use super::*; @@ -426,4 +428,36 @@ mod test { let points_out = slab_u32.read_vec::(array); assert_eq!(points, points_out); } + + #[test] + fn slab_buffer_unit_roundtrip() { + let _ = env_logger::builder().is_test(true).try_init(); + let r = Renderling::headless(10, 10); + let device = r.get_device(); + let queue = r.get_queue(); + let slab = SlabBuffer::new(device, 2); + let vertices = vec![ + Vertex::default().with_position([0.0, 0.0, 0.0]), + Vertex::default().with_position([1.0, 1.0, 1.0]), + Vertex::default().with_position([2.0, 2.0, 2.0]), + ]; + let vertices = slab.append_array(device, queue, &vertices); + let data_id = slab.append( + device, + queue, + &NativeVertexData { + vertices, + material: Id::new(666), + }, + ); + let unit = RenderUnit { + vertex_data: VertexData::Native(data_id), + camera: Id::new(42), + transform: Id::new(1337), + vertex_count: vertices.len() as u32, + }; + let unit_id = slab.append(device, queue, &unit); + let t = futures_lite::future::block_on(slab.read(device, queue, unit_id)).unwrap(); + assert_eq!(unit, t, "read back what we wrote"); + } } diff --git a/crates/renderling/src/stage.rs b/crates/renderling/src/stage.rs index 7817d8c3..8348ddc7 100644 --- a/crates/renderling/src/stage.rs +++ b/crates/renderling/src/stage.rs @@ -81,7 +81,7 @@ impl Stage { /// Write an object to the slab. pub fn write(&self, id: Id, object: &T) -> Result<(), SlabError> { - let () = self.slab.write(&self.device, &self.queue, id, object)?; + self.slab.write(&self.device, &self.queue, id, object)?; Ok(()) } @@ -550,13 +550,12 @@ impl Stage { } } - /// Append the [`RenderUnit`] and return a [`DrawUnit`] that can be used - /// to draw it. + /// Draw the [`RenderUnit`] each frame, and immediately return its `Id`. pub fn draw_unit(&self, unit: &RenderUnit) -> Id { let id = self.slab.append(&self.device, &self.queue, unit); let draw = DrawUnit { id, - vertex_count: unit.vertices.len() as u32, + vertex_count: unit.vertex_count, }; // UNWRAP: if we can't acquire the lock we want to panic. let mut draws = self.draws.write().unwrap(); @@ -568,6 +567,25 @@ impl Stage { id } + /// Returns all the draw operations on the stage. + pub(crate) fn get_draws(&self) -> Vec { + // UNWRAP: if we can't acquire the lock we want to panic. + let draws = self.draws.read().unwrap(); + match draws.deref() { + StageDrawStrategy::Direct(units) => units.clone(), + } + } + + /// Erase the [`RenderUnit`] with the given `Id` from the stage. + pub fn erase_unit(&self, id: Id) { + let mut draws = self.draws.write().unwrap(); + match draws.deref_mut() { + StageDrawStrategy::Direct(units) => { + units.retain(|unit| unit.id != id); + } + } + } + /// Configure [`Renderling`] to render this stage. pub fn configure_graph(&self, r: &mut crate::Renderling, should_copy_frame_to_post: bool) { // set up the render graph @@ -610,6 +628,7 @@ impl Stage { } /// A unit of work to be drawn. +#[derive(Clone, Copy, Debug, Default)] pub(crate) struct DrawUnit { pub id: Id, pub vertex_count: u32, @@ -691,7 +710,7 @@ mod test { use crate::{ default_ortho2d, - shader::stage::{Camera, RenderUnit, Vertex}, + shader::stage::{Camera, NativeVertexData, RenderUnit, Vertex, VertexData}, slab::Slab, Renderling, }; @@ -728,9 +747,14 @@ mod test { let camera_id = stage.append(&camera); let vertices = stage.append_array(&right_tri_vertices()); println!("vertices: {vertices:?}"); + let vertex_data_id = stage.append(&NativeVertexData { + vertices, + ..Default::default() + }); let _ = stage.draw_unit(&RenderUnit { camera: camera_id, - vertices, + vertex_data: VertexData::Native(vertex_data_id), + vertex_count: 3, ..Default::default() }); let stage_slab = futures_lite::future::block_on(stage.slab.read_raw( diff --git a/crates/renderling/src/stage/gltf_support.rs b/crates/renderling/src/stage/gltf_support.rs index 1a37e173..a908e0b9 100644 --- a/crates/renderling/src/stage/gltf_support.rs +++ b/crates/renderling/src/stage/gltf_support.rs @@ -4,12 +4,13 @@ use crate::{ shader::{ gltf::*, pbr::PbrMaterial, - stage::LightingModel, + stage::{Camera, GltfVertexData, LightingModel, VertexData}, texture::{GpuTexture, TextureAddressMode, TextureModes}, }, SceneImage, }; -use glam::{Quat, Vec3, Vec4}; +use glam::{Quat, Vec2, Vec3, Vec4}; +use renderling_shader::stage::{Transform, Vertex}; use snafu::{OptionExt, ResultExt, Snafu}; #[derive(Debug, Snafu)] @@ -51,10 +52,66 @@ impl From for StageGltfError { } } +pub fn get_vertex_count(primitive: &gltf::Primitive<'_>) -> u32 { + if let Some(indices) = primitive.indices() { + let count = indices.count() as u32; + log::trace!(" has {count} indices"); + count + } else { + if let Some(positions) = primitive.get(&gltf::Semantic::Positions) { + let count = positions.count() as u32; + log::trace!(" has {count} positions"); + count + } else { + log::trace!(" has no indices nor positions"); + 0 + } + } +} + +pub fn make_accessor(accessor: gltf::Accessor<'_>, buffers: &Array) -> GltfAccessor { + let size = accessor.size() as u32; + let buffer_view = accessor.view().unwrap(); + let view_buffer = buffer_view.buffer(); + let buffer_index = view_buffer.index(); + let buffer = buffers.at(buffer_index); + let count = accessor.count() as u32; + let view_offset = buffer_view.offset() as u32; + let view_stride = buffer_view.stride().unwrap_or(0) as u32; + let component_type = match accessor.data_type() { + gltf::accessor::DataType::I8 => DataType::I8, + gltf::accessor::DataType::U8 => DataType::U8, + gltf::accessor::DataType::I16 => DataType::I16, + gltf::accessor::DataType::U16 => DataType::U16, + gltf::accessor::DataType::U32 => DataType::U32, + gltf::accessor::DataType::F32 => DataType::F32, + }; + let dimensions = match accessor.dimensions() { + gltf::accessor::Dimensions::Scalar => Dimensions::Scalar, + gltf::accessor::Dimensions::Vec2 => Dimensions::Vec2, + gltf::accessor::Dimensions::Vec3 => Dimensions::Vec3, + gltf::accessor::Dimensions::Vec4 => Dimensions::Vec4, + gltf::accessor::Dimensions::Mat2 => Dimensions::Mat2, + gltf::accessor::Dimensions::Mat3 => Dimensions::Mat3, + gltf::accessor::Dimensions::Mat4 => Dimensions::Mat4, + }; + let normalized = accessor.normalized(); + GltfAccessor { + size, + count, + buffer, + view_offset, + view_stride, + data_type: component_type, + dimensions, + normalized, + } +} + impl Stage { pub fn load_gltf_document( &self, - document: gltf::Document, + document: &gltf::Document, buffer_data: Vec, images: Vec, ) -> Result { @@ -69,64 +126,28 @@ impl Stage { log::trace!("Loading views into the GPU"); let views = self.allocate_array(document.views().len()); - for view in document.views() { + log::trace!(" reserved array: {views:?}"); + for (i, view) in document.views().enumerate() { let buffer = buffers.at(view.buffer().index()); let offset = view.offset() as u32; let length = view.length() as u32; let stride = view.stride().unwrap_or_default() as u32; - self.write( - views.at(view.index()), - &GltfBufferView { - buffer, - offset, - length, - stride, - }, - )?; + let id = views.at(view.index()); + let gltf_view = GltfBufferView { + buffer, + offset, + length, + stride, + }; + log::trace!(" view {i} id: {id:#?}"); + log::trace!(" writing view: {gltf_view:#?}"); + self.write(id, &gltf_view)?; } log::trace!("Loading accessors into the GPU"); let accessors = document .accessors() - .map(|accessor| { - let size = accessor.size() as u32; - let buffer_view = accessor.view().unwrap(); - let view_buffer = buffer_view.buffer(); - let buffer_index = view_buffer.index(); - let buffer = buffers.at(buffer_index); - let count = accessor.count() as u32; - let view_offset = buffer_view.offset() as u32; - let view_stride = buffer_view.stride().unwrap_or(0) as u32; - let component_type = match accessor.data_type() { - gltf::accessor::DataType::I8 => DataType::I8, - gltf::accessor::DataType::U8 => DataType::U8, - gltf::accessor::DataType::I16 => DataType::I16, - gltf::accessor::DataType::U16 => DataType::U16, - gltf::accessor::DataType::U32 => DataType::U32, - gltf::accessor::DataType::F32 => DataType::F32, - }; - let dimensions = match accessor.dimensions() { - gltf::accessor::Dimensions::Scalar => Dimensions::Scalar, - gltf::accessor::Dimensions::Vec2 => Dimensions::Vec2, - gltf::accessor::Dimensions::Vec3 => Dimensions::Vec3, - gltf::accessor::Dimensions::Vec4 => Dimensions::Vec4, - gltf::accessor::Dimensions::Mat2 => Dimensions::Mat2, - gltf::accessor::Dimensions::Mat3 => Dimensions::Mat3, - gltf::accessor::Dimensions::Mat4 => Dimensions::Mat4, - }; - let normalized = accessor.normalized(); - let accessor = GltfAccessor { - size, - count, - buffer, - view_offset, - view_stride, - component_type, - dimensions, - normalized, - }; - accessor - }) + .map(|accessor| make_accessor(accessor, &buffers)) .collect::>(); let accessors = self.append_array(&accessors); @@ -370,12 +391,22 @@ impl Stage { *atlas = new_atlas; } + fn log_accessor(gltf_accessor: gltf::Accessor<'_>) { + log::trace!(" count: {}", gltf_accessor.count()); + log::trace!(" size: {}", gltf_accessor.size()); + log::trace!(" data_type: {:?}", gltf_accessor.data_type()); + log::trace!(" dimensions: {:?}", gltf_accessor.dimensions()); + } log::trace!("Loading meshes"); let meshes = self.allocate_array::(document.meshes().len()); + log::trace!(" reserved array: {meshes:#?}"); for mesh in document.meshes() { let primitives = self.allocate_array::(mesh.primitives().len()); + log::trace!(" reserved array: {primitives:#?}"); for (j, primitive) in mesh.primitives().enumerate() { + log::trace!(" primitive {j}"); debug_assert_eq!(j, primitive.index()); + let vertex_count = get_vertex_count(&primitive); let material = primitive .material() .index() @@ -383,56 +414,223 @@ impl Stage { .unwrap_or(Id::NONE); let indices = primitive .indices() - .map(|acc| accessors.at(acc.index())) + .map(|acc| { + let gltf_accessor = document.accessors().nth(acc.index()).unwrap(); + log::trace!(" indices:"); + log_accessor(gltf_accessor); + accessors.at(acc.index()) + }) .unwrap_or_default(); let positions = primitive .get(&gltf::Semantic::Positions) - .map(|acc| accessors.at(acc.index())) + .map(|acc| { + let gltf_accessor = document.accessors().nth(acc.index()).unwrap(); + log::trace!(" positions:"); + log_accessor(gltf_accessor); + accessors.at(acc.index()) + }) .unwrap_or_default(); + + // We may need the positions and uvs in-memory if we need + // to generate normals or tangents, so we'll keep them in + // a vec, if necessary, and access them through a function. + let mut position_vec: Option> = None; + fn get_positions<'a>( + buffer_data: &[gltf::buffer::Data], + primitive: &gltf::Primitive<'_>, + position_vec: &'a mut Option>, + ) -> &'a Vec { + if position_vec.is_none() { + let reader = primitive.reader(|buffer| { + let data = buffer_data.get(buffer.index())?; + Some(data.0.as_slice()) + }); + let positions = reader + .read_positions() + .map(|ps| ps.map(Vec3::from).collect::>()) + .unwrap_or_default(); + *position_vec = Some(positions); + } + // UNWRAP: safe because we just set it to `Some` if previously `None` + position_vec.as_ref().unwrap() + } + + let mut positions_and_uv_vec: Option> = None; + fn get_uvs<'a>( + buffer_data: &[gltf::buffer::Data], + primitive: &gltf::Primitive<'_>, + positions: &'a mut Option>, + positions_and_uv_vec: &'a mut Option>, + ) -> &'a Vec<(Vec3, Vec2)> { + // ensures we have position + if positions_and_uv_vec.is_none() { + let positions = get_positions(buffer_data, primitive, positions); + let reader = primitive.reader(|buffer| { + let data = buffer_data.get(buffer.index())?; + Some(data.0.as_slice()) + }); + let puvs: Vec<(Vec3, Vec2)> = reader + .read_tex_coords(0) + .map(|uvs| { + positions + .iter() + .copied() + .zip(uvs.into_f32().map(Vec2::from)) + .collect() + }) + .unwrap_or_else(|| { + positions + .iter() + .copied() + .zip(std::iter::repeat(Vec2::ZERO)) + .collect() + }); + *positions_and_uv_vec = Some(puvs); + } + // UNWRAP: safe because we just set it to `Some` + positions_and_uv_vec.as_ref().unwrap() + } + let normals = primitive .get(&gltf::Semantic::Normals) - .map(|acc| accessors.at(acc.index())) - .unwrap_or_default(); + .map(|acc| { + let gltf_accessor = document.accessors().nth(acc.index()).unwrap(); + log::trace!(" normals:"); + log_accessor(gltf_accessor); + accessors.at(acc.index()) + }) + .unwrap_or_else(|| { + log::trace!(" generating normals"); + // Generate the normals + let normals = get_positions(&buffer_data, &primitive, &mut position_vec) + .chunks(3) + .flat_map(|chunk| match chunk { + [a, b, c] => { + let n = Vertex::generate_normal(*a, *b, *c); + [n, n, n] + } + _ => panic!("not triangles!"), + }) + .collect::>(); + let normals_array = self.append_array(&normals); + let buffer = GltfBuffer(normals_array.into_u32_array()); + let buffer_id = self.append(&buffer); + let accessor = GltfAccessor { + size: 12, + buffer: buffer_id, + view_offset: 0, + view_stride: 12, + count: normals.len() as u32, + data_type: DataType::F32, + dimensions: Dimensions::Vec3, + normalized: true, + }; + self.append(&accessor) + }); let tangents = primitive .get(&gltf::Semantic::Tangents) - .map(|acc| accessors.at(acc.index())) - .unwrap_or_default(); + .map(|acc| { + let gltf_accessor = document.accessors().nth(acc.index()).unwrap(); + log::trace!(" tangents:"); + log_accessor(gltf_accessor); + accessors.at(acc.index()) + }) + .unwrap_or_else(|| { + log::trace!(" generating tangents"); + let p_uvs = get_uvs( + &buffer_data, + &primitive, + &mut position_vec, + &mut positions_and_uv_vec, + ); + let tangents = p_uvs + .chunks(3) + .flat_map(|chunk| match chunk { + [(a, a_uv), (b, b_uv), (c, c_uv)] => { + let t = + Vertex::generate_tangent(*a, *a_uv, *b, *b_uv, *c, *c_uv); + [t, t, t] + } + _ => panic!("not triangles!"), + }) + .collect::>(); + let tangents_array = self.append_array(&tangents); + let buffer = GltfBuffer(tangents_array.into_u32_array()); + let buffer_id = self.append(&buffer); + let accessor = GltfAccessor { + size: 12, + buffer: buffer_id, + view_offset: 0, + view_stride: 12, + count: tangents.len() as u32, + data_type: DataType::F32, + dimensions: Dimensions::Vec3, + normalized: true, + }; + self.append(&accessor) + }); let colors = primitive .get(&gltf::Semantic::Colors(0)) - .map(|acc| accessors.at(acc.index())) + .map(|acc| { + let gltf_accessor = document.accessors().nth(acc.index()).unwrap(); + log::trace!(" colors:"); + log_accessor(gltf_accessor); + accessors.at(acc.index()) + }) .unwrap_or_default(); let tex_coords0 = primitive .get(&gltf::Semantic::TexCoords(0)) - .map(|acc| accessors.at(acc.index())) + .map(|acc| { + let gltf_accessor = document.accessors().nth(acc.index()).unwrap(); + log::trace!(" tex_coords0:"); + log_accessor(gltf_accessor); + accessors.at(acc.index()) + }) .unwrap_or_default(); let tex_coords1 = primitive .get(&gltf::Semantic::TexCoords(1)) - .map(|acc| accessors.at(acc.index())) + .map(|acc| { + let gltf_accessor = document.accessors().nth(acc.index()).unwrap(); + log::trace!(" tex_coords1:"); + log_accessor(gltf_accessor); + accessors.at(acc.index()) + }) .unwrap_or_default(); let joints = primitive .get(&gltf::Semantic::Joints(0)) - .map(|acc| accessors.at(acc.index())) + .map(|acc| { + let gltf_accessor = document.accessors().nth(acc.index()).unwrap(); + log::trace!(" joints:"); + log_accessor(gltf_accessor); + accessors.at(acc.index()) + }) .unwrap_or_default(); let weights = primitive .get(&gltf::Semantic::Weights(0)) - .map(|acc| accessors.at(acc.index())) + .map(|acc| { + let gltf_accessor = document.accessors().nth(acc.index()).unwrap(); + log::trace!(" weights:"); + log_accessor(gltf_accessor); + accessors.at(acc.index()) + }) .unwrap_or_default(); - self.write( - primitives.at(primitive.index()), - &GltfPrimitive { - material, - indices, - positions, - normals, - tangents, - colors, - tex_coords0, - tex_coords1, - joints, - weights, - }, - )?; + let id = primitives.at(primitive.index()); + let prim = GltfPrimitive { + vertex_count: vertex_count as u32, + material, + indices, + positions, + normals, + tangents, + colors, + tex_coords0, + tex_coords1, + joints, + weights, + }; + log::trace!(" writing primitive {id:?}:\n{prim:#?}"); + self.write(id, &prim)?; } let weights = mesh.weights().unwrap_or(&[]); let weights = self.append_array(weights); @@ -646,27 +844,332 @@ impl Stage { views, }) } + + // For now we have to keep the original document around to figure out + // what to draw. + fn draw_gltf_node_with<'a>( + &self, + gpu_doc: &GltfDocument, + camera_id: Id, + node: gltf::Node<'a>, + parents: Vec>, + ) -> Vec> { + let mut units = if let Some(mesh) = node.mesh() { + let primitives = mesh.primitives(); + let mesh = gpu_doc.meshes.at(mesh.index()); + primitives + .map(|primitive| { + let parent_node_path = self.append_array(&parents); + let vertex_data_id = self.append(&GltfVertexData { + parent_node_path, + mesh, + primitive_index: primitive.index() as u32, + }); + let (t, r, s) = node.transform().decomposed(); + let transform = self.append(&Transform { + translation: Vec3::from(t), + rotation: Quat::from_array(r), + scale: Vec3::from(s), + }); + let render_unit = RenderUnit { + vertex_data: VertexData::Gltf(vertex_data_id), + vertex_count: super::get_vertex_count(&primitive), + transform, + camera: camera_id, + }; + self.draw_unit(&render_unit) + }) + .collect::>() + } else { + vec![] + }; + for child in node.children() { + let mut parents = parents.clone(); + parents.push(gpu_doc.nodes.at(child.index())); + units.extend(self.draw_gltf_node_with(gpu_doc, camera_id, child, parents)); + } + units + } + + /// Draw the given [`gltf::Node`] using the given [`Camera`] and return the ids of the + /// render units that were created. + pub fn draw_gltf_node( + &self, + gpu_doc: &GltfDocument, + camera_id: Id, + node: gltf::Node<'_>, + ) -> Vec> { + self.draw_gltf_node_with(gpu_doc, camera_id, node, vec![]) + } + + /// Draw the given [`gltf::Scene`] using the given [`Camera`] and return the ids of the + /// render units that were created. + pub fn draw_gltf_scene( + &self, + gpu_doc: &GltfDocument, + camera_id: Id, + scene: gltf::Scene<'_>, + ) -> Vec> { + scene + .nodes() + .flat_map(|node| self.draw_gltf_node(gpu_doc, camera_id, node)) + .collect() + } } #[cfg(test)] mod test { - use glam::Vec3; + use glam::{Vec2, Vec3, Vec4}; + + use crate::{ + shader::{ + array::Array, + gltf::*, + slab::Slab, + stage::{Camera, RenderUnit}, + }, + DrawUnit, Id, Renderling, Stage, + }; - use crate::{Renderling, Stage}; + #[test] + fn get_vertex_count_primitive_sanity() { + let (document, _, _) = + gltf::import("../../gltf/gltfTutorial_008_SimpleMeshes.gltf").unwrap(); + let prim = document + .meshes() + .next() + .unwrap() + .primitives() + .next() + .unwrap(); + let vertex_count = super::get_vertex_count(&prim); + assert_eq!(3, vertex_count); + } #[test] - fn stage_normal_mapping_brick_sphere() { - crate::init_logging(); - let size = 600; + fn accessor_sanity() { + println!("{:08b}", 1u8); + println!("{:08b}", 1i8); + println!("{:08b}", -1i8); + println!("{} {}", u8::MAX, i8::MAX); + let u16buffer = [1u16, 1u16, 1u16, 1u16]; + for chunk in u16buffer.chunks(2) { + match chunk { + [a, b] => { + println!("{a:016b} {b:016b}"); + } + _ => panic!("bad chunk"), + } + } + let u32buffer = bytemuck::cast_slice::(&u16buffer).to_vec(); // + for u in u32buffer.iter() { + println!("{u:032b}"); + } + println!("u32buffer: {u32buffer:?}"); + assert_eq!(2, u32buffer.len()); + let mut data = [0u32; 256]; + let buffer_index = data.write_slice(&u32buffer, 0); + assert_eq!(2, buffer_index); + let buffer = GltfBuffer(Array::new(0, buffer_index as u32)); + let _ = data.write(&buffer, buffer_index); + let accessor = GltfAccessor { + size: 2, + count: 3, + buffer: Id::from(buffer_index), + view_offset: 0, + view_stride: 0, + data_type: DataType::U16, + dimensions: Dimensions::Scalar, + normalized: false, + }; + let i0 = accessor.get_u32(0, &data); + assert_eq!(1, i0); + let i1 = accessor.get_u32(1, &data); + assert_eq!(1, i1); + let i2 = accessor.get_u32(2, &data); + assert_eq!(1, i2); + } + + #[test] + // ensures we can + // * read simple meshes + // * support multiple nodes that reference the same mesh + // * support primitives w/ positions and normal attributes + // * support transforming nodes (T * R * S) + fn stage_gltf_simple_meshes() { let mut r = - Renderling::headless(size, size).with_background_color(Vec3::splat(1.0).extend(1.0)); + Renderling::headless(100, 50).with_background_color(Vec3::splat(0.0).extend(1.0)); let (device, queue) = r.get_device_and_queue_owned(); - let stage = Stage::new(device, queue); + let (document, buffers, images) = + gltf::import("../../gltf/gltfTutorial_008_SimpleMeshes.gltf").unwrap(); + let projection = crate::camera::perspective(100.0, 50.0); + let position = Vec3::new(1.0, 0.5, 1.5); + let view = crate::camera::look_at(position, Vec3::new(1.0, 0.5, 0.0), Vec3::Y); + let stage = Stage::new(device.clone(), queue.clone()).with_lighting(false); stage.configure_graph(&mut r, true); + let gpu_doc = stage + .load_gltf_document(&document, buffers.clone(), images) + .unwrap(); + let camera = Camera { + projection, + view, + position, + }; + let camera_id = stage.append(&camera); + + let default_scene = document.default_scene().unwrap(); + let unit_ids = stage.draw_gltf_scene(&gpu_doc, camera_id, default_scene); + assert_eq!(2, unit_ids.len()); + + let data = futures_lite::future::block_on(stage.slab.read_raw( + &device, + &queue, + 0, + stage.slab.len(), + )) + .unwrap(); + + #[allow(unused)] + #[derive(Debug, Default)] + struct VertexInvocation { + draw: DrawUnit, + instance_index: u32, + vertex_index: u32, + render_unit_id: Id, + render_unit: RenderUnit, + out_camera: u32, + out_material: u32, + out_color: Vec4, + out_uv0: Vec2, + out_uv1: Vec2, + out_norm: Vec3, + out_tangent: Vec3, + out_bitangent: Vec3, + out_pos: Vec3, + clip_pos: Vec4, + } + + let draws = stage.get_draws(); + let slab = &data; + + for i in 0..gpu_doc.accessors.len() { + let accessor = slab.read(gpu_doc.accessors.at(i)); + println!("accessor {i}: {accessor:#?}", i = i, accessor = accessor); + let buffer = slab.read(accessor.buffer); + println!("buffer: {buffer:#?}"); + let buffer_data = slab.read_vec(buffer.0); + println!("buffer_data: {buffer_data:#?}"); + } + + let indices = draws + .iter() + .map(|draw| { + let unit_id = draw.id; + let unit = slab.read(unit_id); + let vertex_data_id = match unit.vertex_data { + renderling_shader::stage::VertexData::Native(_) => panic!("should be gltf"), + renderling_shader::stage::VertexData::Gltf(id) => id, + }; + let vertex_data = slab.read(vertex_data_id); + let mesh = slab.read(vertex_data.mesh); + let primitive_id = mesh.primitives.at(vertex_data.primitive_index as usize); + let primitive = slab.read(primitive_id); + if primitive.indices.is_some() { + let indices_accessor = slab.read(primitive.indices); + (0..draw.vertex_count) + .map(|i| { + let index = indices_accessor.get_u32(i as usize, slab); + index + }) + .collect::>() + } else { + (0..draw.vertex_count).collect::>() + } + }) + .collect::>(); + assert_eq!([0, 1, 2], indices[0].as_slice()); + assert_eq!([0, 1, 2], indices[1].as_slice()); + + let invocations = draws + .into_iter() + .flat_map(|draw| { + let render_unit_id = draw.id; + let instance_index = render_unit_id.inner(); + let render_unit = data.read(render_unit_id); + let data = &data; + (0..draw.vertex_count).map(move |vertex_index| { + let mut invocation = VertexInvocation { + draw, + render_unit_id, + render_unit, + instance_index, + vertex_index, + ..Default::default() + }; + renderling_shader::stage::new_stage_vertex( + instance_index, + vertex_index, + data, + &mut invocation.out_camera, + &mut invocation.out_material, + &mut invocation.out_color, + &mut invocation.out_uv0, + &mut invocation.out_uv1, + &mut invocation.out_norm, + &mut invocation.out_tangent, + &mut invocation.out_bitangent, + &mut invocation.out_pos, + &mut invocation.clip_pos, + ); + invocation + }) + }) + .collect::>(); + let seen_positions = invocations + .iter() + .map(|inv| inv.out_pos) + .take(3) + .collect::>(); + let mesh = document.meshes().next().unwrap(); + let prim = mesh.primitives().next().unwrap(); + let expected_positions_reader = prim.reader(|buffer| Some(&buffers[buffer.index()])); + let expected_positions = expected_positions_reader + .read_positions() + .unwrap() + .map(|pos| Vec3::from(pos)) + .collect::>(); + assert_eq!(expected_positions, seen_positions); + + let img = r.render_image().unwrap(); + img_diff::assert_img_eq("gltf_simple_meshes.png", img); + } + + #[test] + // Ensures we can read a minimal gltf file with a simple triangle mesh. + fn minimal_mesh() { + let mut r = + Renderling::headless(20, 20).with_background_color(Vec3::splat(0.0).extend(1.0)); + let (device, queue) = r.get_device_and_queue_owned(); + let stage = Stage::new(device, queue).with_lighting(false); + stage.configure_graph(&mut r, true); + let (document, buffers, images) = + gltf::import("../../gltf/gltfTutorial_003_MinimalGltfFile.gltf").unwrap(); + let gpu_doc = stage + .load_gltf_document(&document, buffers, images) + .unwrap(); + let projection = crate::camera::perspective(20.0, 20.0); + let eye = Vec3::new(0.5, 0.5, 2.0); + let view = crate::camera::look_at(eye, Vec3::new(0.5, 0.5, 0.0), Vec3::Y); + let camera = Camera { + projection, + view, + position: Vec3::new(0.5, 0.5, 2.0), + }; + let camera_id = stage.append(&camera); + let default_scene = document.default_scene().unwrap(); + let _unit_ids = stage.draw_gltf_scene(&gpu_doc, camera_id, default_scene); - log::trace!("Reading gltf"); - let (document, buffers, images) = gltf::import("../../gltf/red_brick_03_1k.glb").unwrap(); - log::trace!("Loading gltf"); - let _doc = stage.load_gltf_document(document, buffers, images).unwrap(); + let img = r.render_image().unwrap(); + img_diff::assert_img_eq("gltf_minimal_mesh.png", img); } } diff --git a/crates/renderling/src/tutorial.rs b/crates/renderling/src/tutorial.rs index 1cbf8239..61bbfb73 100644 --- a/crates/renderling/src/tutorial.rs +++ b/crates/renderling/src/tutorial.rs @@ -2,7 +2,8 @@ #[cfg(test)] mod test { - use glam::{Vec3, Vec4}; + use glam::{Mat4, Quat, Vec3, Vec4}; + use renderling_shader::slab::Slab; use crate::{ frame::FrameTextureView, @@ -10,7 +11,7 @@ mod test { shader::{ array::Array, id::Id, - stage::{Camera, RenderUnit, Vertex}, + stage::{Camera, NativeVertexData, RenderUnit, Transform, Vertex, VertexData}, }, DepthTexture, Device, Queue, Renderling, }; @@ -520,14 +521,32 @@ mod test { }, ]; let vertices = slab.append_array(&device, &queue, &geometry); + let vertex_data_id = slab.append( + &device, + &queue, + &NativeVertexData { + vertices, + ..Default::default() + }, + ); let unit = RenderUnit { - vertices, + vertex_data: VertexData::Native(vertex_data_id), ..Default::default() }; let unit_id = slab.append(&device, &queue, &unit); + let data = + futures_lite::future::block_on(slab.read_raw(&device, &queue, 0, slab.len())).unwrap(); + let (vertex, t, _) = unit.get_vertex_details(0, &data); + assert_eq!(geometry[0], vertex); + assert_eq!(Vec3::ZERO, t.translation); + assert_eq!(Quat::IDENTITY, t.rotation); + assert_eq!(Vec3::ONE, t.scale); + let camera = data.read(unit.camera); + assert_eq!(Mat4::IDENTITY, camera.projection); + assert_eq!(Mat4::IDENTITY, camera.view); // Create a bindgroup for the slab so our shader can read out the types. - let label = Some("slabbed isosceles triangle"); + let label = Some("slabbed render unit"); let bindgroup_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label, entries: &[wgpu::BindGroupLayoutEntry { @@ -605,14 +624,14 @@ mod test { pipeline: wgpu::RenderPipeline, bindgroup: wgpu::BindGroup, unit_id: Id, - unit: RenderUnit, + unit_vertex_count: u32, } let app = App { pipeline, bindgroup, unit_id, - unit, + unit_vertex_count: vertices.len() as u32, }; r.graph.add_resource(app); @@ -651,7 +670,7 @@ mod test { render_pass.set_pipeline(&app.pipeline); render_pass.set_bind_group(0, &app.bindgroup, &[]); render_pass.draw( - 0..app.unit.vertices.len() as u32, + 0..app.unit_vertex_count, app.unit_id.inner()..app.unit_id.inner() + 1, ); } @@ -723,12 +742,28 @@ mod test { ..Default::default() }, ); + let transform_id = slab.append( + &device, + &queue, + &Transform { + translation: Vec3::new(50.0, 50.0, 0.0), + scale: Vec3::new(50.0, 50.0, 1.0), + ..Default::default() + }, + ); + let vertex_data_id = slab.append( + &device, + &queue, + &NativeVertexData { + vertices, + ..Default::default() + }, + ); let unit = RenderUnit { - vertices, + vertex_data: renderling_shader::stage::VertexData::Native(vertex_data_id), camera: camera_id, - position: Vec3::new(50.0, 50.0, 0.0), - scale: Vec3::new(50.0, 50.0, 1.0), - ..Default::default() + transform: transform_id, + vertex_count: vertices.len() as u32, }; let unit_id = slab.append(&device, &queue, &unit); @@ -811,14 +846,14 @@ mod test { pipeline: wgpu::RenderPipeline, bindgroup: wgpu::BindGroup, unit_id: Id, - unit: RenderUnit, + unit_vertex_count: u32, } let app = App { pipeline, bindgroup, unit_id, - unit, + unit_vertex_count: vertices.len() as u32, }; r.graph.add_resource(app); @@ -857,7 +892,7 @@ mod test { render_pass.set_pipeline(&app.pipeline); render_pass.set_bind_group(0, &app.bindgroup, &[]); render_pass.draw( - 0..app.unit.vertices.len() as u32, + 0..app.unit_vertex_count, app.unit_id.inner()..app.unit_id.inner() + 1, ); }