Source code
Revision control
Copy as Markdown
Other Tools
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<Resource> {
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<Buffer>,
pub vertex_position_buffer: Option<Buffer>,
pub vertex_normal_buffer: Option<Buffer>,
pub vertex_colour_buffer: Option<Buffer>,
pub per_primitive_data_buffer: Option<Buffer>,
pub indices: Vec<u16>,
pub vertices: Vec<Vec4>,
pub normals: Vec<Vec4>,
pub colours: Vec<Vec4>,
pub triangles: Vec<Triangle>,
}
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::<u16>()) 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::<Vec4>()) 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::<Vec4>()) 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::<Vec4>()) 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::<Triangle>()) 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::<Vec4>() 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::<Triangle>() as NSUInteger);
descriptor.set_primitive_data_element_size(size_of::<Triangle>() as NSUInteger);
From::from(descriptor)
}
fn get_resources(&self) -> Vec<Resource> {
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<Buffer>,
pub bounding_box_buffer: Option<Buffer>,
pub per_primitive_data_buffer: Option<Buffer>,
pub spheres: Vec<Sphere>,
}
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::<Sphere>()) 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::<BoundingBox>()) 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::<Sphere>() as NSUInteger);
descriptor.set_primitive_data_element_size(size_of::<Sphere>() as NSUInteger);
From::from(descriptor)
}
fn get_resources(&self) -> Vec<Resource> {
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<dyn Geometry>,
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,
}