use std::{ mem::{size_of, transmute}, sync::Arc, }; use glam::{ f32::{Mat4, Vec3, Vec4}, Vec4Swizzles, }; use metal::*; pub const GEOMETRY_MASK_TRIANGLE: u32 = 1; pub const GEOMETRY_MASK_SPHERE: u32 = 2; pub const GEOMETRY_MASK_LIGHT: u32 = 4; pub const FACE_MASK_NONE: u16 = 0; pub const FACE_MASK_NEGATIVE_X: u16 = 1 << 0; pub const FACE_MASK_POSITIVE_X: u16 = 1 << 1; pub const FACE_MASK_NEGATIVE_Y: u16 = 1 << 2; pub const FACE_MASK_POSITIVE_Y: u16 = 1 << 3; pub const FACE_MASK_NEGATIVE_Z: u16 = 1 << 4; pub const FACE_MASK_POSITIVE_Z: u16 = 1 << 5; pub const FACE_MASK_ALL: u16 = (1 << 6) - 1; pub trait Geometry { fn upload_to_buffers(&mut self) { todo!() } fn clear(&mut self) { todo!() } fn get_geometry_descriptor(&self) -> AccelerationStructureGeometryDescriptor { todo!() } fn get_resources(&self) -> Vec { todo!() } fn get_intersection_function_name(&self) -> Option<&str> { None } } pub fn compute_triangle_normal(v0: &Vec3, v1: &Vec3, v2: &Vec3) -> Vec3 { let e1 = Vec3::normalize(*v1 - *v0); let e2 = Vec3::normalize(*v2 - *v0); return Vec3::cross(e1, e2); } #[derive(Default)] #[repr(C)] pub struct Triangle { pub normals: [Vec4; 3], pub colours: [Vec4; 3], } pub fn get_managed_buffer_storage_mode() -> MTLResourceOptions { return MTLResourceOptions::StorageModeManaged; } pub struct TriangleGeometry { pub device: Device, pub name: String, pub index_buffer: Option, pub vertex_position_buffer: Option, pub vertex_normal_buffer: Option, pub vertex_colour_buffer: Option, pub per_primitive_data_buffer: Option, pub indices: Vec, pub vertices: Vec, pub normals: Vec, pub colours: Vec, pub triangles: Vec, } impl TriangleGeometry { pub fn new(device: Device, name: String) -> Self { Self { device, name, index_buffer: None, vertex_position_buffer: None, vertex_normal_buffer: None, vertex_colour_buffer: None, per_primitive_data_buffer: None, indices: Vec::new(), vertices: Vec::new(), normals: Vec::new(), colours: Vec::new(), triangles: Vec::new(), } } pub fn add_cube_face_with_cube_vertices( &mut self, cube_vertices: &[Vec3], colour: Vec3, i0: u16, i1: u16, i2: u16, i3: u16, inward_normals: bool, ) { let v0 = cube_vertices[i0 as usize]; let v1 = cube_vertices[i1 as usize]; let v2 = cube_vertices[i2 as usize]; let v3 = cube_vertices[i3 as usize]; let n0 = compute_triangle_normal(&v0, &v1, &v2) * if inward_normals { -1f32 } else { 1f32 }; let n1 = compute_triangle_normal(&v0, &v2, &v3) * if inward_normals { -1f32 } else { 1f32 }; let first_index = self.indices.len(); let base_index = self.vertices.len() as u16; self.indices.push(base_index + 0); self.indices.push(base_index + 1); self.indices.push(base_index + 2); self.indices.push(base_index + 0); self.indices.push(base_index + 2); self.indices.push(base_index + 3); self.vertices.push(From::from((v0, 0.0))); self.vertices.push(From::from((v1, 0.0))); self.vertices.push(From::from((v2, 0.0))); self.vertices.push(From::from((v3, 0.0))); self.normals .push(From::from((Vec3::normalize(n0 + n1), 0.0))); self.normals.push(From::from((n0, 0.0))); self.normals .push(From::from((Vec3::normalize(n0 + n1), 0.0))); self.normals.push(From::from((n1, 0.0))); for _ in 0..4 { self.colours.push(From::from((colour, 0.0))); } for triangle_index in 0..2 { let mut triangle = Triangle::default(); for i in 0..3 { let index = self.indices[first_index + triangle_index * 3 + i]; triangle.normals[i] = self.normals[index as usize]; triangle.colours[i] = self.colours[index as usize]; } self.triangles.push(triangle); } } pub fn add_cube_with_faces( &mut self, face_mask: u16, colour: Vec3, transform: Mat4, inward_normals: bool, ) { let mut cube_vertices = [ Vec3::new(-0.5, -0.5, -0.5), Vec3::new(0.5, -0.5, -0.5), Vec3::new(-0.5, 0.5, -0.5), Vec3::new(0.5, 0.5, -0.5), Vec3::new(-0.5, -0.5, 0.5), Vec3::new(0.5, -0.5, 0.5), Vec3::new(-0.5, 0.5, 0.5), Vec3::new(0.5, 0.5, 0.5), ]; for i in 0..8 { let transformed_vertex = Vec4::from((cube_vertices[i], 1.0)); let transformed_vertex = transform * transformed_vertex; cube_vertices[i] = transformed_vertex.xyz(); } const CUBE_INDICES: [[u16; 4]; 6] = [ [0, 4, 6, 2], [1, 3, 7, 5], [0, 1, 5, 4], [2, 6, 7, 3], [0, 2, 3, 1], [4, 5, 7, 6], ]; for face in 0..6 { if face_mask & (1 << face) != 0 { self.add_cube_face_with_cube_vertices( &cube_vertices, colour, CUBE_INDICES[face][0], CUBE_INDICES[face][1], CUBE_INDICES[face][2], CUBE_INDICES[face][3], inward_normals, ); } } } } impl Geometry for TriangleGeometry { fn upload_to_buffers(&mut self) { self.index_buffer = Some(unsafe { self.device.new_buffer_with_data( transmute(self.indices.as_ptr()), (self.indices.len() * size_of::()) as NSUInteger, get_managed_buffer_storage_mode(), ) }); self.vertex_position_buffer = Some(unsafe { self.device.new_buffer_with_data( transmute(self.vertices.as_ptr()), (self.vertices.len() * size_of::()) as NSUInteger, get_managed_buffer_storage_mode(), ) }); self.vertex_normal_buffer = Some(unsafe { self.device.new_buffer_with_data( transmute(self.normals.as_ptr()), (self.normals.len() * size_of::()) as NSUInteger, get_managed_buffer_storage_mode(), ) }); self.vertex_colour_buffer = Some(unsafe { self.device.new_buffer_with_data( transmute(self.colours.as_ptr()), (self.colours.len() * size_of::()) as NSUInteger, get_managed_buffer_storage_mode(), ) }); self.per_primitive_data_buffer = Some(unsafe { self.device.new_buffer_with_data( transmute(self.triangles.as_ptr()), (self.triangles.len() * size_of::()) as NSUInteger, get_managed_buffer_storage_mode(), ) }); self.index_buffer .as_ref() .unwrap() .did_modify_range(NSRange::new( 0, self.index_buffer.as_ref().unwrap().length(), )); self.vertex_position_buffer .as_ref() .unwrap() .did_modify_range(NSRange::new( 0, self.vertex_position_buffer.as_ref().unwrap().length(), )); self.vertex_normal_buffer .as_ref() .unwrap() .did_modify_range(NSRange::new( 0, self.vertex_normal_buffer.as_ref().unwrap().length(), )); self.vertex_colour_buffer .as_ref() .unwrap() .did_modify_range(NSRange::new( 0, self.vertex_colour_buffer.as_ref().unwrap().length(), )); self.per_primitive_data_buffer .as_ref() .unwrap() .did_modify_range(NSRange::new( 0, self.per_primitive_data_buffer.as_ref().unwrap().length(), )); self.index_buffer .as_ref() .unwrap() .set_label(&format!("index buffer of {}", self.name)); self.vertex_position_buffer .as_ref() .unwrap() .set_label(&format!("vertex position buffer of {}", self.name)); self.vertex_normal_buffer .as_ref() .unwrap() .set_label(&format!("vertex normal buffer of {}", self.name)); self.vertex_colour_buffer .as_ref() .unwrap() .set_label(&format!("vertex colour buffer of {}", self.name)); self.per_primitive_data_buffer .as_ref() .unwrap() .set_label(&format!("per primitive data buffer of {}", self.name)); } fn clear(&mut self) { self.indices.clear(); self.vertices.clear(); self.normals.clear(); self.colours.clear(); self.triangles.clear(); } fn get_geometry_descriptor(&self) -> AccelerationStructureGeometryDescriptor { let descriptor = AccelerationStructureTriangleGeometryDescriptor::descriptor(); descriptor.set_index_buffer(Some(self.index_buffer.as_ref().unwrap())); descriptor.set_index_type(MTLIndexType::UInt16); descriptor.set_vertex_buffer(Some(self.vertex_position_buffer.as_ref().unwrap())); descriptor.set_vertex_stride(size_of::() as NSUInteger); descriptor.set_triangle_count((self.indices.len() / 3) as NSUInteger); descriptor .set_primitive_data_buffer(Some(self.per_primitive_data_buffer.as_ref().unwrap())); descriptor.set_primitive_data_stride(size_of::() as NSUInteger); descriptor.set_primitive_data_element_size(size_of::() as NSUInteger); From::from(descriptor) } fn get_resources(&self) -> Vec { vec![ From::from(self.index_buffer.as_ref().unwrap().clone()), From::from(self.vertex_normal_buffer.as_ref().unwrap().clone()), From::from(self.vertex_colour_buffer.as_ref().unwrap().clone()), ] } } #[repr(C)] pub struct BoundingBox { pub min: Vec3, pub max: Vec3, } #[repr(C)] pub struct Sphere { pub origin_radius_squared: Vec4, pub colour_radius: Vec4, } pub struct SphereGeometry { pub device: Device, pub sphere_buffer: Option, pub bounding_box_buffer: Option, pub per_primitive_data_buffer: Option, pub spheres: Vec, } impl SphereGeometry { pub fn new(device: Device) -> Self { Self { device, sphere_buffer: None, bounding_box_buffer: None, per_primitive_data_buffer: None, spheres: Vec::new(), } } pub fn add_sphere_with_origin(&mut self, origin: Vec3, radius: f32, colour: Vec3) { self.spheres.push(Sphere { origin_radius_squared: Vec4::from((origin, radius * radius)), colour_radius: Vec4::from((colour, radius)), }); } } impl Geometry for SphereGeometry { fn upload_to_buffers(&mut self) { self.sphere_buffer = Some(unsafe { self.device.new_buffer_with_data( transmute(self.spheres.as_ptr()), (self.spheres.len() * size_of::()) as NSUInteger, get_managed_buffer_storage_mode(), ) }); self.sphere_buffer .as_ref() .unwrap() .set_label("sphere buffer"); let mut bounding_boxes = Vec::new(); for sphere in &self.spheres { bounding_boxes.push(BoundingBox { min: sphere.origin_radius_squared.xyz() - sphere.colour_radius.w, max: sphere.origin_radius_squared.xyz() + sphere.colour_radius.w, }); } self.bounding_box_buffer = Some(unsafe { self.device.new_buffer_with_data( transmute(bounding_boxes.as_ptr()), (bounding_boxes.len() * size_of::()) as NSUInteger, get_managed_buffer_storage_mode(), ) }); self.bounding_box_buffer .as_ref() .unwrap() .set_label("bounding box buffer"); self.sphere_buffer .as_ref() .unwrap() .did_modify_range(NSRange::new( 0, self.sphere_buffer.as_ref().unwrap().length(), )); self.bounding_box_buffer .as_ref() .unwrap() .did_modify_range(NSRange::new( 0, self.bounding_box_buffer.as_ref().unwrap().length(), )); } fn clear(&mut self) { self.spheres.clear(); } fn get_geometry_descriptor(&self) -> AccelerationStructureGeometryDescriptor { let descriptor = AccelerationStructureBoundingBoxGeometryDescriptor::descriptor(); descriptor.set_bounding_box_buffer(Some(self.bounding_box_buffer.as_ref().unwrap())); descriptor.set_bounding_box_count(self.spheres.len() as NSUInteger); descriptor.set_primitive_data_buffer(Some(&self.sphere_buffer.as_ref().unwrap())); descriptor.set_primitive_data_stride(size_of::() as NSUInteger); descriptor.set_primitive_data_element_size(size_of::() as NSUInteger); From::from(descriptor) } fn get_resources(&self) -> Vec { return vec![From::from(self.sphere_buffer.as_ref().unwrap().clone())]; } fn get_intersection_function_name(&self) -> Option<&str> { Some("sphereIntersectionFunction") } } pub struct GeometryInstance { pub geometry: Arc, pub transform: Mat4, pub mask: u32, pub index_in_scene: NSUInteger, } #[repr(C)] pub struct AreaLight { pub position: Vec4, pub forward: Vec4, pub right: Vec4, pub up: Vec4, pub colour: Vec4, }