diff --git a/DEVLOG.md b/DEVLOG.md index 01ca915e..b8ca8c7a 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -1,5 +1,10 @@ # devlog +## Wed Dec 20, 2023 + +I think I'm going to keep going with this idea of making GLTF the internal representation of the +renderer. + ## Tue Dec 19, 2023 ### Thoughts on GLTF diff --git a/crates/renderling-shader/src/array.rs b/crates/renderling-shader/src/array.rs index a69ec262..34088471 100644 --- a/crates/renderling-shader/src/array.rs +++ b/crates/renderling-shader/src/array.rs @@ -4,6 +4,26 @@ use core::marker::PhantomData; use crate::id::Id; use crate::slab::Slabbed; +#[derive(Clone, Copy)] +pub struct ArrayIter { + array: Array, + index: usize, +} + +impl Iterator for ArrayIter { + type Item = Id; + + fn next(&mut self) -> Option { + if self.index >= self.array.len() { + None + } else { + let id = self.array.at(self.index); + self.index += 1; + Some(id) + } + } +} + #[repr(C)] pub struct Array { // u32 offset in the slab @@ -25,6 +45,17 @@ impl Clone for Array { impl Copy for Array {} +/// An `Id` is an `Array` with a length of 1. +impl From> for Array { + fn from(id: Id) -> Self { + Self { + index: id.inner(), + len: 1, + _phantom: PhantomData, + } + } +} + impl core::fmt::Debug for Array { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { if self.is_null() { @@ -122,6 +153,13 @@ impl Array { self.index as usize } + pub fn iter(&self) -> ArrayIter { + ArrayIter { + array: *self, + index: 0, + } + } + /// Convert this array into a `u32` array. pub fn into_u32_array(self) -> Array where diff --git a/crates/renderling-shader/src/gltf.rs b/crates/renderling-shader/src/gltf.rs index fa36286a..99fe9a28 100644 --- a/crates/renderling-shader/src/gltf.rs +++ b/crates/renderling-shader/src/gltf.rs @@ -132,7 +132,7 @@ pub struct GltfAccessor { // /// This may be Id::NONE if the corresponding accessor is sparse. pub view: Id, - // The offset relative to the start of the parent buffer view in bytes. + // The offset relative to the start of the parent **buffer view** in bytes. // // This will be 0 if the corresponding accessor is sparse. pub offset: u32, diff --git a/crates/renderling-shader/src/stage.rs b/crates/renderling-shader/src/stage.rs index 6dc52d80..37b5b7ac 100644 --- a/crates/renderling-shader/src/stage.rs +++ b/crates/renderling-shader/src/stage.rs @@ -1010,18 +1010,6 @@ impl Default for StageLegend { } } -#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] -#[repr(C)] -#[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 an array of `Indices` in the stage's slab. - pub indices: Array, - // Points to a `PbrMaterial` in the stage's slab. - pub material: Id, -} - #[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[repr(C)] #[derive(Default, Clone, Copy, PartialEq, Slabbed)] @@ -1034,53 +1022,6 @@ pub struct GltfVertexData { pub primitive_index: u32, } -#[repr(C)] -#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] -#[derive(Clone, Copy, PartialEq, Slabbed)] -pub struct VertexData { - // The hash of the vertex data. This is used to determine how to read - // the vertex data from the slab. - pub hash: u32, - // The first index of the vertex data in the slab. - pub index: u32, -} - -impl Default for VertexData { - fn default() -> Self { - VertexData { - hash: 0, - index: u32::MAX, - } - } -} - -impl VertexData { - pub const NATIVE: u32 = 0; - pub const GLTF: u32 = 1; - - pub fn new_native(id: Id) -> Self { - Self { - hash: Self::NATIVE, - index: id.into(), - } - } - - pub fn new_gltf(id: Id) -> Self { - Self { - hash: Self::GLTF, - index: id.into(), - } - } - - pub fn is_native(&self) -> bool { - self.hash == Self::NATIVE - } - - pub fn is_gltf(&self) -> bool { - self.hash == Self::GLTF - } -} - #[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[repr(C)] #[derive(Clone, Copy, PartialEq, Slabbed)] @@ -1100,18 +1041,27 @@ impl Default for Transform { } } -/// A fully-computed unit of rendering, roughly meaning a mesh with model matrix -/// transformations. +/// A rendering "command" that draws a single mesh from a top-level node. #[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[repr(C)] #[derive(Default, Clone, Copy, PartialEq, Slabbed)] pub struct RenderUnit { - pub vertex_data: VertexData, + // Which node are we rendering, and what is the path through its + // ancestors to get to it. + pub node_path: Array>, + // Index of the mesh within the child node that we're rendering. + pub mesh_index: u32, + // Index of the primitive within the mesh that we're rendering. + pub primitive_index: u32, // Points to a `Camera` in the stage's slab. pub camera: Id, - // Points to a `Transform` in the stage's slab. + // Points to a top-level `Transform` in the stage's slab. + // + // This is used to transform your GLTF models. pub transform: Id, // Number of vertices to draw for this unit. + // + // This is a cache for convenience on CPU. pub vertex_count: u32, } @@ -1121,45 +1071,34 @@ impl RenderUnit { vertex_index: u32, slab: &[u32], ) -> (Vertex, Transform, Id) { - let transform = slab.read(self.transform); - match self.vertex_data.hash { - VertexData::NATIVE => { - let id = Id::::from(self.vertex_data.index); - let NativeVertexData { - vertices, - indices, - material, - } = slab.read(id); - let index = if indices.is_empty() { - vertex_index - } else { - slab.read(indices.at(vertex_index as usize)) - }; - let vertex = slab.read(vertices.at(index as usize)); - (vertex, transform, material) - } - VertexData::GLTF => { - let id = Id::::from(self.vertex_data.index); - // TODO: Take the GLTF parent node's transform into account - 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) - } - _ => Default::default(), + let t = slab.read(self.transform); + let mut model = Mat4::from_scale_rotation_translation(t.scale, t.rotation, t.translation); + let mut node = GltfNode::default(); + for id_id in self.node_path.iter() { + let node_id = slab.read(id_id); + node = slab.read(node_id); + let node_transform = + Mat4::from_scale_rotation_translation(node.scale, node.rotation, node.translation); + model = model * node_transform; } + // TODO: check nodes for skinning + let mesh = slab.read(node.mesh); + let primitive_id = mesh.primitives.at(self.primitive_index as usize); + let primitive = slab.read(primitive_id); + let material = primitive.material; + let vertex = primitive.get_vertex(vertex_index as usize, slab); + let (s, r, t) = model.to_scale_rotation_translation_or_id(); + let transform = Transform { + translation: t, + rotation: r, + scale: s, + }; + (vertex, transform, material) } } #[spirv(vertex)] -pub fn new_stage_vertex( +pub fn gltf_vertex( // Which render unit are we rendering #[spirv(instance_index)] instance_index: u32, // Which vertex within the render unit are we rendering @@ -1234,7 +1173,7 @@ pub fn get_material(material_index: u32, has_lighting: bool, slab: &[u32]) -> pb #[allow(clippy::too_many_arguments)] #[spirv(fragment)] /// Scene fragment shader. -pub fn stage_fragment( +pub fn gltf_fragment( #[spirv(descriptor_set = 1, binding = 0)] atlas: &Image2d, #[spirv(descriptor_set = 1, binding = 1)] atlas_sampler: &Sampler, @@ -1262,7 +1201,7 @@ pub fn stage_fragment( output: &mut Vec4, brigtness: &mut Vec4, ) { - stage_fragment_impl( + gltf_fragment_impl( atlas, atlas_sampler, irradiance, @@ -1288,7 +1227,7 @@ pub fn stage_fragment( #[allow(clippy::too_many_arguments)] /// Scene fragment shader. -pub fn stage_fragment_impl( +pub fn gltf_fragment_impl( atlas: &T, atlas_sampler: &S, irradiance: &C, diff --git a/crates/renderling/src/lib.rs b/crates/renderling/src/lib.rs index 576bb541..ec0996b9 100644 --- a/crates/renderling/src/lib.rs +++ b/crates/renderling/src/lib.rs @@ -62,7 +62,7 @@ mod state; #[cfg(feature = "text")] mod text; mod texture; -mod tutorial; +//mod tutorial; mod ui; mod uniform; @@ -254,14 +254,13 @@ fn init_logging() { .try_init(); } -#[cfg(test)] +#[cfg(all(feature = "blah", test))] mod test { use super::*; use glam::{Mat3, Mat4, Quat, UVec2, Vec2, Vec3, Vec4, Vec4Swizzles}; use pretty_assertions::assert_eq; use renderling_shader::stage::{ - light::*, new_stage_vertex, Camera, GpuEntity, NativeVertexData, RenderUnit, Transform, - Vertex, VertexData, + gltf_vertex, light::*, Camera, GpuEntity, RenderUnit, Transform, Vertex, }; #[test] @@ -610,59 +609,6 @@ mod test { ] } - /// A helper struct that contains all outputs of the vertex shader. - #[allow(unused)] - #[derive(Clone, Debug, Default, PartialEq)] - pub struct VertexInvocation { - pub instance_index: u32, - pub vertex_index: u32, - pub render_unit_id: Id, - pub render_unit: RenderUnit, - pub out_camera: u32, - pub out_material: u32, - pub out_color: Vec4, - pub out_uv0: Vec2, - pub out_uv1: Vec2, - pub out_norm: Vec3, - pub out_tangent: Vec3, - pub out_bitangent: Vec3, - pub out_pos: Vec3, - // output clip coordinates - pub clip_pos: Vec4, - // output normalized device coordinates - pub ndc_pos: Vec3, - } - - impl VertexInvocation { - #[allow(dead_code)] - pub fn invoke(instance_index: u32, vertex_index: u32, slab: &[u32]) -> Self { - let mut v = Self { - instance_index, - vertex_index, - ..Default::default() - }; - v.render_unit_id = Id::from(v.instance_index); - v.render_unit = slab.read(v.render_unit_id); - new_stage_vertex( - v.instance_index, - v.vertex_index, - slab, - &mut v.out_camera, - &mut v.out_material, - &mut v.out_color, - &mut v.out_uv0, - &mut v.out_uv1, - &mut v.out_norm, - &mut v.out_tangent, - &mut v.out_bitangent, - &mut v.out_pos, - &mut v.clip_pos, - ); - v.ndc_pos = v.clip_pos.xyz() / v.clip_pos.w; - v - } - } - #[test] // Tests that updating the material actually updates the rendering of an unlit mesh fn unlit_textured_cube_material() { diff --git a/crates/renderling/src/linkage/stage-gltf_fragment.spv b/crates/renderling/src/linkage/stage-gltf_fragment.spv new file mode 100644 index 00000000..c8200e30 Binary files /dev/null and b/crates/renderling/src/linkage/stage-gltf_fragment.spv differ diff --git a/crates/renderling/src/linkage/stage-gltf_vertex.spv b/crates/renderling/src/linkage/stage-gltf_vertex.spv new file mode 100644 index 00000000..26b35017 Binary files /dev/null and b/crates/renderling/src/linkage/stage-gltf_vertex.spv differ diff --git a/crates/renderling/src/linkage/stage-new_stage_vertex.spv b/crates/renderling/src/linkage/stage-new_stage_vertex.spv deleted file mode 100644 index 61592a8d..00000000 Binary files a/crates/renderling/src/linkage/stage-new_stage_vertex.spv and /dev/null differ diff --git a/crates/renderling/src/linkage/stage-stage_fragment.spv b/crates/renderling/src/linkage/stage-stage_fragment.spv deleted file mode 100644 index 5f18e07e..00000000 Binary files a/crates/renderling/src/linkage/stage-stage_fragment.spv and /dev/null differ diff --git a/crates/renderling/src/linkage/tutorial-slabbed_render_unit.spv b/crates/renderling/src/linkage/tutorial-slabbed_render_unit.spv index eb8430f4..b0ac224f 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 960ffb21..0cf72c79 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/linkage/tutorial-slabbed_vertices_no_instance.spv b/crates/renderling/src/linkage/tutorial-slabbed_vertices_no_instance.spv index a5d9b952..b1991b0f 100644 Binary files a/crates/renderling/src/linkage/tutorial-slabbed_vertices_no_instance.spv and b/crates/renderling/src/linkage/tutorial-slabbed_vertices_no_instance.spv differ diff --git a/crates/renderling/src/slab.rs b/crates/renderling/src/slab.rs index fa7f113a..4fb279fb 100644 --- a/crates/renderling/src/slab.rs +++ b/crates/renderling/src/slab.rs @@ -389,8 +389,6 @@ impl SlabBuffer { #[cfg(test)] mod test { - use renderling_shader::stage::{NativeVertexData, RenderUnit, Vertex, VertexData}; - use crate::Renderling; use super::*; @@ -443,37 +441,4 @@ 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), - ..Default::default() - }, - ); - let unit = RenderUnit { - vertex_data: VertexData::new_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 c699245a..0f5b0c81 100644 --- a/crates/renderling/src/stage.rs +++ b/crates/renderling/src/stage.rs @@ -388,10 +388,10 @@ impl Stage { fn create_stage_render_pipeline(device: &wgpu::Device) -> wgpu::RenderPipeline { log::trace!("creating stage render pipeline"); let label = Some("stage render pipeline"); - let vertex_shader = device - .create_shader_module(wgpu::include_spirv!("linkage/stage-new_stage_vertex.spv")); + let vertex_shader = + device.create_shader_module(wgpu::include_spirv!("linkage/stage-gltf_vertex.spv")); let fragment_shader = device - .create_shader_module(wgpu::include_spirv!("linkage/stage-stage_fragment.spv")); + .create_shader_module(wgpu::include_spirv!("linkage/stage-gltf_fragment.spv")); let stage_slab_buffers_layout = Stage::buffers_bindgroup_layout(device); let textures_layout = Stage::textures_bindgroup_layout(device); let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { @@ -404,7 +404,7 @@ impl Stage { layout: Some(&layout), vertex: wgpu::VertexState { module: &vertex_shader, - entry_point: "stage::new_stage_vertex", + entry_point: "stage::gltf_vertex", buffers: &[], }, primitive: wgpu::PrimitiveState { @@ -430,7 +430,7 @@ impl Stage { }, fragment: Some(wgpu::FragmentState { module: &fragment_shader, - entry_point: "stage::stage_fragment", + entry_point: "stage::gltf_fragment", targets: &[ Some(wgpu::ColorTargetState { format: wgpu::TextureFormat::Rgba16Float, @@ -791,7 +791,7 @@ pub fn stage_render( Ok((bloom_result,)) } -#[cfg(test)] +#[cfg(all(test, feature = "blah"))] mod test { use glam::Vec3; diff --git a/crates/renderling/src/stage/gltf_support.rs b/crates/renderling/src/stage/gltf_support.rs index e927a93b..4aab0118 100644 --- a/crates/renderling/src/stage/gltf_support.rs +++ b/crates/renderling/src/stage/gltf_support.rs @@ -4,13 +4,13 @@ use crate::{ shader::{ gltf::*, pbr::PbrMaterial, - stage::{Camera, GltfVertexData, LightingModel, VertexData}, + stage::{Camera, LightingModel}, texture::{GpuTexture, TextureAddressMode, TextureModes}, }, SceneImage, }; use glam::{Quat, Vec2, Vec3, Vec4}; -use renderling_shader::stage::{Transform, Vertex}; +use renderling_shader::stage::Vertex; use snafu::{OptionExt, ResultExt, Snafu}; #[derive(Debug, Snafu)] @@ -1042,27 +1042,19 @@ impl Stage { //return vec![self.draw_unit(&render_unit)]; } let mut units = if let Some(mesh) = node.mesh() { + log::trace!("drawing mesh {}", mesh.index()); let primitives = mesh.primitives(); let mesh = gpu_doc.meshes.at(mesh.index()); + let mut node_path = parents.clone(); + node_path.push(gpu_doc.nodes.at(node.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 = Transform { - translation: Vec3::from(t), - rotation: Quat::from_array(r), - scale: Vec3::from(s), - }; - let transform = self.append(&transform); + let node_path = self.append_array(&node_path); let render_unit = RenderUnit { - vertex_data: VertexData::new_gltf(vertex_data_id), + node_path, + mesh_index: mesh.index() as u32, + primitive_index: primitive.index() as u32, vertex_count: super::get_vertex_count(&primitive), - transform, camera: camera_id, ..Default::default() }; @@ -1104,11 +1096,157 @@ impl Stage { .flat_map(|node| self.draw_gltf_node(gpu_doc, camera_id, node)) .collect() } + + /// Create a mesh primitive on the slab from a list of vertices. + pub fn new_primitive( + &self, + vertices: &[Vertex], + indices: &[u32], + material: Id, + ) -> GltfPrimitive { + let vertex_count = vertices.len().max(indices.len()) as u32; + let indices = if indices.is_empty() { + Id::NONE + } else { + let buffer = GltfBuffer(self.append_array(indices).into_u32_array()); + let buffer = self.append(&buffer); + let view = self.append(&GltfBufferView { + buffer, + offset: 0, + length: indices.len() as u32 * 4, // 4 bytes per u32 + stride: 4, // 4 bytes in a u32, + }); + let accessor = self.append(&GltfAccessor { + size: 4, + view, + offset: 0, + count: indices.len() as u32, + data_type: DataType::U32, + dimensions: Dimensions::Scalar, + normalized: false, + }); + accessor + }; + + let vertex_buffer = GltfBuffer(self.append_array(vertices).into_u32_array()); + let buffer = self.append(&vertex_buffer); + let u32_stride = 4 // 4 position components, + + 4 // 4 color components, + + 4 // 4 uv components, + + 4 // 4 normal components, + + 4 // 4 tangent components, + + 4 // 4 joint components, + + 4; // 4 weight components + let stride = u32_stride * 4; // 4 bytes in a u32 + + let view = self.append(&GltfBufferView { + buffer, + offset: 0, + length: vertices.len() as u32 * u32_stride * 4, // stride as u32s * 4 bytes each + stride, + }); + + let positions = self.append(&GltfAccessor { + size: 3 * 4, // 3 position components * 4 bytes each + view, + offset: 0, + count: vertex_count as u32, + data_type: DataType::F32, + dimensions: Dimensions::Vec3, + normalized: false, + }); + + let colors = self.append(&GltfAccessor { + size: 4 * 4, // 4 color components * 4 bytes each + view, + offset: 4 * 4, // 3 + 1 position components * 4 bytes each + count: vertex_count as u32, + data_type: DataType::F32, + dimensions: Dimensions::Vec4, + normalized: false, + }); + + let tex_coords0 = self.append(&GltfAccessor { + size: 2 * 4, // 2 uv components * 4 bytes each + view, + offset: 8 * 4, // (3 + 1) position + 4 color components * 4 bytes each + count: vertex_count as u32, + data_type: DataType::F32, + dimensions: Dimensions::Vec2, + normalized: false, + }); + + let tex_coords1 = self.append(&GltfAccessor { + size: 2 * 4, // 2 uv components * 4 bytes each + view, + offset: 10 * 4, // (3 + 1) position + 4 color + 2 uv components * 4 bytes each + count: vertex_count as u32, + data_type: DataType::F32, + dimensions: Dimensions::Vec2, + normalized: false, + }); + + let normals = self.append(&GltfAccessor { + size: 3 * 4, // 3 normal components * 4 bytes each + view, + offset: 12 * 4, // (3 + 1) position + 4 color + 4 uv components * 4 bytes each + count: vertex_count as u32, + data_type: DataType::F32, + dimensions: Dimensions::Vec3, + normalized: false, + }); + + let tangents = self.append(&GltfAccessor { + size: 4 * 4, // 4 tangent components * 4 bytes each + view, + offset: 16 * 4, // (3 + 1) position + 4 color + 4 uv + (3 + 1) normal components * 4 bytes each + count: vertex_count as u32, + data_type: DataType::F32, + dimensions: Dimensions::Vec4, + normalized: false, + }); + + let joints = self.append(&GltfAccessor { + size: 4 * 4, // 4 joint components * 4 bytes each + view, + offset: 20 * 4, // (3 + 1) position + 4 color + 4 uv + (3 + 1) normal + 4 tangent components * 4 bytes each + count: vertex_count as u32, + data_type: DataType::F32, + dimensions: Dimensions::Vec4, + normalized: false, + }); + + let weights = self.append(&GltfAccessor { + size: 4 * 4, // 4 weight components * 4 bytes each + view, + offset: 24 * 4, // (3 + 1) position + 4 color + 4 uv + (3 + 1) normal + 4 tangent + 4 joint components * 4 bytes each + count: vertex_count as u32, + data_type: DataType::F32, + dimensions: Dimensions::Vec4, + normalized: false, + }); + + GltfPrimitive { + vertex_count, + material, + indices, + positions, + normals, + normals_were_generated: false, + tangents, + tangents_were_generated: false, + colors, + tex_coords0, + tex_coords1, + joints, + weights, + } + } } #[cfg(test)] mod test { - use glam::{Vec3, Vec4}; + use glam::{Vec2, Vec3, Vec4, Vec4Swizzles}; use crate::{ shader::{ @@ -1116,10 +1254,7 @@ mod test { gltf::*, pbr::PbrMaterial, slab::Slab, - stage::{ - Camera, GltfVertexData, LightingModel, NativeVertexData, RenderUnit, Transform, - Vertex, VertexData, - }, + stage::{Camera, LightingModel, RenderUnit, Transform, Vertex}, }, Id, Renderling, Stage, }; @@ -1222,93 +1357,6 @@ mod test { 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(); - - let draws = stage.get_draws(); - let slab = &data; - - let indices = draws - .iter() - .map(|draw| { - let unit_id = draw.id; - let unit = slab.read(unit_id); - assert_eq!(unit.vertex_data.is_gltf(), true); - let vertex_data_id = Id::::from(unit.vertex_data.index); - 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 = crate::test::VertexInvocation { - 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); } @@ -1356,11 +1404,7 @@ mod test { .load_gltf_document(&document, buffers, images) .unwrap(); let (projection, view) = crate::camera::default_ortho2d(100.0, 100.0); - let camera_id = stage.append(&Camera { - projection, - view, - position: Vec3::ZERO, - }); + let camera_id = stage.append(&Camera::new(projection, view)); assert!(!gpu_doc.textures.is_empty()); let albedo_texture_id = gpu_doc.textures.at(0); assert!(albedo_texture_id.is_some()); @@ -1370,7 +1414,7 @@ mod test { ..Default::default() }); println!("material_id: {:#?}", material_id); - let vertices = stage.append_array(&vec![ + let vertices = [ Vertex::default() .with_position([0.0, 0.0, 0.0]) .with_uv0([0.0, 0.0]), @@ -1383,23 +1427,44 @@ mod test { Vertex::default() .with_position([0.0, 1.0, 0.0]) .with_uv0([0.0, 1.0]), - ]); - let indices = stage.append_array(&[0, 3, 2, 0, 2, 1]); - let native_data_id = stage.append(&NativeVertexData { - vertices, - indices, - material: material_id, + ]; + let indices = [0, 3, 2, 0, 2, 1]; + let primitive = stage.new_primitive(&vertices, &indices, material_id); + let mesh = stage.append(&GltfMesh { + primitives: stage.append_array(&[primitive]), + ..Default::default() }); + let node = stage.append(&GltfNode { + mesh, + ..Default::default() + }); + let node_path = stage.append_array(&[node]); + let transform = stage.append(&Transform { scale: Vec3::new(100.0, 100.0, 1.0), ..Default::default() }); - let _unit_id = stage.draw_unit(&RenderUnit { - vertex_data: VertexData::new_native(native_data_id), + + let unit_id = stage.draw_unit(&RenderUnit { camera: camera_id, transform, - vertex_count: indices.len() as u32, + vertex_count: primitive.vertex_count, + node_path, + mesh_index: 0, + primitive_index: 0, }); + + let data = stage.read_slab().unwrap(); + for i in 0..indices.len() { + println!("\nget vertex: {i}"); + let v = primitive.get_vertex(i, &data); + assert_eq!(vertices[indices[i] as usize], v); + } + + //let uvs = (0..primitive.vertex_count).map(|) + let inv = VertexInvocation::invoke(unit_id.inner(), 0, &data); + println!("inv: {inv:#?}"); + let img = r.render_image().unwrap(); img_diff::assert_img_eq("gltf_images.png", img); } @@ -1450,7 +1515,7 @@ mod test { #[test] // Demonstrates how to generate a mesh primitive on the CPU. - fn generate_gltf_cmy_tri() { + fn gltf_cmy_tri() { let size = 100; let mut r = Renderling::headless(size, size).with_background_color(Vec3::splat(0.0).extend(1.0)); @@ -1539,22 +1604,76 @@ mod test { }); // render unit - let gltf_vertex_data = stage.append(&GltfVertexData { - mesh: meshes.at(0), - primitive_index: 0, - ..Default::default() - }); - let vertex_data = VertexData::new_gltf(gltf_vertex_data); let (projection, view) = crate::camera::default_ortho2d(100.0, 100.0); let camera = stage.append(&Camera::new(projection, view)); - let _unit_id = stage.draw_unit(&RenderUnit { - vertex_data, + let node_path = stage.append_array(&[nodes.at(0)]); + let unit_id = stage.draw_unit(&RenderUnit { camera, + node_path, + mesh_index: 0, + primitive_index: 0, vertex_count: 3, ..Default::default() }); + let data = stage.read_slab().unwrap(); + let invocation = VertexInvocation::invoke(unit_id.inner(), 0, &data); + println!("invoctaion: {invocation:#?}"); + let img = r.render_image().unwrap(); img_diff::assert_img_eq("gltf/cmy_tri.png", img); } + + /// A helper struct that contains all outputs of the vertex shader. + #[allow(unused)] + #[derive(Clone, Debug, Default, PartialEq)] + pub struct VertexInvocation { + pub instance_index: u32, + pub vertex_index: u32, + pub render_unit_id: Id, + pub render_unit: RenderUnit, + pub out_camera: u32, + pub out_material: u32, + pub out_color: Vec4, + pub out_uv0: Vec2, + pub out_uv1: Vec2, + pub out_norm: Vec3, + pub out_tangent: Vec3, + pub out_bitangent: Vec3, + pub out_pos: Vec3, + // output clip coordinates + pub clip_pos: Vec4, + // output normalized device coordinates + pub ndc_pos: Vec3, + } + + impl VertexInvocation { + #[allow(dead_code)] + pub fn invoke(instance_index: u32, vertex_index: u32, slab: &[u32]) -> Self { + let mut v = Self { + instance_index, + vertex_index, + ..Default::default() + }; + v.render_unit_id = Id::from(v.instance_index); + v.render_unit = slab.read(v.render_unit_id); + renderling_shader::stage::gltf_vertex( + v.instance_index, + v.vertex_index, + slab, + &mut v.out_camera, + &mut v.out_material, + &mut v.out_color, + &mut v.out_uv0, + &mut v.out_uv1, + &mut v.out_norm, + &mut v.out_tangent, + &mut v.out_bitangent, + &mut v.out_pos, + &mut v.clip_pos, + ); + v.ndc_pos = v.clip_pos.xyz() / v.clip_pos.w; + v + } + } }