diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a5b4e8910..4538fd826 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,8 +2,32 @@ name: CI on: [push, pull_request] env: CARGO_TERM_COLOR: always - EMBREE_VERSION: 3.12.1 + EMBREE_VERSION: 3.13.5 jobs: + check_format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: true + components: rustfmt + - name: Format Core + run: cargo fmt -- --check + - name: Format Examples + run: scripts/check-examples-formatting.sh + miri: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install Miri + run: | + rustup toolchain install nightly --component miri + rustup override set nightly + cargo miri setup + - name: Test with Miri + run: cargo miri test build_linux: runs-on: ubuntu-latest steps: @@ -19,10 +43,6 @@ jobs: - run: cargo test - run: cargo doc - run: scripts/build-examples-linux-mac.sh - - name: Format Core - run: cargo fmt -- --check - - name: Format Examples - run: scripts/check-examples-formatting.sh build_mac: runs-on: macos-latest steps: @@ -34,7 +54,7 @@ jobs: - run: unzip embree-${EMBREE_VERSION}.x86_64.macosx.zip - run: source embree-${EMBREE_VERSION}.x86_64.macosx/embree-vars.sh - run: echo "EMBREE_DIR=`pwd`/embree-${EMBREE_VERSION}.x86_64.macosx/" >> $GITHUB_ENV - - run: cp $EMBREE_DIR/lib/* . + - run: cp -r $EMBREE_DIR/lib/* . - run: cargo build - run: scripts/build-test-mac.sh build_windows: diff --git a/.gitignore b/.gitignore index 6a8b52461..7836bf5e5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ target Cargo.lock *.png *.dll +.idea diff --git a/Cargo.toml b/Cargo.toml index d3d4874c9..c99c27a9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,8 @@ exclude = [ "examples/*" ] +[workspace] +members = [ + "examples/*", +] +exclude = ["examples/todos"] diff --git a/build.rs b/build.rs index dd7bcb441..491dc95f3 100644 --- a/build.rs +++ b/build.rs @@ -1,5 +1,4 @@ -use std::env; -use std::path::PathBuf; +use std::{env, path::PathBuf}; fn main() { println!("{:?}", env::var("EMBREE_DIR")); diff --git a/examples/displacement_geometry/Cargo.toml b/examples/displacement_geometry/Cargo.toml new file mode 100644 index 000000000..1418d7faf --- /dev/null +++ b/examples/displacement_geometry/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "displacement_geometry" +authors = ["Yang Chen "] +version = "0.1.0" +edition = "2021" + +[dependencies] +glam = "0.23.0" +embree = { path = "../.." } +support = { path = "../support" } +rayon = { version = "1.6.1", optional = true } + +[features] +smooth_normals = [] +rayon = ["dep:rayon"] +default = ["rayon"] \ No newline at end of file diff --git a/examples/displacement_geometry/src/main.rs b/examples/displacement_geometry/src/main.rs new file mode 100644 index 000000000..2ca30e7f5 --- /dev/null +++ b/examples/displacement_geometry/src/main.rs @@ -0,0 +1,263 @@ +use embree::{ + AlignedArray, BufferSlice, BufferUsage, Device, Format, Geometry, GeometryKind, + IntersectContext, Ray, RayHit, Scene, SceneFlags, +}; +use glam::{vec3, Vec3}; +use support::{ + noise, rgba_to_u32, Camera, DebugState, ParallelIterator, RgbaImage, TiledImage, + DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH, TILE_SIZE_X, TILE_SIZE_Y, +}; + +const EDGE_LEVEL: f32 = 256.0; +const NUM_INDICES: usize = 24; +const NUM_FACES: usize = 6; +#[allow(dead_code)] +const FACE_SIZE: usize = 4; + +const CUBE_VERTICES: AlignedArray = AlignedArray([ + -1.0, -1.0, -1.0, 0.0, // 0 + 1.0, -1.0, -1.0, 0.0, // 1 + 1.0, -1.0, 1.0, 0.0, // 2 + -1.0, -1.0, 1.0, 0.0, // 3 + -1.0, 1.0, -1.0, 0.0, // 4 + 1.0, 1.0, -1.0, 0.0, // 5 + 1.0, 1.0, 1.0, 0.0, // 6 + -1.0, 1.0, 1.0, 0.0, // 7 +]); + +const CUBE_INDICES: [u32; NUM_INDICES] = [ + 0, 4, 5, 1, // 0 + 1, 5, 6, 2, // 1 + 2, 6, 7, 3, // 2 + 0, 3, 7, 4, // 3 + 4, 7, 6, 5, // 4 + 0, 1, 2, 3, // 5 +]; + +const CUBE_FACES: [u32; NUM_FACES] = [4; 6]; + +const LIGHT_DIR: [f32; 3] = [0.57; 3]; + +fn displacement(p: [f32; 3]) -> f32 { + let mut dn = 0.0; + let mut freq = 1.0; + while freq < 40.0 { + let n = noise([p[0] * freq, p[1] * freq, p[2] * freq]).abs(); + dn += 1.4 * n * n / freq; + freq *= 2.0; + } + dn +} + +#[cfg(feature = "smooth_normals")] +fn displacement_du_or_dv(p: Vec3, dp_du_or_dp_dv: Vec3) -> f32 { + let du_or_dv = 0.001; + (displacement((p + du_or_dv * dp_du_or_dp_dv).into()) - displacement(p.into())) / du_or_dv +} + +fn create_cube(device: &Device) -> Geometry<'static> { + let mut geom = device.create_geometry(GeometryKind::SUBDIVISION).unwrap(); + geom.set_buffer( + BufferUsage::VERTEX, + 0, + Format::FLOAT3, + BufferSlice::from_slice(&CUBE_VERTICES.0, ..), + 4 * std::mem::size_of::(), + 8, + ) + .unwrap(); + geom.set_buffer( + BufferUsage::INDEX, + 0, + Format::UINT, + BufferSlice::from_slice(&CUBE_INDICES, ..), + std::mem::size_of::(), + NUM_INDICES, + ) + .unwrap(); + geom.set_buffer( + BufferUsage::FACE, + 0, + Format::UINT, + BufferSlice::from_slice(&CUBE_FACES, ..), + std::mem::size_of::(), + NUM_FACES, + ) + .unwrap(); + + geom.set_new_buffer( + BufferUsage::LEVEL, + 0, + Format::FLOAT, + std::mem::size_of::(), + NUM_INDICES, + ) + .unwrap() + .view_mut::() + .unwrap() + .copy_from_slice(&[EDGE_LEVEL; NUM_INDICES]); + unsafe { + geom.set_displacement_function( + |_raw_geom, vertices, _prim_id, _time_step, _user_data: Option<&mut ()>| { + for (_, ng, p) in vertices.into_iter_mut() { + let disp = displacement([*p[0], *p[1], *p[2]]); + let dp = [disp * ng[0], disp * ng[1], disp * ng[2]]; + *p[0] += dp[0]; + *p[1] += dp[1]; + *p[2] += dp[2]; + } + }, + ); + } + geom.commit(); + geom +} + +fn create_ground_plane(device: &Device) -> Geometry<'static> { + let mut mesh = device.create_geometry(GeometryKind::QUAD).unwrap(); + { + mesh.set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, 4) + .unwrap() + .view_mut::<[f32; 4]>() + .unwrap() + .copy_from_slice(&[ + [-10.0, -2.0, -10.0, 0.0], + [-10.0, -2.0, 10.0, 0.0], + [10.0, -2.0, 10.0, 0.0], + [10.0, -2.0, -10.0, 0.0], + ]); + mesh.set_new_buffer(BufferUsage::INDEX, 0, Format::UINT4, 16, 1) + .unwrap() + .view_mut::<[u32; 4]>() + .unwrap() + .copy_from_slice(&[[0, 1, 2, 3]]); + } + mesh.commit(); + mesh +} + +fn main() { + let device = Device::new().unwrap(); + device.set_error_function(|err, msg| { + eprintln!("{}: {}", err, msg); + }); + let mut scene = device.create_scene().unwrap(); + scene.set_flags(SceneFlags::ROBUST); + + let cube = create_cube(&device); + let ground = create_ground_plane(&device); + + let ground_id = scene.attach_geometry(&ground); + let cube_id = scene.attach_geometry(&cube); + scene.commit(); + + let display = support::Display::new( + DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT, + "Dynamic Scene", + ); + + let state = DebugState { + scene: scene.clone(), + user: (), + }; + + support::display::run( + display, + state, + move |_, _| {}, + move |image, camera, time, _| { + render_frame(image, camera, &scene, cube_id, ground_id); + }, + |_| {}, + ); +} + +fn render_pixel( + x: u32, + y: u32, + camera: &Camera, + scene: &Scene, + cube_id: u32, + ground_id: u32, +) -> u32 { + let dir = camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)); + let mut ctx = IntersectContext::coherent(); + let mut ray_hit = RayHit::from_ray(Ray::segment( + camera.pos.into(), + dir.into(), + 0.0, + f32::INFINITY, + )); + scene.intersect(&mut ctx, &mut ray_hit); + + let mut color = vec3(0.0, 0.0, 0.0); + if ray_hit.hit.is_valid() { + let diffuse = if ray_hit.hit.geomID == cube_id { + vec3(0.9, 0.6, 0.5) + } else { + vec3(1.0, 0.0, 0.0) + }; + color += diffuse * 0.5; + + let normal = { + let mut n = Vec3::from(ray_hit.hit.unit_normal()); + #[cfg(feature = "smooth_normals")] + { + use embree::{InterpolateInput, InterpolateOutput}; + let hit_point: Vec3 = ray_hit.ray.hit_point().into(); + if ray_hit.hit.geomID != ground_id { + let mut output = InterpolateOutput::new(3, true, true, false); + let cube = scene.get_geometry_unchecked(cube_id).unwrap(); + cube.interpolate( + InterpolateInput { + prim_id: ray_hit.hit.primID, + u: ray_hit.hit.u, + v: ray_hit.hit.v, + usage: BufferUsage::VERTEX, + slot: 0, + }, + &mut output, + ); + let mut dp_du = Vec3::from_slice(output.dp_du().as_ref().unwrap()); + let mut dp_dv = Vec3::from_slice(output.dp_dv().as_ref().unwrap()); + let ng = dp_du.cross(dp_dv).normalize(); + dp_du += ng * displacement_du_or_dv(hit_point, dp_du); + dp_dv += ng * displacement_du_or_dv(hit_point, dp_dv); + n = dp_du.cross(dp_dv).normalize() + } + } + n + }; + + let mut shadow_ray = Ray::segment(ray_hit.ray.hit_point(), LIGHT_DIR, 0.001, f32::INFINITY); + + // Check if the shadow ray is occluded. + if !scene.occluded(&mut ctx, &mut shadow_ray) { + color += diffuse * glam::Vec3::from(LIGHT_DIR).dot(normal).clamp(0.0, 1.0); + } + } + + rgba_to_u32( + (color.x * 255.0) as u8, + (color.y * 255.0) as u8, + (color.z * 255.0) as u8, + 255, + ) +} + +fn render_frame( + frame: &mut TiledImage, + camera: &Camera, + scene: &Scene, + cube_id: u32, + ground_id: u32, +) { + frame.par_tiles_mut().for_each(|tile| { + tile.pixels.iter_mut().enumerate().for_each(|(i, pixel)| { + let x = tile.x + (i % tile.w as usize) as u32; + let y = tile.y + (i / tile.w as usize) as u32; + *pixel = render_pixel(x, y, camera, scene, cube_id, ground_id); + }); + }); +} diff --git a/examples/dynamic_scene/Cargo.toml b/examples/dynamic_scene/Cargo.toml new file mode 100644 index 000000000..6709a5b11 --- /dev/null +++ b/examples/dynamic_scene/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "dynamic_scene" +version = "0.1.0" +authors = ["Yang Chen "] +edition = "2021" + +[dependencies] +glam = "0.23.0" +embree = { path = "../.." } +support = { path = "../support" } + diff --git a/examples/dynamic_scene/src/main.rs b/examples/dynamic_scene/src/main.rs new file mode 100644 index 000000000..8e405ebf8 --- /dev/null +++ b/examples/dynamic_scene/src/main.rs @@ -0,0 +1,257 @@ +//! This example show how to create a dynamic scene. + +use embree::{ + BufferUsage, BuildQuality, Device, Format, Geometry, IntersectContext, Ray, RayHit, Scene, + SceneFlags, +}; +use glam::Vec3; +use support::{ + rgba_to_u32, Camera, DebugState, IndexedParallelIterator, ParallelIterator, ParallelSliceMut, + TiledImage, +}; + +const NUM_SPHERES: usize = 20; +const NUM_PHI: usize = 120; +const NUM_THETA: usize = 2 * NUM_PHI; + +fn create_sphere<'a>( + device: &Device, + quality: BuildQuality, + pos: Vec3, + radius: f32, +) -> Geometry<'a> { + // Create a triangulated sphere + let mut geometry = device + .create_geometry(embree::GeometryKind::TRIANGLE) + .unwrap(); + geometry.set_build_quality(quality); + + let mut vertices = geometry + .set_new_buffer( + BufferUsage::VERTEX, + 0, + Format::FLOAT3, + 16, + NUM_THETA * (NUM_PHI + 1), + ) + .unwrap() + .view_mut::<[f32; 4]>() + .unwrap(); + + let mut indices = geometry + .set_new_buffer( + BufferUsage::INDEX, + 0, + Format::UINT3, + 12, + 2 * NUM_THETA * (NUM_PHI - 1), + ) + .unwrap() + .view_mut::<[u32; 3]>() + .unwrap(); + + let mut tri = 0; + let rcp_num_theta = 1.0 / NUM_THETA as f32; + let rcp_num_phi = 1.0 / NUM_PHI as f32; + for phi_idx in 0..NUM_PHI { + for theta_idx in 0..NUM_THETA { + let phi = phi_idx as f32 * rcp_num_phi * std::f32::consts::PI; + let theta = theta_idx as f32 * rcp_num_theta * 2.0 * std::f32::consts::PI; + vertices[phi_idx * NUM_THETA + theta_idx] = [ + pos.x + radius * phi.sin() * theta.sin(), + pos.y + radius * phi.cos(), + pos.z + radius * phi.sin() * theta.cos(), + 0.0, + ]; + } + if phi_idx == 0 { + continue; + } + + for theta_idx in 1..=NUM_THETA { + let p00 = ((phi_idx - 1) * NUM_THETA + theta_idx - 1) as u32; + let p01 = ((phi_idx - 1) * NUM_THETA + theta_idx % NUM_THETA) as u32; + let p10 = (phi_idx * NUM_THETA + theta_idx - 1) as u32; + let p11 = (phi_idx * NUM_THETA + theta_idx % NUM_THETA) as u32; + + if phi_idx > 1 { + indices[tri] = [p10, p01, p00]; + tri += 1; + } + + if phi_idx < NUM_PHI { + indices[tri] = [p11, p01, p10]; + tri += 1; + } + } + } + geometry.commit(); + geometry +} + +fn create_ground_plane<'a>(device: &Device) -> Geometry<'a> { + let mut geometry = Geometry::new(device, embree::GeometryKind::TRIANGLE).unwrap(); + { + geometry + .set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, 4) + .unwrap() + .view_mut::<[f32; 4]>() + .unwrap() + .copy_from_slice(&[ + [-10.0, -2.0, -10.0, 0.0], + [-10.0, -2.0, 10.0, 0.0], + [10.0, -2.0, -10.0, 0.0], + [10.0, -2.0, 10.0, 0.0], + ]); + geometry + .set_new_buffer(BufferUsage::INDEX, 0, Format::UINT3, 12, 2) + .unwrap() + .view_mut::<[u32; 3]>() + .unwrap() + .copy_from_slice(&[[0, 1, 2], [1, 3, 2]]); + } + geometry.commit(); + geometry +} + +fn animate_sphere(scene: &Scene, id: u32, pos: Vec3, radius: f32, time: f32) { + let mut geometry = scene.get_geometry_unchecked(id).unwrap(); + let mut vertices = geometry + .get_buffer(BufferUsage::VERTEX, 0) + .unwrap() + .view_mut::<[f32; 4]>() + .unwrap(); + let num_theta_rcp = 1.0 / NUM_THETA as f32; + let num_phi_rcp = 1.0 / NUM_PHI as f32; + let f = 2.0 * (1.0 + 0.5 * time.sin()); + + vertices + .par_chunks_mut(NUM_THETA) + .enumerate() + .for_each(|(phi_idx, chunk)| { + let phi = phi_idx as f32 * num_phi_rcp * std::f32::consts::PI; + for (theta_idx, v) in chunk.iter_mut().enumerate() { + let theta = theta_idx as f32 * num_theta_rcp * 2.0 * std::f32::consts::PI; + v[0] = pos.x + radius * (f * phi).sin() * theta.sin(); + v[1] = pos.y + radius * phi.cos(); + v[2] = pos.z + radius * (f * phi).sin() * theta.cos(); + } + }); + geometry.update_buffer(BufferUsage::VERTEX, 0); + geometry.commit(); +} + +const LIGHT_DIR: [f32; 3] = [0.58, 0.58, 0.58]; + +fn main() { + let device = Device::new().unwrap(); + device.set_error_function(|err, msg| { + eprintln!("{}: {}", err, msg); + }); + let mut scene = device.create_scene().unwrap(); + scene.set_flags(SceneFlags::DYNAMIC | SceneFlags::ROBUST); + scene.set_build_quality(BuildQuality::LOW); + + let mut positions = [Vec3::ZERO; NUM_SPHERES]; + let mut radii = [1.0; NUM_SPHERES]; + let mut colors = [Vec3::ZERO; NUM_SPHERES + 1]; + + // Create a few triangulated spheres. + for i in 0..NUM_SPHERES { + let phi = i as f32 / NUM_SPHERES as f32 * std::f32::consts::PI * 2.0; + let radius = 2.0 * std::f32::consts::PI / NUM_SPHERES as f32; + let pos = 2.0 * Vec3::new(phi.sin(), 0.0, -phi.cos()); + let quality = if i % 2 == 0 { + BuildQuality::LOW + } else { + BuildQuality::REFIT + }; + let sphere = create_sphere(&device, quality, pos, radius); + let id = scene.attach_geometry(&sphere); + positions[id as usize] = pos; + radii[id as usize] = radius; + colors[id as usize] = Vec3::new( + (i % 16 + 1) as f32 / 17.0, + (i % 8 + 1) as f32 / 9.0, + (i % 4 + 1) as f32 / 5.0, + ); + } + let id = scene.attach_geometry(&create_ground_plane(&device)); + colors[id as usize] = Vec3::new(1.0, 1.0, 1.0); + scene.commit(); + + let display = support::Display::new(512, 512, "Dynamic Scene"); + + let state = DebugState { scene, user: () }; + + support::display::run( + display, + state, + move |time, state| { + for i in 0..NUM_SPHERES { + animate_sphere(&state.scene, i as u32, positions[i], radii[i], time); + } + state.scene.commit(); + }, + move |image, camera, time, state| { + render_frame(image, &camera, time, &colors, state); + }, + |_| {}, + ); +} + +fn render_pixel( + x: u32, + y: u32, + pixel: &mut u32, + _time: f32, + scene: &Scene, + camera: &Camera, + colors: &[Vec3], +) { + let dir = camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)); + let mut intersection_ctx = IntersectContext::coherent(); + let mut ray_hit = RayHit::from_ray(Ray::segment( + camera.pos.into(), + dir.into(), + 0.001, + f32::INFINITY, + )); + scene.intersect(&mut intersection_ctx, &mut ray_hit); + + if ray_hit.hit.is_valid() { + let diffuse = colors[ray_hit.hit.geomID as usize]; + + let mut shadow_ray = Ray::segment(ray_hit.ray.hit_point(), LIGHT_DIR, 0.001, f32::INFINITY); + + // Check if the shadow ray is occluded. + let color = if !scene.occluded(&mut intersection_ctx, &mut shadow_ray) { + diffuse + } else { + diffuse * 0.5 + }; + + *pixel = rgba_to_u32( + (color.x * 255.0) as u8, + (color.y * 255.0) as u8, + (color.z * 255.0) as u8, + 255, + ); + } +} + +fn render_frame( + frame: &mut TiledImage, + camera: &Camera, + time: f32, + colors: &[Vec3], + state: &mut DebugState<()>, +) { + frame.par_tiles_mut().for_each(|tile| { + tile.pixels.iter_mut().enumerate().for_each(|(i, pixel)| { + let x = tile.x + (i % tile.w as usize) as u32; + let y = tile.y + (i / tile.w as usize) as u32; + render_pixel(x, y, pixel, time, &state.scene, camera, colors); + }); + }); +} diff --git a/examples/instancing/Cargo.toml b/examples/instancing/Cargo.toml index 6135ea986..2299ffe0d 100644 --- a/examples/instancing/Cargo.toml +++ b/examples/instancing/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "instancing" version = "0.1.0" -authors = ["Will Usher "] +authors = ["Will Usher ", "Yang Chen "] edition = "2021" [dependencies] diff --git a/examples/instancing/src/main.rs b/examples/instancing/src/main.rs index b0f58b60d..00a37369c 100644 --- a/examples/instancing/src/main.rs +++ b/examples/instancing/src/main.rs @@ -1,93 +1,149 @@ #![allow(dead_code)] - -extern crate cgmath; extern crate embree; extern crate support; use cgmath::{InnerSpace, Matrix, Matrix4, SquareMatrix, Vector3, Vector4}; use embree::{ - Device, Geometry, Instance, IntersectContext, QuadMesh, Ray, RayHit, Scene, TriangleMesh, + BufferUsage, BuildQuality, Device, Format, Geometry, Instance, IntersectContext, Ray, RayHit, + SceneFlags, INVALID_ID, }; -use std::{f32, u32}; -use support::Camera; - -/// Make a triangulated sphere, from the Embree tutorial: -/// https://github.com/embree/embree/blob/master/tutorials/instanced_geometry/instanced_geometry_device.cpp -fn make_triangulated_sphere<'a>( - device: &'a Device, - pos: Vector3, - radius: f32, -) -> Geometry<'a> { - let num_phi = 5; - let num_theta = 2 * num_phi; - let mut mesh = TriangleMesh::unanimated( - device, - 2 * num_theta * (num_phi - 1), - num_theta * (num_phi + 1), - ); - { - let mut verts = mesh.vertex_buffer.map(); - let mut tris = mesh.index_buffer.map(); - - let inv_num_phi = 1.0 / (num_phi as f32); - let inv_num_theta = 1.0 / (num_theta as f32); - for phi in 0..num_phi + 1 { - for theta in 0..num_theta { - let phif = phi as f32 * f32::consts::PI * inv_num_phi; - let thetaf = theta as f32 * f32::consts::PI * 2.0 * inv_num_theta; - - let v = &mut verts[phi * num_theta + theta]; - v.x = pos.x + radius * f32::sin(phif) * f32::sin(thetaf); - v.y = pos.y + radius * f32::cos(phif); - v.z = pos.z + radius * f32::sin(phif) * f32::cos(thetaf); - } +use support::{rgba_to_u32, Camera, DebugState, ParallelIterator, TiledImage}; + +const NUM_PHI: usize = 5; +const NUM_THETA: usize = 2 * NUM_PHI; + +const COLORS: [[[f32; 3]; 4]; 4] = [ + [ + [0.25, 0.0, 0.0], + [0.5, 0.0, 0.0], + [0.75, 0.0, 0.0], + [1.0, 0.0, 0.0], + ], + [ + [0.0, 0.25, 0.0], + [0.0, 0.5, 0.0], + [0.0, 0.75, 0.0], + [0.0, 1.0, 0.0], + ], + [ + [0.0, 0.0, 0.25], + [0.0, 0.0, 0.5], + [0.0, 0.0, 0.75], + [0.0, 0.0, 1.0], + ], + [ + [0.25, 0.25, 0.0], + [0.5, 0.5, 0.0], + [0.75, 0.75, 0.0], + [1.0, 1.0, 0.0], + ], +]; + +fn create_sphere(device: &Device, pos: Vector3, radius: f32) -> Geometry<'static> { + // Create a triangulated sphere + let mut geometry = device + .create_geometry(embree::GeometryKind::TRIANGLE) + .unwrap(); + geometry.set_build_quality(BuildQuality::LOW); + + let mut vertices = geometry + .set_new_buffer( + BufferUsage::VERTEX, + 0, + Format::FLOAT3, + 16, + NUM_THETA * (NUM_PHI + 1), + ) + .unwrap() + .view_mut::<[f32; 4]>() + .unwrap(); + + let mut indices = geometry + .set_new_buffer( + BufferUsage::INDEX, + 0, + Format::UINT3, + 12, + 2 * NUM_THETA * (NUM_PHI - 1), + ) + .unwrap() + .view_mut::<[u32; 3]>() + .unwrap(); + + let mut tri = 0; + let rcp_num_theta = 1.0 / NUM_THETA as f32; + let rcp_num_phi = 1.0 / NUM_PHI as f32; + for phi_idx in 0..NUM_PHI { + for theta_idx in 0..NUM_THETA { + let phi = phi_idx as f32 * rcp_num_phi * std::f32::consts::PI; + let theta = theta_idx as f32 * rcp_num_theta * 2.0 * std::f32::consts::PI; + vertices[phi_idx * NUM_THETA + theta_idx] = [ + pos.x + radius * phi.sin() * theta.sin(), + pos.y + radius * phi.cos(), + pos.z + radius * phi.sin() * theta.cos(), + 0.0, + ]; + } + if phi_idx == 0 { + continue; } - let mut tri = 0; - for phi in 1..num_phi + 1 { - for theta in 1..num_theta + 1 { - let p00 = (phi - 1) * num_theta + theta - 1; - let p01 = (phi - 1) * num_theta + theta % num_theta; - let p10 = phi * num_theta + theta - 1; - let p11 = phi * num_theta + theta % num_theta; - - if phi > 1 { - tris[tri].x = p10 as u32; - tris[tri].y = p01 as u32; - tris[tri].z = p00 as u32; - tri += 1; - } - if phi < num_phi { - tris[tri].x = p11 as u32; - tris[tri].y = p01 as u32; - tris[tri].z = p10 as u32; - tri += 1; - } + for theta_idx in 1..=NUM_THETA { + let p00 = ((phi_idx - 1) * NUM_THETA + theta_idx - 1) as u32; + let p01 = ((phi_idx - 1) * NUM_THETA + theta_idx % NUM_THETA) as u32; + let p10 = (phi_idx * NUM_THETA + theta_idx - 1) as u32; + let p11 = (phi_idx * NUM_THETA + theta_idx % NUM_THETA) as u32; + + if phi_idx > 1 { + indices[tri] = [p10, p01, p00]; + tri += 1; + } + + if phi_idx < NUM_PHI { + indices[tri] = [p11, p01, p10]; + tri += 1; } } } - let mut mesh = Geometry::Triangle(mesh); - mesh.commit(); - mesh + geometry.commit(); + geometry } -fn make_ground_plane<'a>(device: &'a Device) -> Geometry<'a> { - let mut mesh = QuadMesh::unanimated(device, 1, 4); + +fn create_ground_plane(device: &Device) -> Geometry<'static> { + let mut geometry = Geometry::new(device, embree::GeometryKind::TRIANGLE).unwrap(); { - let mut verts = mesh.vertex_buffer.map(); - let mut quads = mesh.index_buffer.map(); - verts[0] = Vector4::new(-10.0, -2.0, -10.0, 0.0); - verts[1] = Vector4::new(-10.0, -2.0, 10.0, 0.0); - verts[2] = Vector4::new(10.0, -2.0, 10.0, 0.0); - verts[3] = Vector4::new(10.0, -2.0, -10.0, 0.0); - - quads[0] = Vector4::new(0, 1, 2, 3); + geometry + .set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, 4) + .unwrap() + .view_mut::<[f32; 4]>() + .unwrap() + .copy_from_slice(&[ + [-10.0, -2.0, -10.0, 0.0], + [-10.0, -2.0, 10.0, 0.0], + [10.0, -2.0, -10.0, 0.0], + [10.0, -2.0, 10.0, 0.0], + ]); + geometry + .set_new_buffer(BufferUsage::INDEX, 0, Format::UINT3, 12, 2) + .unwrap() + .view_mut::<[u32; 3]>() + .unwrap() + .copy_from_slice(&[[0, 1, 2], [1, 3, 2]]); } - let mut mesh = Geometry::Quad(mesh); - mesh.commit(); - mesh + geometry.commit(); + geometry } -// Animate like the Embree example, returns the (transforms, normal_transforms) -fn animate_instances(time: f32, num_instances: usize) -> (Vec>, Vec>) { + +// Animate like the Embree example. +fn animate_instances( + time: f32, + num_instances: usize, + transforms: &mut [Matrix4], + normal_transforms: &mut [Matrix4], +) { + debug_assert!(transforms.len() == num_instances); + debug_assert!(normal_transforms.len() == num_instances); + let t0 = 0.7 * time; let t1 = 1.5 * time; @@ -97,154 +153,177 @@ fn animate_instances(time: f32, num_instances: usize) -> (Vec>, Vec Vector4::new(-f32::sin(t1), 0.0, f32::cos(t1), 0.0), Vector4::new(0.0, 0.0, 0.0, 1.0), ); - - let mut transforms = Vec::with_capacity(num_instances); - let mut normal_transforms = Vec::with_capacity(num_instances); for i in 0..num_instances { - let t = t0 + i as f32 * 2.0 * f32::consts::PI / 4.0; + let t = t0 + i as f32 * 2.0 * std::f32::consts::PI / 4.0; let trans = Matrix4::::from_translation( 2.2 * Vector3::::new(f32::cos(t), 0.0, f32::sin(t)), ); - transforms.push(trans * rot); - normal_transforms.push(transforms[i].invert().unwrap().transpose()); + transforms[i] = trans * rot; + normal_transforms[i] = transforms[i].invert().unwrap().transpose(); } - (transforms, normal_transforms) +} + +struct UserState { + transforms: Vec>, + normal_transforms: Vec>, + ground_plane_id: u32, + light_dir: Vector3, } fn main() { - let mut display = support::Display::new(512, 512, "instancing"); - let device = Device::new(); + let display = support::Display::new(512, 512, "instancing"); + let device = Device::new().unwrap(); + + // Create a scene. + let mut scene = device.create_scene().unwrap(); + scene.set_build_quality(BuildQuality::LOW); + scene.set_flags(SceneFlags::DYNAMIC); - // Make the scene we'll instance with 4 triangulated spheres. + // Create a scene with 4 triangulated spheres. + let mut scene1 = device.create_scene().unwrap(); let spheres = vec![ - make_triangulated_sphere(&device, Vector3::new(0.0, 0.0, 1.0), 0.5), - make_triangulated_sphere(&device, Vector3::new(1.0, 0.0, 0.0), 0.5), - make_triangulated_sphere(&device, Vector3::new(0.0, 0.0, -1.0), 0.5), - make_triangulated_sphere(&device, Vector3::new(-1.0, 0.0, 0.0), 0.5), + create_sphere(&device, Vector3::new(0.0, 0.0, 1.0), 0.5), + create_sphere(&device, Vector3::new(1.0, 0.0, 0.0), 0.5), + create_sphere(&device, Vector3::new(0.0, 0.0, -1.0), 0.5), + create_sphere(&device, Vector3::new(-1.0, 0.0, 0.0), 0.5), ]; - let mut instanced_scene = Scene::new(&device); for s in spheres.into_iter() { - instanced_scene.attach_geometry(s); + scene1.attach_geometry(&s); } - let committed_instance = instanced_scene.commit(); - - // Make the instances first so their ids will be 0-3 that we can then use - // directly to index into the instance_colors - let instances = vec![ - Instance::unanimated(&device, &committed_instance), - Instance::unanimated(&device, &committed_instance), - Instance::unanimated(&device, &committed_instance), - Instance::unanimated(&device, &committed_instance), + scene1.commit(); + + // Instantiate geometries + let mut instances = vec![ + Instance::new(&device).unwrap(), + Instance::new(&device).unwrap(), + Instance::new(&device).unwrap(), + Instance::new(&device).unwrap(), ]; - let num_instances = instances.len(); - let mut scene = Scene::new(&device); - for i in instances.into_iter() { - scene.attach_geometry(Geometry::Instance(i)); + for inst in instances.iter_mut() { + inst.set_instanced_scene(&scene1); + inst.set_time_step_count(1); + inst.commit(); + scene.attach_geometry(&inst); } + scene.commit(); - let instance_colors = vec![ - vec![ - Vector3::new(0.25, 0.0, 0.0), - Vector3::new(0.5, 0.0, 0.0), - Vector3::new(0.75, 0.0, 0.0), - Vector3::new(1.00, 0.0, 0.0), - ], - vec![ - Vector3::new(0.0, 0.25, 0.0), - Vector3::new(0.0, 0.50, 0.0), - Vector3::new(0.0, 0.75, 0.0), - Vector3::new(0.0, 1.00, 0.0), - ], - vec![ - Vector3::new(0.0, 0.0, 0.25), - Vector3::new(0.0, 0.0, 0.50), - Vector3::new(0.0, 0.0, 0.75), - Vector3::new(0.0, 0.0, 1.00), - ], - vec![ - Vector3::new(0.25, 0.25, 0.0), - Vector3::new(0.50, 0.50, 0.0), - Vector3::new(0.75, 0.75, 0.0), - Vector3::new(1.00, 1.00, 0.0), - ], - ]; + let ground_plane = create_ground_plane(&device); + let ground_plane_id = scene.attach_geometry(&ground_plane); - let ground = make_ground_plane(&device); - let ground_id = scene.attach_geometry(ground); + let user_state = UserState { + transforms: vec![Matrix4::identity(); instances.len()], + normal_transforms: vec![Matrix4::identity(); instances.len()], + ground_plane_id, + light_dir: Vector3::new(1.0, 1.0, -1.0).normalize(), + }; - let light_dir = Vector3::new(1.0, 1.0, -1.0).normalize(); - let mut intersection_ctx = IntersectContext::coherent(); + let state = DebugState { + scene: scene.clone(), + user: user_state, + }; - display.run(|image, camera_pose, time| { - for p in image.iter_mut() { - *p = 0; - } - - // Update scene transformations - let (transforms, normal_transforms) = animate_instances(time, num_instances); - let mut tfm_iter = transforms.iter(); - for g in scene.iter_mut() { - if let Geometry::Instance(ref mut inst) = g.1 { - inst.set_transform(tfm_iter.next().expect("out of bounds tfm")); + support::display::run( + display, + state, + move |time, state| { + // Update scene transformations + animate_instances( + time, + instances.len(), + &mut state.user.transforms, + &mut state.user.normal_transforms, + ); + for (inst, tfm) in instances.iter_mut().zip(state.user.transforms.iter()) { + inst.set_transform(0, tfm.as_ref()); + inst.commit(); } - // A bit annoying here that we can't call the mut on the geometry - // part because we borred the inner instance piece as mutable - g.1.commit(); - } + state.scene.commit(); + }, + render_frame, + |_| {}, + ); +} - let rtscene = scene.commit(); +fn render_pixel( + x: u32, + y: u32, + pixel: &mut u32, + _time: f32, + camera: &Camera, + state: &DebugState, +) { + let mut ctx = IntersectContext::coherent(); + let dir = camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)); + let mut ray_hit = RayHit::from_ray(Ray::segment( + camera.pos.into(), + dir.into(), + 0.001, + f32::INFINITY, + )); + state.scene.intersect(&mut ctx, &mut ray_hit); - let img_dims = image.dimensions(); - let camera = Camera::look_dir( - camera_pose.pos, - camera_pose.dir, - camera_pose.up, - 55.0, - img_dims, + if ray_hit.hit.is_valid() { + // Transform the normals of the instances into world space with the + // normal_transforms + let hit = &ray_hit.hit; + let geom_id = hit.geomID; + let inst_id = hit.instID[0]; + let mut normal = Vector3::from(hit.unit_normal()); + if inst_id != INVALID_ID { + let v = state.user.normal_transforms[inst_id as usize] + * Vector4::new(normal.x, normal.y, normal.z, 0.0); + normal = Vector3::new(v.x, v.y, v.z).normalize() + } + let mut illum = 0.3; + let shadow_pos = camera.pos + dir * ray_hit.ray.tfar; + let mut shadow_ray = Ray::segment( + shadow_pos.into(), + state.user.light_dir.into(), + 0.001, + f32::INFINITY, ); - // Render the scene - for j in 0..img_dims.1 { - for i in 0..img_dims.0 { - let dir = camera.ray_dir((i as f32 + 0.5, j as f32 + 0.5)); - let mut ray_hit = RayHit::new(Ray::new(camera.pos, dir)); - rtscene.intersect(&mut intersection_ctx, &mut ray_hit); - - if ray_hit.hit.hit() { - // Transform the normals of the instances into world space with the normal_transforms - let hit = &ray_hit.hit; - let geom_id = hit.geomID; - let inst_id = hit.instID[0]; - let mut normal = Vector3::new(hit.Ng_x, hit.Ng_y, hit.Ng_z).normalize(); - if inst_id != u32::MAX { - let v = normal_transforms[inst_id as usize] - * Vector4::new(normal.x, normal.y, normal.z, 0.0); - normal = Vector3::new(v.x, v.y, v.z).normalize() - } - let mut illum = 0.3; - let shadow_pos = camera.pos + dir * ray_hit.ray.tfar; - let mut shadow_ray = Ray::segment(shadow_pos, light_dir, 0.001, f32::INFINITY); - rtscene.occluded(&mut intersection_ctx, &mut shadow_ray); - - if shadow_ray.tfar >= 0.0 { - illum = - support::clamp(illum + f32::max(light_dir.dot(normal), 0.0), 0.0, 1.0); - } - - let p = image.get_pixel_mut(i, j); - if inst_id == u32::MAX && geom_id == ground_id { - p[0] = (255.0 * illum) as u8; - p[1] = p[0]; - p[2] = p[0]; - } else { - // Shade the instances using their color - let color = &instance_colors[inst_id as usize][geom_id as usize]; - p[0] = (255.0 * illum * color.x) as u8; - p[1] = (255.0 * illum * color.y) as u8; - p[2] = (255.0 * illum * color.z) as u8; - } - } - } + state.scene.occluded(&mut ctx, &mut shadow_ray); + + if shadow_ray.tfar >= 0.0 { + illum = support::clamp( + illum + f32::max(state.user.light_dir.dot(normal), 0.0), + 0.0, + 1.0, + ); } + + *pixel = if inst_id == INVALID_ID && geom_id == state.user.ground_plane_id { + rgba_to_u32( + (255.0 * illum) as u8, + (255.0 * illum) as u8, + (255.0 * illum) as u8, + 255, + ) + } else { + // Shade the instances using their color + let color = &COLORS[inst_id as usize][geom_id as usize]; + rgba_to_u32( + (255.0 * illum * color[0]) as u8, + (255.0 * illum * color[1]) as u8, + (255.0 * illum * color[2]) as u8, + 255, + ) + } + } +} + +fn render_frame( + frame: &mut TiledImage, + camera: &Camera, + time: f32, + state: &mut DebugState, +) { + frame.par_tiles_mut().for_each(|tile| { + tile.pixels.iter_mut().enumerate().for_each(|(i, pixel)| { + let x = tile.x + (i % tile.w as usize) as u32; + let y = tile.y + (i / tile.w as usize) as u32; + render_pixel(x, y, pixel, time, camera, state); + }); }); } diff --git a/examples/intersection_filter/Cargo.toml b/examples/intersection_filter/Cargo.toml new file mode 100644 index 000000000..70b24538f --- /dev/null +++ b/examples/intersection_filter/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "intersection_filter" +authors = ["Yang Chen "] +version = "0.1.0" +edition = "2021" + +[dependencies] +glam = "0.23.0" +embree = { path = "../.." } +support = { path = "../support" } diff --git a/examples/intersection_filter/src/main.rs b/examples/intersection_filter/src/main.rs new file mode 100644 index 000000000..fa96bf0f1 --- /dev/null +++ b/examples/intersection_filter/src/main.rs @@ -0,0 +1,609 @@ +//! This example shows how to use filter callback functions to efficiently +//! implement transparent objects. +//! +//! The filter function is used for primary rays lets the ray pass through +//! the geometry if it is entirely transparent. Otherwise, the shading loop +//! handles the transparency properly, by potentially shooting secondary rays. +//! +//! The filter function used for shadow rays accumulates the transparency of +//! all surfaces along the ray, and terminates traversal if an opaque surface +//! occluder is hit. + +use embree::{ + AlignedArray, BufferSlice, BufferUsage, BuildQuality, Device, Format, Geometry, GeometryKind, + HitN, IntersectContextExt, Ray, RayHit, RayN, Scene, SoAHit, SoARay, ValidityN, INVALID_ID, +}; +use glam::{vec3, Mat4, Vec3, Vec4}; +use support::{ + rgba_to_u32, Camera, DebugState, Mode, ParallelIterator, TileMut, TiledImage, + DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH, +}; + +const CUBE_NUM_VERTICES: usize = 8; +const CUBE_NUM_QUAD_INDICES: usize = 24; +const CUBE_NUM_TRI_INDICES: usize = 36; +const CUBE_NUM_QUAD_FACES: usize = 6; +const CUBE_NUM_TRI_FACES: usize = 12; + +const MODE: Mode = Mode::Stream; + +const HIT_LIST_LEN: usize = 16; +const COLORS: [[f32; 3]; 12] = [ + [1.0, 0.0, 0.0], + [1.0, 0.0, 0.0], + [0.0, 1.0, 0.0], + [0.0, 1.0, 0.0], + [0.5, 0.5, 0.5], + [0.5, 0.5, 0.5], + [1.0, 1.0, 1.0], + [1.0, 1.0, 1.0], + [0.0, 0.0, 1.0], + [0.0, 0.0, 1.0], + [1.0, 1.0, 0.0], + [1.0, 1.0, 0.0], +]; + +const CUBE_VERTICES: AlignedArray<[f32; 4], CUBE_NUM_VERTICES> = AlignedArray([ + [-1.0, -1.0, -1.0, 1.0], + [-1.0, -1.0, 1.0, 1.0], + [-1.0, 1.0, -1.0, 1.0], + [-1.0, 1.0, 1.0, 1.0], + [1.0, -1.0, -1.0, 1.0], + [1.0, -1.0, 1.0, 1.0], + [1.0, 1.0, -1.0, 1.0], + [1.0, 1.0, 1.0, 1.0], +]); + +#[allow(dead_code)] +const CUBE_QUAD_INDICES: AlignedArray = AlignedArray([ + 0, 1, 3, 2, // + 5, 4, 6, 7, // + 0, 4, 5, 1, // + 6, 2, 3, 7, // + 0, 2, 6, 4, // + 3, 1, 5, 7, // +]); + +const CUBE_TRI_INDICES: AlignedArray = AlignedArray([ + 0, 1, 3, // + 3, 1, 2, // + 5, 4, 6, // + 6, 4, 7, // + 0, 4, 5, // + 5, 1, 0, // + 6, 2, 3, // + 3, 7, 6, // + 0, 2, 6, // + 6, 4, 0, // + 3, 1, 5, // + 5, 7, 3, // +]); + +#[allow(dead_code)] +const CUBE_QUAD_FACES: [u32; CUBE_NUM_QUAD_FACES] = [4; 6]; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +struct RayExtra { + transparency: f32, // accumulated transparency + first_hit: u32, // index of first hit + last_hit: u32, // index of last hit + hit_geom_ids: [u32; HIT_LIST_LEN], + hit_prim_ids: [u32; HIT_LIST_LEN], +} + +impl Default for RayExtra { + fn default() -> Self { + RayExtra { + transparency: 1.0, + first_hit: 0, + last_hit: 0, + hit_geom_ids: [0; HIT_LIST_LEN], + hit_prim_ids: [0; HIT_LIST_LEN], + } + } +} + +fn transparency_function(h: [f32; 3]) -> f32 { + let v = ((4.0 * h[0]).sin() * (4.0 * h[1]).cos() * (4.0 * h[2]).sin()).abs(); + ((v - 0.1) * 3.0).clamp(0.0, 1.0) +} + +type IntersectContext2 = IntersectContextExt; +type IntersectContext2Stream = IntersectContextExt>; + +fn render_pixel(x: u32, y: u32, camera: &Camera, scene: &Scene) -> u32 { + let mut weight = 1.0; + let mut color = Vec3::ZERO; + let mut primary = RayHit::from_ray(Ray::new( + camera.pos.into(), + camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)).into(), + 0.0, + f32::INFINITY, + 0.0, + u32::MAX, + 0, // needs to encode rayID for filter function + )); + let mut primary_extra = RayExtra { + transparency: 0.0, + ..Default::default() + }; + let mut primary_ctx = IntersectContext2::coherent(primary_extra); + + let shadow_extra = RayExtra::default(); + let mut ctx_shadow = IntersectContext2::coherent(shadow_extra); + + loop { + scene.intersect(&mut primary_ctx, &mut primary); + + if !primary.hit.is_valid() { + break; + } + + let opacity = 1.0 - primary_ctx.ext.transparency; + let diffuse = Vec3::from(COLORS[primary.hit.primID as usize]); + let la = diffuse * 0.5; + color += weight * opacity * la; + let light_dir = vec3(0.57, 0.57, 0.57); + + // initialize shadow ray + let mut shadow_ray = Ray::segment_with_id( + primary.ray.hit_point(), + light_dir.into(), + 0.001, + f32::INFINITY, + 0, + ); + + if !scene.occluded(&mut ctx_shadow, &mut shadow_ray) { + let ll = diffuse + * shadow_extra.transparency + * light_dir + .dot(primary.hit.unit_normal().into()) // + .clamp(0.0, 1.0); + color += weight * opacity * ll; + } + + weight *= primary_extra.transparency; + primary.ray.tnear = 1.001 * primary.ray.tfar; + primary.ray.tfar = f32::INFINITY; + primary.hit.geomID = embree::INVALID_ID; + primary.hit.primID = embree::INVALID_ID; + primary_extra.transparency = 0.0; + } + + rgba_to_u32( + (color.x.clamp(0.0, 1.0) * 255.0) as u8, + (color.y.clamp(0.0, 1.0) * 255.0) as u8, + (color.z.clamp(0.0, 1.0) * 255.0) as u8, + 255, + ) +} + +fn render_tile(tile: &mut TileMut, camera: &Camera, scene: &Scene) { + tile.pixels.iter_mut().enumerate().for_each(|(i, pixel)| { + let x = tile.x + (i % tile.w as usize) as u32; + let y = tile.y + (i / tile.w as usize) as u32; + *pixel = render_pixel(x, y, camera, scene); + }); +} + +fn render_tile_stream(tile: &mut TileMut, width: u32, height: u32, camera: &Camera, scene: &Scene) { + let tile_x_end = (tile.x + tile.w).min(width); + let tile_y_end = (tile.y + tile.h).min(height); + let tile_w = tile_x_end - tile.x; + let tile_h = tile_y_end - tile.y; + let tile_size = (tile_w * tile_h) as usize; + let mut weights = vec![1.0; tile_size]; + let mut colors = vec![Vec3::ZERO; tile_size]; + let mut primary = vec![RayHit::default(); tile_size]; + let primary_extra = vec![RayExtra::default(); tile_size]; + let mut primary_ctx = IntersectContext2Stream::coherent(primary_extra); + let mut shadows = vec![Ray::default(); tile_size]; + let shadows_extra = vec![RayExtra::default(); tile_size]; + let mut shadows_ctx = IntersectContext2Stream::coherent(shadows_extra); + let mut validates = vec![true; tile_size]; + + // actual number of rays in stream may be less than number of pixels in tile + let mut i = 0; + let mut num_active = 0; + // generate stream of primary rays + for y in tile.y..tile_y_end { + for x in tile.x..tile_x_end { + num_active += 1; + validates[i] = true; + primary[i] = RayHit::from_ray(Ray::segment_with_id( + camera.pos.into(), + camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)).into(), + 0.0, + f32::INFINITY, + i as u32, // needs to encode rayID for filter function + )); + primary_ctx.ext[i] = RayExtra { + transparency: 0.0, + ..Default::default() + }; + i += 1; + } + } + + let light_dir = vec3(0.57, 0.57, 0.57); + + while num_active > 0 { + //let mut primary_context = + // IntersectContext2Stream::coherent(primaries_extra.as_mut_slice()); + scene.intersect_stream_aos(&mut primary_ctx, &mut primary); + // terminate rays and update color + for n in 0..tile_size as usize { + // invalidate shadow rays by default + shadows[n].tnear = f32::INFINITY; + shadows[n].tfar = f32::NEG_INFINITY; + + // ignore invalid rays + if !validates[n] { + continue; + } + + // terminate ray if it did not hit anything + if !primary[n].hit.is_valid() { + validates[n] = false; + continue; + } + + // update color + let opacity = 1.0 - primary_ctx.ext[n].transparency; + let diffuse = Vec3::from(COLORS[primary[n].hit.primID as usize]); + let la = diffuse * 0.5; + colors[n] += weights[n] * opacity * la; + + // initialize shadow ray + { + shadows[n] = Ray::segment_with_id( + primary[n].ray.hit_point(), + light_dir.into(), + 0.001, + f32::INFINITY, + n as u32, + ); + shadows_ctx.ext[n] = RayExtra::default(); + } + } + + // trace shadow rays + // let mut shadow_context = + // IntersectContext2Stream::coherent(shadows_extra.as_mut_slice()); + scene.occluded_stream_aos(&mut shadows_ctx, &mut shadows); + + // add light contribution and generate transmission rays + num_active = 0; + for n in 0..tile_size as usize { + // invalidate rays by default + let primary_tfar = primary[n].ray.tfar; + primary[n].ray.tnear = f32::INFINITY; + primary[n].ray.tfar = f32::NEG_INFINITY; + + /* ignore invalid rays */ + if !validates[n] { + continue; + } + + num_active += 1; + + // add light contribution + let opacity = 1.0 - primary_ctx.ext[n].transparency; + let diffuse = Vec3::from(COLORS[primary[n].hit.primID as usize]); + if shadows[n].tfar != f32::NEG_INFINITY { + let ll = diffuse + * shadows_ctx.ext[n].transparency + * light_dir + .dot(primary[n].hit.unit_normal().into()) + .clamp(0.0, 1.0); + colors[n] += weights[n] * opacity * ll; + } + /* initialize transmission ray */ + weights[n] *= primary_ctx.ext[n].transparency; + primary[n].ray.tnear = 1.001 * primary_tfar; + primary[n].ray.tfar = f32::INFINITY; + primary[n].hit.geomID = INVALID_ID; + primary[n].hit.primID = INVALID_ID; + primary_ctx.ext[n].transparency = 0.0; + } + } + + // write color to tile + i = 0; + for y in 0..tile_h { + for x in 0..tile_w { + tile.pixels[(y * tile_w + x) as usize] = rgba_to_u32( + (colors[i].x.clamp(0.0, 1.0) * 255.0) as u8, + (colors[i].y.clamp(0.0, 1.0) * 255.0) as u8, + (colors[i].z.clamp(0.0, 1.0) * 255.0) as u8, + 255, + ); + i += 1; + } + } +} + +fn render_frame(frame: &mut TiledImage, camera: &Camera, scene: &Scene) { + let width = frame.width; + let height = frame.height; + match MODE { + Mode::Normal => { + frame + .par_tiles_mut() + .for_each(|mut tile| render_tile(&mut tile, camera, scene)); + } + Mode::Stream => { + frame + .par_tiles_mut() + .for_each(|mut tile| render_tile_stream(&mut tile, width, height, camera, scene)); + } + } +} + +fn intersect_filter<'a>( + rays: RayN<'a>, + _hits: HitN<'a>, + mut valid: ValidityN<'a>, + ctx: &mut IntersectContext2, + _user_data: Option<&mut ()>, +) { + assert_eq!(rays.len(), 1); + + // ignore invalid rays + if valid[0] != -1 { + return; + } + + // calculate transparency + let t = transparency_function(rays.hit_point(0)); + + // ignore hit if completely transparent + if t >= 1.0 { + valid[0] = 0; + } else { + // otherwise accept hit and remember transparency + ctx.ext.transparency = t; + } +} + +fn intersect_filter_n<'a, 'b>( + rays: RayN<'a>, + _hits: HitN<'a>, + mut valid: ValidityN<'a>, + ctx: &'b mut IntersectContext2Stream, + _user_data: Option<&mut ()>, +) { + assert_eq!(rays.len(), valid.len()); + let n = rays.len(); + // iterate over all rays in ray packet + for i in 0..n { + // calculate loop and execution mask + let vi = i; + if vi >= n { + continue; + } + + // ignore invalid rays + if valid[vi] != -1 { + continue; + } + + // calculate transparency + let t = transparency_function(rays.hit_point(i)); + // ignore hit if completely transparent + if t >= 1.0 { + valid[vi] = 0; + } else { + // otherwise accept hit and remember transparency + ctx.ext[rays.id(i) as usize].transparency = t; + } + } +} + +fn occluded_filter<'a>( + rays: RayN<'a>, + hits: HitN<'a>, + mut valid: ValidityN<'a>, + context: &mut IntersectContext2, + _user_data: Option<&mut ()>, +) { + assert_eq!(rays.len(), 1); + + if valid[0] != -1 { + return; + } + + for i in context.ext.first_hit..context.ext.last_hit { + let slot = i as usize % HIT_LIST_LEN; + if context.ext.hit_geom_ids[slot] == hits.geom_id(0) + && context.ext.hit_prim_ids[slot] == hits.prim_id(0) + { + valid[0] = 0; // ignore duplicate intersections + return; + } + } + + // store hit in hit list + let slot = context.ext.last_hit % HIT_LIST_LEN as u32; + context.ext.hit_geom_ids[slot as usize] = hits.geom_id(0); + context.ext.hit_prim_ids[slot as usize] = hits.prim_id(0); + context.ext.last_hit += 1; + + if context.ext.last_hit - context.ext.first_hit > HIT_LIST_LEN as u32 { + context.ext.first_hit += 1; + } + + let t = transparency_function(rays.hit_point(0)); + context.ext.transparency *= t; + if t != 0.0 { + valid[0] = 0; + } +} + +fn occluded_filter_n<'a>( + rays: RayN<'a>, + hits: HitN<'a>, + mut valid: ValidityN<'a>, + ctx: &mut IntersectContext2Stream, + _user_data: Option<&mut ()>, +) { + assert_eq!(rays.len(), valid.len()); + let n = rays.len(); + + // iterate over all rays in ray packet + for i in 0..n { + // calculate loop and execution mask + let vi = i as usize; + if vi >= n { + continue; + } + + // ignore invalid rays + if valid[vi] != -1 { + continue; + } + + let hit_geom_id = hits.geom_id(i); + let hit_prim_id = hits.prim_id(i); + + // the occlusion filter may be called multiple times with the same hit, + // we remember the last N hits, and skip duplicates + let rid = rays.id(i) as usize; + let first_hit = ctx.ext[rid].first_hit; + let mut last_hit = ctx.ext[rid].last_hit; + for j in first_hit..last_hit { + let slot = j as usize % HIT_LIST_LEN; + let last_geom_id = ctx.ext[rid].hit_geom_ids[slot]; + let last_prim_id = ctx.ext[rid].hit_prim_ids[slot]; + if last_geom_id == hit_geom_id && last_prim_id == hit_prim_id { + valid[vi] = 0; // ignore duplicate intersections + break; + } + } + if valid[vi] == 0 { + continue; + } + + // store hit in hit list + let slot = last_hit % HIT_LIST_LEN as u32; + ctx.ext[rid].hit_geom_ids[slot as usize] = hit_geom_id; + ctx.ext[rid].hit_prim_ids[slot as usize] = hit_prim_id; + last_hit += 1; + ctx.ext[rid].last_hit = last_hit; + if last_hit - first_hit >= HIT_LIST_LEN as u32 { + ctx.ext[rid].first_hit = first_hit + 1; + } + + // calculate transparency + let t = transparency_function(rays.hit_point(i)) * ctx.ext[rid].transparency; + ctx.ext[rid].transparency = t; + + // reject a hit if not fully opaque + if t != 0.0 { + valid[vi] = 0; + } + } +} + +fn create_ground_plane<'a>(device: &Device) -> Geometry<'a> { + let mut mesh = device.create_geometry(GeometryKind::QUAD).unwrap(); + { + mesh.set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, 4) + .unwrap() + .view_mut::<[f32; 4]>() + .unwrap() + .copy_from_slice(&[ + [-10.0, -2.0, -10.0, 0.0], + [-10.0, -2.0, 10.0, 0.0], + [10.0, -2.0, 10.0, 0.0], + [10.0, -2.0, -10.0, 0.0], + ]); + mesh.set_new_buffer(BufferUsage::INDEX, 0, Format::UINT4, 16, 1) + .unwrap() + .view_mut::<[u32; 4]>() + .unwrap() + .copy_from_slice(&[[0, 1, 2, 3]]); + } + mesh.commit(); + mesh +} + +fn create_cube<'a>(device: &Device, offset: Vec3, scale: Vec3, rotation: f32) -> Geometry<'a> { + // create a triangulated cube with 12 triangles and 8 vertices + let mut geom = device.create_geometry(GeometryKind::TRIANGLE).unwrap(); + let rotated = CUBE_VERTICES.map(|v| { + (Mat4::from_translation(offset) + * Mat4::from_axis_angle(Vec3::Y, rotation) + * Mat4::from_scale(scale) + * Vec4::from(v)) + .into() + }); + geom.set_new_buffer( + BufferUsage::VERTEX, + 0, + Format::FLOAT3, + 16, + CUBE_NUM_VERTICES, + ) + .unwrap() + .view_mut::<[f32; 4]>() + .unwrap() + .copy_from_slice(&rotated); + geom.set_buffer( + BufferUsage::INDEX, + 0, + Format::UINT3, + BufferSlice::from_slice(CUBE_TRI_INDICES.as_slice(), ..), + std::mem::size_of::() * 3, + CUBE_NUM_TRI_FACES, + ) + .unwrap(); + + // set intersection filter for the cube + match MODE { + Mode::Normal => { + geom.set_intersect_filter_function(intersect_filter); + geom.set_occluded_filter_function(occluded_filter); + } + Mode::Stream => { + geom.set_intersect_filter_function(intersect_filter_n); + geom.set_occluded_filter_function(occluded_filter_n); + } + } + geom.commit(); + geom +} + +fn main() { + let device = Device::new().unwrap(); + let mut scene = device.create_scene().unwrap(); + scene.set_build_quality(BuildQuality::HIGH); + let ground = create_ground_plane(&device); + let cube = create_cube(&device, vec3(0.0, 0.0, 0.0), vec3(10.0, 1.0, 1.0), 45.0); + scene.attach_geometry(&ground); + scene.attach_geometry(&cube); + scene.commit(); + + let display = support::Display::new( + DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT, + "Intersection Filter", + ); + + let state = DebugState { + scene: scene.clone(), + user: (), + }; + + support::display::run( + display, + state, + |_, _| {}, + move |image, camera, _, _| { + render_frame(image, &camera, &scene); + }, + |_| {}, + ); +} diff --git a/examples/minimal/Cargo.toml b/examples/minimal/Cargo.toml new file mode 100644 index 000000000..6d78441c9 --- /dev/null +++ b/examples/minimal/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "minimal" +version = "0.1.0" +authors = ["Yang Chen "] +edition = "2021" + +[dependencies] +embree = { path = "../.." } +support = { path = "../support" } +glam = "0.23.0" diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs new file mode 100644 index 000000000..093716749 --- /dev/null +++ b/examples/minimal/src/main.rs @@ -0,0 +1,86 @@ +//! This example shows how to intersect a ray with a single triangle. + +use embree::{BufferUsage, Device, Format, GeometryKind, IntersectContext, Ray, RayHit, Scene}; + +/// Casts a single ray with the given origin and direction. +fn cast_ray(scene: &Scene, origin: [f32; 3], direction: [f32; 3]) { + let ray = Ray::segment(origin, direction, 0.0, f32::INFINITY); + + // The intersect context can be used to set intersection filters or flags, and + // it also contains the instance ID stack used in multi-level instancing. + let mut context = IntersectContext::coherent(); + + // Intersect a single ray with the scene. + let mut ray_hit = RayHit::from(ray); + scene.intersect(&mut context, &mut ray_hit); + + print!("{origin:?} "); + + if ray_hit.hit.is_valid() { + println!( + "Found intersection on geometry {}, primitive {} at tfar = {}", + ray_hit.hit.geomID, ray_hit.hit.primID, ray_hit.ray.tfar + ); + } else { + println!("Did not find any intersection"); + } +} + +fn main() { + let device = Device::new().unwrap(); + + device.set_error_function(|err, msg| { + println!("[embree] {:?}: {}", err, msg); + }); + + let mut scene = device.create_scene().unwrap(); + { + // Create a triangle mesh geometry, and initialise a single triangle. + let mut triangle = device.create_geometry(GeometryKind::TRIANGLE).unwrap(); + triangle + .set_new_buffer( + BufferUsage::VERTEX, + 0, + Format::FLOAT3, + 3 * std::mem::size_of::(), + 3, + ) + .unwrap() + .view_mut::<[f32; 3]>() + .unwrap() + .copy_from_slice(&[ + [0.0, 0.0, 0.0], // vertex 0 + [1.0, 0.0, 0.0], // vertex 1 + [0.0, 1.0, 0.0], // vertex 2 + ]); + triangle + .set_new_buffer( + BufferUsage::INDEX, + 0, + Format::UINT3, + 3 * std::mem::size_of::(), + 1, + ) + .unwrap() + .view_mut::<[u32; 3]>() + .unwrap() + .copy_from_slice(&[ + [0, 1, 2], // triangle 0 + ]); + + // Geometry objects must be committed when you are done setting them up, + // otherwise you will not get any intersection results. + triangle.commit(); + + scene.attach_geometry(&triangle); + + // The scene must also be committed when you are done setting it up. + scene.commit(); + + // The geometry will be dropped when it goes out of scope, but the scene + // will still hold a reference to it. + } + + cast_ray(&scene, [0.0, 0.0, -1.0], [0.0, 0.0, 1.0]); + cast_ray(&scene, [1.0, 1.0, -1.0], [0.0, 0.0, 1.0]); +} diff --git a/examples/support/Cargo.toml b/examples/support/Cargo.toml index 74fc1ac68..b1a0483ef 100644 --- a/examples/support/Cargo.toml +++ b/examples/support/Cargo.toml @@ -5,11 +5,16 @@ authors = ["Will Usher "] edition = "2021" [dependencies] -image = "0.23.0" +image = "0.24.5" arcball = "1.1.0" cgmath = "0.18" clock_ticks = "0.1.1" -wgpu = "0.12" -winit = "0.26" -futures = {version = "0.3", features = ["executor"]} +egui = { version = "0.21", features = ["bytemuck"] } +egui-winit = "0.21" +egui-wgpu = "0.21" +embree = { path = "../.." } +wgpu = "0.15.1" +winit = "0.28.1" +futures = { version = "0.3", features = ["executor"]} +rayon = "1.5" diff --git a/examples/support/src/camera.rs b/examples/support/src/camera.rs index 7042e0999..debf94dcc 100644 --- a/examples/support/src/camera.rs +++ b/examples/support/src/camera.rs @@ -2,7 +2,7 @@ use std::f32; use cgmath::InnerSpace; -use cgmath::{Matrix4, SquareMatrix, Vector2, Vector3, Vector4}; +use cgmath::Vector3; #[derive(PartialEq)] pub struct Camera { @@ -31,11 +31,11 @@ impl Camera { let screen_dv = dy * dim_y; let dir_top_left = dz - 0.5 * screen_du - 0.5 * screen_dv; Camera { - pos: pos, - dir_top_left: dir_top_left, - screen_du: screen_du, - screen_dv: screen_dv, - img: img, + pos, + dir_top_left, + screen_du, + screen_dv, + img, } } pub fn look_at( diff --git a/examples/support/src/common.rs b/examples/support/src/common.rs new file mode 100644 index 000000000..a355ba4c6 --- /dev/null +++ b/examples/support/src/common.rs @@ -0,0 +1,608 @@ +pub const DEFAULT_DISPLAY_WIDTH: u32 = 512; +pub const DEFAULT_DISPLAY_HEIGHT: u32 = 512; + +/// Size (horizontal) of a screen tile in pixels. +pub const TILE_SIZE_X: u32 = 8; + +/// Size (vertical) of a screen tile in pixels. +pub const TILE_SIZE_Y: u32 = 8; + +/// Size of a screen tile in pixels. +pub const TILE_SIZE: u32 = TILE_SIZE_X * TILE_SIZE_Y; + +pub const PERMUTATIONS: [u32; 513] = [ + 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, + 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, + 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, + 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, + 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, + 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, + 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, + 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, + 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, + 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, + 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, + 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, + 128, 195, 78, 66, 215, 61, 156, 180, 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, + 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, + 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, + 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, + 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, + 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, + 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, + 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, + 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, + 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, + 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, + 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, + 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180, 151, +]; + +pub const G3: [f32; 128 * 4] = [ + -0.582745, + 0.443494, + -0.680971, + 0.0, + -0.601153, + 0.791961, + 0.106833, + 0.0, + -0.265466, + 0.576385, + -0.772857, + 0.0, + 0.981035, + 0.0963612, + -0.16818, + 0.0, + 0.524388, + 0.819103, + 0.232568, + 0.0, + -0.170518, + -0.43875, + 0.882282, + 0.0, + 0.598053, + -0.435348, + 0.672908, + 0.0, + 0.53956, + 0.839346, + -0.0661294, + 0.0, + -0.782511, + -0.600267, + -0.165398, + 0.0, + -0.122114, + 0.968043, + 0.219044, + 0.0, + -0.235567, + 0.842331, + -0.484755, + 0.0, + -0.158657, + 0.504139, + 0.848924, + 0.0, + -0.578396, + 0.39317, + -0.714756, + 0.0, + 0.883328, + -0.337159, + -0.325661, + 0.0, + 0.0597264, + -0.0861552, + 0.99449, + 0.0, + -0.970124, + 0.233685, + -0.0651921, + 0.0, + 0.208238, + -0.858421, + 0.468776, + 0.0, + 0.916908, + -0.0997567, + 0.38643, + 0.0, + -0.786568, + -0.577957, + -0.217431, + 0.0, + 0.14868, + 0.618251, + -0.77179, + 0.0, + -0.24168, + 0.675858, + 0.69628, + 0.0, + -0.50994, + 0.83025, + 0.225046, + 0.0, + -0.534183, + -0.676382, + -0.507107, + 0.0, + -0.793861, + -0.6048, + -0.0632565, + 0.0, + -0.92148, + 0.240548, + -0.304977, + 0.0, + -0.210037, + 0.39862, + -0.892741, + 0.0, + -0.310918, + 0.339375, + -0.887781, + 0.0, + 0.99836, + 0.0466305, + -0.0331999, + 0.0, + -0.0439099, + 0.304806, + 0.951402, + 0.0, + -0.676304, + -0.440938, + 0.590073, + 0.0, + 0.339805, + -0.328495, + 0.881263, + 0.0, + -0.0625568, + 0.916832, + 0.394342, + 0.0, + 0.776463, + -0.630153, + 0.00360388, + 0.0, + -0.224717, + -0.8758, + 0.427172, + 0.0, + 0.618879, + -0.70266, + -0.351081, + 0.0, + -0.380313, + 0.101503, + -0.919271, + 0.0, + 0.149639, + -0.957418, + 0.246897, + 0.0, + 0.128024, + 0.948139, + 0.290932, + 0.0, + -0.292448, + 0.893976, + -0.339532, + 0.0, + -0.192062, + -0.972477, + -0.131909, + 0.0, + 0.44007, + -0.870905, + 0.218776, + 0.0, + 0.303887, + -0.659003, + -0.688017, + 0.0, + 0.195552, + 0.41876, + -0.886792, + 0.0, + -0.889922, + 0.454236, + -0.0413315, + 0.0, + 0.515034, + 0.225353, + -0.827016, + 0.0, + 0.63084, + -0.573408, + -0.522728, + 0.0, + -0.745779, + 0.549592, + -0.376514, + 0.0, + 0.0711763, + -0.979204, + 0.189982, + 0.0, + 0.705657, + 0.707887, + 0.0307322, + 0.0, + 0.114603, + 0.655735, + -0.746242, + 0.0, + -0.0739232, + -0.0135353, + 0.997172, + 0.0, + 0.173356, + -0.20818, + 0.962605, + 0.0, + 0.34008, + -0.787344, + 0.514232, + 0.0, + -0.143596, + 0.334295, + -0.931465, + 0.0, + 0.721989, + -0.30942, + -0.618863, + 0.0, + -0.827657, + 0.0410685, + 0.559729, + 0.0, + -0.804277, + -0.418454, + 0.421942, + 0.0, + -0.379459, + 0.792556, + 0.477353, + 0.0, + 0.0391537, + 0.0756503, + 0.996365, + 0.0, + 0.821943, + 0.237588, + 0.517651, + 0.0, + -0.788974, + 0.463584, + -0.403249, + 0.0, + 0.175972, + 0.984364, + -0.00782073, + 0.0, + 0.891497, + 0.399363, + 0.213873, + 0.0, + -0.819111, + 0.106216, + 0.563716, + 0.0, + 0.105511, + 0.544028, + -0.832406, + 0.0, + -0.464551, + 0.63753, + 0.614612, + 0.0, + 0.232387, + 0.935154, + -0.267363, + 0.0, + 0.777619, + 0.272068, + -0.566823, + 0.0, + 0.975331, + 0.190338, + 0.111807, + 0.0, + 0.224313, + 0.450072, + -0.86436, + 0.0, + 0.841897, + -0.536898, + 0.0543103, + 0.0, + 0.637123, + -0.664145, + -0.391135, + 0.0, + 0.901675, + -0.422984, + 0.0898189, + 0.0, + -0.496241, + 0.367413, + -0.786608, + 0.0, + -0.255468, + -0.689763, + -0.677469, + 0.0, + -0.0616459, + -0.951141, + -0.302539, + 0.0, + -0.431011, + -0.889035, + -0.154425, + 0.0, + -0.0711688, + 0.486502, + -0.870776, + 0.0, + -0.223359, + -0.36162, + 0.905175, + 0.0, + -0.678546, + 0.695482, + -0.23639, + 0.0, + 0.576553, + 0.77934, + 0.245389, + 0.0, + -0.194568, + -0.24951, + 0.948624, + 0.0, + 0.28962, + -0.447736, + 0.845962, + 0.0, + -0.0403821, + -0.871893, + 0.488028, + 0.0, + 0.790972, + -0.560788, + 0.244705, + 0.0, + -0.34553, + 0.739953, + 0.57713, + 0.0, + -0.516376, + -0.697122, + 0.49737, + 0.0, + 0.115998, + 0.859293, + 0.498156, + 0.0, + 0.643831, + -0.239955, + 0.72657, + 0.0, + -0.125114, + 0.987348, + -0.0974144, + 0.0, + -0.306452, + 0.610699, + -0.73016, + 0.0, + -0.269845, + 0.893027, + -0.360119, + 0.0, + 0.328563, + -0.570628, + -0.752615, + 0.0, + -0.306918, + -0.42057, + 0.853769, + 0.0, + 0.699245, + -0.51785, + 0.492837, + 0.0, + -0.558362, + -0.469763, + -0.68378, + 0.0, + 0.476563, + -0.841398, + 0.254826, + 0.0, + 0.0276172, + -0.623206, + 0.78157, + 0.0, + 0.587723, + -0.800313, + -0.118659, + 0.0, + 0.594035, + -0.740708, + 0.313806, + 0.0, + -0.340185, + -0.887929, + 0.309605, + 0.0, + 0.312245, + -0.246681, + -0.917416, + 0.0, + 0.194206, + 0.186398, + -0.963089, + 0.0, + 0.915704, + 0.329835, + -0.229553, + 0.0, + 0.94133, + 0.229917, + 0.247055, + 0.0, + -0.888253, + -0.144148, + 0.436152, + 0.0, + -0.906917, + -0.362625, + -0.214486, + 0.0, + 0.403108, + -0.908884, + 0.10693, + 0.0, + 0.983963, + 0.169256, + 0.056292, + 0.0, + -0.197949, + 0.888236, + 0.414553, + 0.0, + 0.0879741, + 0.247673, + 0.964841, + 0.0, + 0.474384, + -0.868071, + -0.146331, + 0.0, + 0.699884, + 0.541342, + -0.465953, + 0.0, + 0.610965, + 0.567249, + 0.552223, + 0.0, + 0.830508, + -0.285788, + -0.478103, + 0.0, + 0.328573, + -0.683076, + -0.652263, + 0.0, + -0.00537775, + 0.873381, + 0.487009, + 0.0, + -0.51289, + 0.828835, + 0.223557, + 0.0, + -0.871168, + -0.15102, + 0.467182, + 0.0, + -0.545561, + 0.390016, + -0.741789, + 0.0, + 0.874063, + 0.259258, + 0.410852, + 0.0, + -0.781555, + 0.612184, + -0.120005, + 0.0, + -0.284928, + 0.708938, + -0.645154, + 0.0, + -0.568809, + 0.0883274, + 0.817713, + 0.0, + -0.0429388, + 0.549957, + -0.834088, + 0.0, + 0.933296, + -0.127233, + 0.335813, + 0.0, + 0.698149, + -0.493464, + 0.51873, + 0.0, + -0.603413, + 0.617495, + -0.504572, + 0.0, +]; + +#[inline(always)] +pub fn lerp(a: f32, b: f32, t: f32) -> f32 { a + (b - a) * t } + +#[inline(always)] +pub fn fade(t: f32) -> f32 { (t * t * t) * (t * (t * 6.0 - 15.0) + 10.0) } + +#[inline(always)] +pub fn grad3(hash: u32, x: f32, y: f32, z: f32) -> f32 { + let h = hash & 127; + x * G3[h as usize * 4] + y * G3[h as usize * 4 + 1] + z * G3[h as usize * 4 + 2] +} + +pub fn noise(pos: [f32; 3]) -> f32 { + let [mut x, mut y, mut z] = pos; + let fx = x.floor(); + let fy = y.floor(); + let fz = z.floor(); + let ix = fx as u32 & 255; + let iy = fy as u32 & 255; + let iz = fz as u32 & 255; + x -= fx; + y -= fy; + z -= fz; + let u = fade(x); + let v = fade(y); + let w = fade(z); + + let p00 = PERMUTATIONS[ix as usize] + iy; + let p000 = PERMUTATIONS[p00 as usize] + iz; + let p010 = PERMUTATIONS[p00 as usize + 1] + iz; + let p001 = p000 + 1; + let p011 = p010 + 1; + let p10 = PERMUTATIONS[ix as usize + 1] + iy; + let p100 = PERMUTATIONS[p10 as usize] + iz; + let p110 = PERMUTATIONS[p10 as usize + 1] + iz; + let p101 = p100 + 1; + let p111 = p110 + 1; + + let g000 = grad3(PERMUTATIONS[p000 as usize], x, y, z); + let g100 = grad3(PERMUTATIONS[p100 as usize], x - 1.0, y, z); + let g010 = grad3(PERMUTATIONS[p010 as usize], x, y - 1.0, z); + let g110 = grad3(PERMUTATIONS[p110 as usize], x - 1.0, y - 1.0, z); + let g001 = grad3(PERMUTATIONS[p001 as usize], x, y, z - 1.0); + let g101 = grad3(PERMUTATIONS[p101 as usize], x - 1.0, y, z - 1.0); + let g011 = grad3(PERMUTATIONS[p011 as usize], x, y - 1.0, z - 1.0); + let g111 = grad3(PERMUTATIONS[p111 as usize], x - 1.0, y - 1.0, z - 1.0); + + lerp( + lerp(lerp(g000, g100, u), lerp(g010, g110, u), v), + lerp(lerp(g001, g101, u), lerp(g011, g111, u), v), + w, + ) +} diff --git a/examples/support/src/display.rs b/examples/support/src/display.rs index d46a6e8dd..1485fc819 100644 --- a/examples/support/src/display.rs +++ b/examples/support/src/display.rs @@ -1,16 +1,22 @@ use core::num::NonZeroU32; -use std::borrow::Cow; +use std::{arch::x86_64::_rdtsc, borrow::Cow, fmt::Debug}; +use crate::{rgba_to_u32, Camera, DebugState, ShadingMode, TiledImage, TILE_SIZE_X, TILE_SIZE_Y}; use arcball::ArcballCamera; -use cgmath::{Matrix4, SquareMatrix, Vector2, Vector3, Vector4}; +use cgmath::{InnerSpace, Vector2, Vector3}; use clock_ticks; +use egui_wgpu::renderer::ScreenDescriptor; +use embree::{IntersectContext, Ray, RayHit, RayHitNp, RayNp}; use futures; -use image::RgbaImage; +use rayon::iter::ParallelIterator; use wgpu; use winit::{ dpi::{LogicalSize, Size}, - event::{Event, VirtualKeyCode, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, + event::{ + ElementState, Event, KeyboardInput, MouseButton, MouseScrollDelta, VirtualKeyCode, + WindowEvent, + }, + event_loop::{ControlFlow, EventLoop, EventLoopBuilder}, window::{Window, WindowBuilder}, }; @@ -20,11 +26,11 @@ type float4 = vec4; type int2 = vec2; struct VertexInput { - [[builtin(vertex_index)]] index: u32; + @builtin(vertex_index) index: u32, }; struct VertexOutput { - [[builtin(position)]] position: float4; + @builtin(position) position: float4, }; var coords: array = array( @@ -34,18 +40,18 @@ var coords: array = array( float2(1.0, 1.0) ); -[[stage(vertex)]] +@vertex fn vertex_main(vert: VertexInput) -> VertexOutput { var out: VertexOutput; out.position = float4(coords[vert.index], 0.0, 1.0); return out; }; -[[group(0), binding(0)]] +@group(0) @binding(0) var image: texture_2d; -[[stage(fragment)]] -fn fragment_main(in: VertexOutput) -> [[location(0)]] float4 { +@fragment +fn fragment_main(in: VertexOutput) -> @location(0) float4 { return textureLoad(image, int2(in.position.xy), 0); } "; @@ -54,8 +60,10 @@ fn fragment_main(in: VertexOutput) -> [[location(0)]] float4 { pub struct Display { window: Window, event_loop: EventLoop<()>, + #[allow(dead_code)] instance: wgpu::Instance, surface: wgpu::Surface, + #[allow(dead_code)] adapter: wgpu::Adapter, device: wgpu::Device, queue: wgpu::Queue, @@ -75,7 +83,7 @@ impl CameraPose { impl Display { pub fn new(w: u32, h: u32, title: &str) -> Display { - let event_loop = EventLoop::new(); + let event_loop = EventLoopBuilder::<()>::new().build(); let win_size = Size::Logical(LogicalSize::new(w as f64, h as f64)); let window = WindowBuilder::new() .with_inner_size(win_size) @@ -83,8 +91,12 @@ impl Display { .build(&event_loop) .unwrap(); - let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY); - let surface = unsafe { instance.create_surface(&window) }; + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: wgpu::Backends::PRIMARY, + dx12_shader_compiler: Default::default(), + }); + let surface = + unsafe { instance.create_surface(&window) }.expect("Failed to create surface"); let adapter = futures::executor::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::default(), @@ -104,32 +116,48 @@ impl Display { .expect("Failed to create device"); Display { - window: window, - event_loop: event_loop, - instance: instance, - surface: surface, - adapter: adapter, - device: device, - queue: queue, + window, + event_loop, + instance, + surface, + adapter, + device, + queue, } } } -/// The function passed should render and update the image to be displayed in the window, -/// optionally using the camera pose information passed. -pub fn run(display: Display, mut render: F) -where - F: 'static + FnMut(&mut RgbaImage, CameraPose, f32), + +/// The function passed should render and update the image to be displayed in +/// the window, optionally using the camera pose information passed. +pub fn run( + display: Display, + mut state: DebugState, + mut update: G, + mut render: F, + run_ui: U, +) where + F: FnMut(&mut TiledImage, &Camera, f32, &mut DebugState) + 'static, + G: FnMut(f32, &mut DebugState) + 'static, + U: FnOnce(&egui::Context) + Copy + 'static, + T: Sized + Send + Sync + 'static, { - let window_size = display.window.inner_size(); - let mut embree_target = RgbaImage::new(window_size.width, window_size.height); + let mut window_size = display.window.inner_size(); + let mut image_buf: Vec = vec![0u8; (window_size.width * window_size.height * 4) as usize]; + + let mut embree_target = TiledImage::new( + window_size.width, + window_size.height, + TILE_SIZE_X, + TILE_SIZE_Y, + ); - let mut arcball_camera = ArcballCamera::new( + let mut arcball = ArcballCamera::new( Vector3::new(0.0, 0.0, 0.0), 1.0, [window_size.width as f32, window_size.height as f32], ); - arcball_camera.zoom(-50.0, 0.16); - arcball_camera.rotate( + arcball.zoom(-30.0, 0.16); + arcball.rotate( Vector2::new( window_size.width as f32 / 2.0, window_size.height as f32 / 4.0, @@ -143,13 +171,13 @@ where // Porting in my wgpu-rs example just to test set up let vertex_module = display .device - .create_shader_module(&wgpu::ShaderModuleDescriptor { + .create_shader_module(wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(WGSL_SHADERS)), }); let fragment_module = display .device - .create_shader_module(&wgpu::ShaderModuleDescriptor { + .create_shader_module(wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(WGSL_SHADERS)), }); @@ -170,12 +198,13 @@ where } index_buffer.unmap(); - let window_extent = wgpu::Extent3d { + let mut window_extent = wgpu::Extent3d { width: window_size.width, height: window_size.height, depth_or_array_layers: 1, }; - let embree_texture = display.device.create_texture(&wgpu::TextureDescriptor { + + let mut embree_texture = display.device.create_texture(&wgpu::TextureDescriptor { label: None, size: window_extent, mip_level_count: 1, @@ -183,6 +212,7 @@ where dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8Unorm, usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[], }); let bindgroup_layout = @@ -202,7 +232,7 @@ where }], }); - let bindgroup = display + let mut bind_group = display .device .create_bind_group(&wgpu::BindGroupDescriptor { label: None, @@ -241,7 +271,9 @@ where format: swap_chain_format, width: window_size.width, height: window_size.height, - present_mode: wgpu::PresentMode::Fifo, + present_mode: wgpu::PresentMode::AutoNoVsync, + alpha_mode: Default::default(), + view_formats: vec![], }, ); @@ -277,11 +309,11 @@ where fragment: Some(wgpu::FragmentState { module: &fragment_module, entry_point: "fragment_main", - targets: &[wgpu::ColorTargetState { + targets: &[Some(wgpu::ColorTargetState { format: swap_chain_format, blend: None, write_mask: wgpu::ColorWrites::ALL, - }], + })], }), multiview: None, }); @@ -293,47 +325,259 @@ where a: 1.0, }; - let mut mouse_pressed = [false, false]; - //let mut prev_mouse = None; + let egui_ctx = egui::Context::default(); + let mut egui_state = egui_winit::State::new(&display.event_loop); + let mut egui_renderer = egui_wgpu::Renderer::new(&display.device, swap_chain_format, None, 1); + + let mut screen_desc = ScreenDescriptor { + size_in_pixels: window_size.into(), + pixels_per_point: display.window.scale_factor() as f32, + }; + + let mut shading_mode = ShadingMode::Default; + let mut fps = 0.0f64; + let mut mouse_prev = Vector2::new(0.0, 0.0); + let mut mouse_pressed = [false, false, false]; let t_start = clock_ticks::precise_time_s(); + let mut last_frame_time = t_start; + display.event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Poll; - match event { - Event::WindowEvent { event, .. } => match event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::KeyboardInput { input, .. } - if input.virtual_keycode == Some(VirtualKeyCode::Escape) => - { - *control_flow = ControlFlow::Exit + Event::WindowEvent { event, .. } => { + if !egui_state.on_event(&egui_ctx, &event).consumed { + match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::KeyboardInput { input, .. } => match input { + KeyboardInput { + virtual_keycode: Some(VirtualKeyCode::Escape), + .. + } => *control_flow = ControlFlow::Exit, + _ => {} + }, + WindowEvent::MouseInput { state, button, .. } => match button { + MouseButton::Left => mouse_pressed[0] = state == ElementState::Pressed, + MouseButton::Middle => { + mouse_pressed[1] = state == ElementState::Pressed + } + MouseButton::Right => mouse_pressed[2] = state == ElementState::Pressed, + MouseButton::Other(_) => {} + }, + WindowEvent::CursorMoved { position, .. } => { + let mouse_cur = Vector2::new(position.x as f32, position.y as f32); + if mouse_pressed[0] { + arcball.rotate(mouse_prev, mouse_cur); + } + if mouse_pressed[2] { + arcball.pan(mouse_cur - mouse_prev); + } + mouse_prev = mouse_cur; + } + WindowEvent::MouseWheel { delta, .. } => match delta { + MouseScrollDelta::LineDelta(_, y) => { + arcball.zoom(y, 0.1); + } + MouseScrollDelta::PixelDelta(pos) => { + arcball.zoom(pos.y as f32, 0.01); + } + }, + WindowEvent::Resized(size) + | WindowEvent::ScaleFactorChanged { + new_inner_size: &mut size, + .. + } => { + if size.width > 0 && size.height > 0 { + if size.width != window_size.width + || size.height != window_size.height + { + window_size = size; + + // update arcball + arcball.update_screen( + window_size.width as f32, + window_size.height as f32, + ); + + // update swapchain + display.surface.configure( + &display.device, + &wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format: swap_chain_format, + width: window_size.width, + height: window_size.height, + present_mode: wgpu::PresentMode::AutoNoVsync, + alpha_mode: Default::default(), + view_formats: vec![], + }, + ); + + image_buf.resize( + (window_size.width * window_size.height * 4) as usize, + 0, + ); + + // update embree target + embree_target = TiledImage::new( + window_size.width as u32, + window_size.height as u32, + TILE_SIZE_X, + TILE_SIZE_Y, + ); + window_extent = wgpu::Extent3d { + width: window_size.width, + height: window_size.height, + depth_or_array_layers: 1, + }; + // recreate embree texture + embree_texture = + display.device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: window_extent, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8Unorm, + usage: wgpu::TextureUsages::COPY_DST + | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }); + // update screen size for egui + screen_desc.size_in_pixels = window_size.into(); + screen_desc.pixels_per_point = + display.window.scale_factor() as f32; + + bind_group = display.device.create_bind_group( + &wgpu::BindGroupDescriptor { + label: None, + layout: &bindgroup_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView( + &embree_texture.create_view( + &wgpu::TextureViewDescriptor { + label: None, + format: Some( + wgpu::TextureFormat::Rgba8Unorm, + ), + dimension: Some( + wgpu::TextureViewDimension::D2, + ), + aspect: wgpu::TextureAspect::All, + base_mip_level: 0, + mip_level_count: None, + base_array_layer: 0, + array_layer_count: None, + }, + ), + ), + }], + }, + ); + } + } + } + _ => (), + } } - _ => (), - }, + } Event::MainEventsCleared => { - let cam_pose = CameraPose::new( - arcball_camera.eye_pos(), - arcball_camera.eye_dir(), - arcball_camera.up_dir(), - ); - render( - &mut embree_target, - cam_pose, - (clock_ticks::precise_time_s() - t_start) as f32, + let egui_input = egui_state.take_egui_input(&display.window); + + let cam_pose = + CameraPose::new(arcball.eye_pos(), arcball.eye_dir(), arcball.up_dir()); + + let camera = Camera::look_dir( + cam_pose.pos, + cam_pose.dir, + cam_pose.up, + 75.0, + (window_size.width, window_size.height), ); - let frame = display - .surface - .get_current_texture() - .expect("Failed to get surface output texture"); - let render_target_view = frame - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); + let time = (clock_ticks::precise_time_s() - t_start) as f32; + + update(time, &mut state); + + // render embree target + embree_target.reset_pixels(); + match shading_mode { + ShadingMode::Default => { + render( + &mut embree_target, + &camera, + (clock_ticks::precise_time_s() - t_start) as f32, + &mut state, + ); + } + ShadingMode::EyeLight => { + render_frame_eye_light( + &mut embree_target, + (clock_ticks::precise_time_s() - t_start) as f32, + &camera, + &state, + ); + } + ShadingMode::Occlusion => {} + ShadingMode::UV => { + render_frame_pixel_uv( + &mut embree_target, + (clock_ticks::precise_time_s() - t_start) as f32, + &camera, + &state, + ); + } + ShadingMode::Normal => { + render_frame_pixel_normal( + &mut embree_target, + (clock_ticks::precise_time_s() - t_start) as f32, + &camera, + &state, + ); + } + ShadingMode::CPUCycles => { + render_frame_pixel_cpu_cycles( + &mut embree_target, + (clock_ticks::precise_time_s() - t_start) as f32, + &camera, + &state, + ); + } + ShadingMode::GeometryID => { + render_frame_pixel_geometry_id( + &mut embree_target, + (clock_ticks::precise_time_s() - t_start) as f32, + &camera, + &state, + ); + } + ShadingMode::GeometryPrimitiveID => { + render_frame_pixel_geometry_primitive_id( + &mut embree_target, + (clock_ticks::precise_time_s() - t_start) as f32, + &camera, + &state, + ); + } + // TODO(yang): implement + ShadingMode::AmbientOcclusion + | ShadingMode::TexCoords + | ShadingMode::TexCoordsGrid => { + render( + &mut embree_target, + &camera, + (clock_ticks::precise_time_s() - t_start) as f32, + &mut state, + ); + } + } + embree_target.write_to_flat_buffer(&mut image_buf); // Just use queue write_texture even though it likely makes a temporary upload // buffer, because making the async map API work in here will be a mess. display.queue.write_texture( embree_texture.as_image_copy(), - &embree_target.as_raw()[..], + &image_buf, wgpu::ImageDataLayout { offset: 0, bytes_per_row: Some(NonZeroU32::new(window_size.width * 4).unwrap()), @@ -342,34 +586,407 @@ where window_extent, ); + // present embree target on screen + let frame = display + .surface + .get_current_texture() + .expect("Failed to get surface output texture"); + let render_target_view = frame + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + let mut encoder = display .device .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: None, - color_attachments: &[wgpu::RenderPassColorAttachment { + color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: &render_target_view, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(clear_color), store: true, }, - }], + })], depth_stencil_attachment: None, }); render_pass.set_pipeline(&render_pipeline); - render_pass.set_bind_group(0, &bindgroup, &[]); + render_pass.set_bind_group(0, &bind_group, &[]); // Note: also bug in wgpu-rs set_index_buffer or web sys not passing // the right index type render_pass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint16); render_pass.draw_indexed(0..4, 0, 0..1); } - display.queue.submit(Some(encoder.finish())); + + let mut ui_encoder = + display + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("egui_encoder"), + }); + { + let egui_output = egui_ctx.run(egui_input, |ctx: &egui::Context| { + run_ui(ctx); + egui::Window::new("fps") + .title_bar(false) + .min_width(200.0) + .show(ctx, |ui| { + ui.vertical(|ui| { + ui.horizontal_wrapped(|ui| { + ui.label("FPS: "); + ui.label(format!("{:3}", fps.floor())); + }); + + ui.horizontal_wrapped(|ui| { + ui.label("Shading: "); + egui::ComboBox::from_label("") + .selected_text(format!("{:?}", shading_mode)) + .show_ui(ui, |ui| { + ui.selectable_value( + &mut shading_mode, + ShadingMode::Default, + "Default", + ); + ui.selectable_value( + &mut shading_mode, + ShadingMode::EyeLight, + "EyeLight", + ); + ui.selectable_value( + &mut shading_mode, + ShadingMode::Normal, + "Normal", + ); + ui.selectable_value( + &mut shading_mode, + ShadingMode::CPUCycles, + "CpuCycles", + ); + ui.selectable_value( + &mut shading_mode, + ShadingMode::GeometryID, + "GeomID", + ); + ui.selectable_value( + &mut shading_mode, + ShadingMode::GeometryPrimitiveID, + "GeomPrimID", + ); + ui.selectable_value( + &mut shading_mode, + ShadingMode::UV, + "Uv", + ); + ui.selectable_value( + &mut shading_mode, + ShadingMode::Occlusion, + "Occlusion", + ); + ui.selectable_value( + &mut shading_mode, + ShadingMode::TexCoords, + "TexCoords", + ); + ui.selectable_value( + &mut shading_mode, + ShadingMode::TexCoordsGrid, + "TexCoordsGrid", + ); + ui.selectable_value( + &mut shading_mode, + ShadingMode::AmbientOcclusion, + "AmbientOcclusion", + ); + }); + }); + }); + }); + }); + egui_state.handle_platform_output( + &display.window, + &egui_ctx, + egui_output.platform_output, + ); + let primitives = egui_ctx.tessellate(egui_output.shapes); + let _user_cmds = { + for (id, image_delta) in &egui_output.textures_delta.set { + egui_renderer.update_texture( + &display.device, + &display.queue, + *id, + image_delta, + ); + } + egui_renderer.update_buffers( + &display.device, + &display.queue, + &mut ui_encoder, + &primitives, + &screen_desc, + ) + }; + { + let mut render_pass = + ui_encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("egui_render_pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &render_target_view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: true, + }, + })], + depth_stencil_attachment: None, + }); + + egui_renderer.render(&mut render_pass, &primitives, &screen_desc); + } + + for id in &egui_output.textures_delta.free { + egui_renderer.free_texture(id); + } + } + + display + .queue + .submit([encoder.finish(), ui_encoder.finish()]); frame.present(); + + let elapsed = clock_ticks::precise_time_s() - last_frame_time; + last_frame_time = clock_ticks::precise_time_s(); + fps = 1.0 / elapsed; } _ => (), } }); } + +fn render_frame_eye_light( + frame: &mut TiledImage, + _time: f32, + camera: &Camera, + state: &DebugState, +) { + frame.par_tiles_mut().for_each(|tile| { + let tile_size = (tile.w * tile.h) as usize; + let mut ray_hits = RayHitNp::new(RayNp::new(tile_size)); + for (i, mut ray) in ray_hits.ray.iter_mut().enumerate() { + let x = tile.x + (i % tile.w as usize) as u32; + let y = tile.y + (i / tile.w as usize) as u32; + ray.set_origin(camera.pos.into()); + ray.set_dir(camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)).into()); + ray.set_tnear(0.0); + ray.set_tfar(f32::INFINITY); + } + let mut ctx = IntersectContext::coherent(); + state.scene.intersect_stream_soa(&mut ctx, &mut ray_hits); + + for (i, (ray, hit)) in ray_hits.iter().enumerate() { + if hit.is_valid() { + let dot = Vector3::from(hit.unit_normal()).dot(Vector3::from(ray.unit_dir())); + if dot < 0.0 { + tile.pixels[i] = rgba_to_u32(0, (dot.abs() * 255.0) as u8, 0, 255); + } else { + tile.pixels[i] = rgba_to_u32((dot.abs() * 255.0) as u8, 0, 0, 255); + } + } else { + tile.pixels[i] = rgba_to_u32(0, 0, 0, 255); + } + } + }); +} + +fn render_frame_pixel_uv( + frame: &mut TiledImage, + _time: f32, + camera: &Camera, + state: &DebugState, +) { + frame.par_tiles_mut().for_each(|tile| { + let tile_size = (tile.w * tile.h) as usize; + let mut ray_hits = RayHitNp::new(RayNp::new(tile_size)); + for (i, mut ray) in ray_hits.ray.iter_mut().enumerate() { + let x = tile.x + (i % tile.w as usize) as u32; + let y = tile.y + (i / tile.w as usize) as u32; + ray.set_origin(camera.pos.into()); + ray.set_dir(camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)).into()); + ray.set_tnear(0.0); + ray.set_tfar(f32::INFINITY); + } + let mut ctx = IntersectContext::coherent(); + state.scene.intersect_stream_soa(&mut ctx, &mut ray_hits); + + for (i, (_, hit)) in ray_hits.iter().enumerate() { + if hit.is_valid() { + let [u, v] = hit.uv(); + tile.pixels[i] = rgba_to_u32( + (u * 255.0) as u8, + (v * 255.0) as u8, + ((1.0 - u - v) * 255.0) as u8, + 255, + ); + } else { + tile.pixels[i] = rgba_to_u32(0, 0, 255, 255); + } + } + }); +} + +fn render_frame_pixel_normal( + frame: &mut TiledImage, + _time: f32, + camera: &Camera, + state: &DebugState, +) { + frame.par_tiles_mut().for_each(|tile| { + let tile_size = (tile.w * tile.h) as usize; + let mut ray_hits = RayHitNp::new(RayNp::new(tile_size)); + for (i, mut ray) in ray_hits.ray.iter_mut().enumerate() { + let x = tile.x + (i % tile.w as usize) as u32; + let y = tile.y + (i / tile.w as usize) as u32; + ray.set_origin(camera.pos.into()); + ray.set_dir(camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)).into()); + ray.set_tnear(0.0); + ray.set_tfar(f32::INFINITY); + } + let mut ctx = IntersectContext::coherent(); + state.scene.intersect_stream_soa(&mut ctx, &mut ray_hits); + + for (i, (_, hit)) in ray_hits.iter().enumerate() { + if hit.is_valid() { + let [nx, ny, nz] = hit.unit_normal(); + tile.pixels[i] = rgba_to_u32( + (nx.abs() * 255.0) as u8, + (ny.abs() * 255.0) as u8, + (nz.abs() * 255.0) as u8, + 255, + ); + } else { + tile.pixels[i] = rgba_to_u32(0, 0, 255, 255); + } + } + }); +} + +fn render_frame_pixel_geometry_id( + frame: &mut TiledImage, + _time: f32, + camera: &Camera, + state: &DebugState, +) { + frame.par_tiles_mut().for_each(|tile| { + let tile_size = (tile.w * tile.h) as usize; + let mut ray_hits = RayHitNp::new(RayNp::new(tile_size)); + for (i, mut ray) in ray_hits.ray.iter_mut().enumerate() { + let x = tile.x + (i % tile.w as usize) as u32; + let y = tile.y + (i / tile.w as usize) as u32; + ray.set_origin(camera.pos.into()); + ray.set_dir(camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)).into()); + ray.set_tnear(0.0); + ray.set_tfar(f32::INFINITY); + } + let mut ctx = IntersectContext::coherent(); + state.scene.intersect_stream_soa(&mut ctx, &mut ray_hits); + + for (i, (_, hit)) in ray_hits.iter().enumerate() { + if hit.is_valid() { + let geom_id = hit.geom_id(); + let [r, g, b] = random_color(geom_id); + tile.pixels[i] = rgba_to_u32(r, g, b, 255); + } else { + tile.pixels[i] = rgba_to_u32(0, 0, 0, 255); + } + } + }); +} + +fn random_color(id: u32) -> [u8; 3] { + [ + (((id + 13) * 17 * 23) & 255) as u8, + (((id + 15) * 11 * 13) & 255) as u8, + (((id + 17) * 7 * 19) & 255) as u8, + ] +} + +fn random_color_f32(id: u32) -> [f32; 3] { + let one_over_255 = 1.0 / 255.0; + [ + (((id + 13) * 17 * 23) & 255) as f32 * one_over_255, + (((id + 15) * 11 * 13) & 255) as f32 * one_over_255, + (((id + 17) * 7 * 19) & 255) as f32 * one_over_255, + ] +} + +fn render_frame_pixel_cpu_cycles( + frame: &mut TiledImage, + _time: f32, + camera: &Camera, + state: &DebugState, +) { + frame.par_tiles_mut().for_each(|tile| { + for (i, pixel) in tile.pixels.iter_mut().enumerate() { + let x = tile.x + (i % tile.w as usize) as u32; + let y = tile.y + (i / tile.w as usize) as u32; + + let mut ray_hit = RayHit::from_ray(Ray::segment( + camera.pos.into(), + camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)).into(), + 0.0, + f32::INFINITY, + )); + + let c0 = unsafe { _rdtsc() }; + let mut ctx = IntersectContext::coherent(); + state.scene.intersect(&mut ctx, &mut ray_hit); + let c1 = unsafe { _rdtsc() }; + *pixel = rgba_to_u32( + ((c1 - c0) & 255) as u8, + ((c1 - c0) >> 8 & 255) as u8, + ((c1 - c0) >> 16 & 255) as u8, + 255, + ); + } + }); +} + +fn render_frame_pixel_geometry_primitive_id( + frame: &mut TiledImage, + _time: f32, + camera: &Camera, + state: &DebugState, +) { + frame.par_tiles_mut().for_each(|tile| { + let tile_size = (tile.w * tile.h) as usize; + let mut ray_hits = RayHitNp::new(RayNp::new(tile_size)); + for (i, mut ray) in ray_hits.ray.iter_mut().enumerate() { + let x = tile.x + (i % tile.w as usize) as u32; + let y = tile.y + (i / tile.w as usize) as u32; + ray.set_origin(camera.pos.into()); + ray.set_dir(camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)).into()); + ray.set_tnear(0.0); + ray.set_tfar(f32::INFINITY); + } + let mut ctx = IntersectContext::coherent(); + state.scene.intersect_stream_soa(&mut ctx, &mut ray_hits); + + for (i, (ray, hit)) in ray_hits.iter().enumerate() { + if hit.is_valid() { + let geom_id = hit.geom_id(); + let prim_id = hit.prim_id(); + let [r, g, b] = random_color_f32(geom_id ^ prim_id); + let dot = (Vector3::from(hit.unit_normal()).dot(Vector3::from(ray.dir()))).abs(); + tile.pixels[i] = rgba_to_u32( + (r * dot * 255.0) as u8, + (g * dot * 255.0) as u8, + (b * dot * 255.0) as u8, + 255, + ); + } else { + tile.pixels[i] = rgba_to_u32(0, 0, 0, 255); + } + } + }); +} diff --git a/examples/support/src/lib.rs b/examples/support/src/lib.rs index 8bfc54c4a..d065159f7 100644 --- a/examples/support/src/lib.rs +++ b/examples/support/src/lib.rs @@ -1,21 +1,316 @@ -extern crate arcball; -extern crate cgmath; -extern crate clock_ticks; -extern crate futures; -extern crate image; - -type Mat4 = cgmath::Matrix4; -type CgPoint = cgmath::Point3; -type CgVec = cgmath::Vector3; -type Vector2 = cgmath::Vector2; -type Vector3 = cgmath::Vector3; -type Vector4 = cgmath::Vector4; - pub mod camera; +mod common; pub mod display; +pub use common::*; + pub use camera::Camera; pub use display::Display; +pub use egui; + +use embree::Scene; +pub use image::{Rgba, RgbaImage}; +pub use rayon::{iter::*, prelude::*, slice::*, vec::*}; + +pub mod math { + pub use cgmath::*; +} + +/// The type of ray used for rendering with Embree. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Mode { + /// A single ray. + Normal, + /// A stream of rays. + Stream, +} + +/// Shading mode for the tutorial. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ShadingMode { + /// Default tutorial shader + Default, + /// EyeLight shading + EyeLight, + /// Occlusion shading; only traces occlusion rays + Occlusion, + /// UV debug shading + UV, + /// Texture coordinates debug shading + TexCoords, + /// Grid texture debug shading + TexCoordsGrid, + /// Visualisation of shading normals + Normal, + /// CPU cycles visualisation + CPUCycles, + /// Visualisation of geometry IDs + GeometryID, + /// Visualisation of geometry and primitive IDs + GeometryPrimitiveID, + /// Ambient occlusion shading + AmbientOcclusion, +} + +/// An image that is tiled into smaller tiles for parallel rendering. +/// +/// Tiles and pixels inside tiles are stored in a flat array in row-major order. +/// The pixel is encoded as a 4-byte RGBA value. +pub struct TiledImage { + pub width: u32, + pub height: u32, + pub tile_width: u32, + pub tile_height: u32, + pub tile_size: u32, + pub num_tiles_x: u32, + pub num_tiles_y: u32, + pub num_tiles: u32, + pub pixels: Vec, + /// Whether the image is being reinterpreted as a non-tiled image. + is_tiled: bool, +} + +impl TiledImage { + /// Create a new tiled image. + pub fn new(width: u32, height: u32, tile_width: u32, tile_height: u32) -> Self { + let num_tiles_x = (width + tile_width - 1) / tile_width; + let num_tiles_y = (height + tile_height - 1) / tile_height; + let tile_size = tile_width * tile_height; + let num_tiles = num_tiles_x * num_tiles_y; + Self { + width, + height, + tile_width, + tile_height, + tile_size, + num_tiles_x, + num_tiles_y, + num_tiles, + pixels: vec![0; (num_tiles * tile_size) as usize], + is_tiled: true, + } + } + + pub fn reinterpret_as_none_tiled(&mut self) { self.is_tiled = false; } + + pub fn reinterpret_as_tiled(&mut self) { self.is_tiled = true; } + + /// Write the tiled image to a flat image. + pub fn write_to_image(&self, image: &mut RgbaImage) { + if !self.is_tiled { + for i in 0..self.width { + for j in 0..self.height { + let pixel = self.pixels[(j * self.width + i) as usize]; + image.put_pixel(i, j, Rgba(pixel.to_le_bytes())); + } + } + } else { + for j in 0..self.height { + for i in 0..self.width { + let tile_x = i / self.tile_width; + let tile_y = j / self.tile_height; + let tile_index = tile_y * self.num_tiles_x + tile_x; + let tile_offset = (tile_index * self.tile_size) as usize; + let tile_i = i % self.tile_width; + let tile_j = j % self.tile_height; + let tile_pixel_index = + tile_offset + (tile_j * self.tile_width + tile_i) as usize; + let pixel = self.pixels[tile_pixel_index]; + image.put_pixel(i, j, Rgba(pixel.to_le_bytes())); + } + } + } + } + + /// Write the tiled image to a flat image buffer. + pub fn write_to_flat_buffer(&self, buffer: &mut [u8]) { + debug_assert!(buffer.len() >= (self.width * self.height * 4) as usize); + if !self.is_tiled { + unsafe { + buffer.as_mut_ptr().copy_from_nonoverlapping( + self.pixels.as_ptr() as *const u8, + (self.width * self.height * 4) as usize, + ); + } + } else { + for tile in self.tiles() { + let base_offset = (tile.y * self.width + tile.x) as usize * 4; + // Copy the tile pixels to the buffer per row. + for i in 0..self.tile_height { + let row_offset = self.width as usize * 4 * i as usize; + unsafe { + buffer + .as_mut_ptr() + .add(base_offset + row_offset) + .copy_from_nonoverlapping( + tile.pixels.as_ptr().add((i * self.tile_width) as usize) + as *const u8, + self.tile_width as usize * 4, + ); + } + } + } + } + } + + pub fn tile_mut(&mut self, index: usize) -> TileMut<'_> { + debug_assert!(self.is_tiled); + let idx = index as u32; + let x = (idx % self.num_tiles_x) * self.tile_width; + let y = (idx / self.num_tiles_x) * self.tile_height; + let offset = (idx * self.tile_size) as usize; + TileMut { + idx, + x, + y, + w: self.tile_width, + h: self.tile_height, + pixels: &mut self.pixels[offset..offset + self.tile_size as usize], + } + } + + pub fn tile(&self, index: usize) -> Tile<'_> { + debug_assert!(self.is_tiled); + let idx = index as u32; + let x = (idx % self.num_tiles_x) * self.tile_width; + let y = (idx / self.num_tiles_x) * self.tile_height; + let offset = (idx * self.tile_size) as usize; + Tile { + idx, + x, + y, + w: self.tile_width, + h: self.tile_height, + pixels: &self.pixels[offset..offset + self.tile_size as usize], + } + } + + pub fn tiles(&self) -> impl Iterator> { + debug_assert!(self.is_tiled); + self.pixels + .chunks(self.tile_size as usize) + .enumerate() + .map(|(i, pixels)| { + let idx = i as u32; + let x = (idx % self.num_tiles_x) * self.tile_width; + let y = (idx / self.num_tiles_x) * self.tile_height; + Tile { + idx, + x, + y, + w: self.tile_width, + h: self.tile_height, + pixels, + } + }) + } + + pub fn tiles_mut(&mut self) -> impl Iterator> { + debug_assert!(self.is_tiled); + self.pixels + .chunks_mut(self.tile_size as usize) + .enumerate() + .map(|(i, pixels)| { + let idx = i as u32; + let x = (idx % self.num_tiles_x) * self.tile_width; + let y = (idx / self.num_tiles_x) * self.tile_height; + TileMut { + idx, + x, + y, + w: self.tile_width, + h: self.tile_height, + pixels, + } + }) + } + + pub fn par_tiles(&self) -> impl IndexedParallelIterator> { + debug_assert!(self.is_tiled); + self.pixels + .par_chunks(self.tile_size as usize) + .enumerate() + .map(|(i, pixels)| { + let idx = i as u32; + let x = (idx % self.num_tiles_x) * self.tile_width; + let y = (idx / self.num_tiles_x) * self.tile_height; + Tile { + idx, + x, + y, + w: self.tile_width, + h: self.tile_height, + pixels, + } + }) + } + + /// Iterate over the tiles of the tiled image. + pub fn par_tiles_mut(&mut self) -> impl IndexedParallelIterator> { + debug_assert!(self.is_tiled); + self.pixels + .par_chunks_mut(self.tile_size as usize) + .enumerate() + .map(|(i, pixels)| { + let idx = i as u32; + let x = (idx % self.num_tiles_x) * self.tile_width; + let y = (idx / self.num_tiles_x) * self.tile_height; + TileMut { + idx, + x, + y, + w: self.tile_width, + h: self.tile_height, + pixels, + } + }) + } + + /// Reset the pixels of the tiled image. + pub fn reset_pixels(&mut self) { + unsafe { + std::ptr::write_bytes(self.pixels.as_mut_ptr(), 0, self.pixels.len()); + } + } +} + +/// A tile of the tiled image. +pub struct Tile<'a> { + /// The index of the tile in the tiled image. + pub idx: u32, + /// The x coordinate of the tile in the image. + pub x: u32, + /// The y coordinate of the tile in the image. + pub y: u32, + /// The width of the tile. + pub w: u32, + /// The height of the tile. + pub h: u32, + /// The pixels of the tile, in RGBA format. + pub pixels: &'a [u32], +} + +/// A mutable tile of the tiled image. +pub struct TileMut<'a> { + /// The index of the tile in the tiled image. + pub idx: u32, + /// The x coordinate of the tile in the image. + pub x: u32, + /// The y coordinate of the tile in the image. + pub y: u32, + /// The width of the tile. + pub w: u32, + /// The height of the tile. + pub h: u32, + /// The pixels of the tile, in RGBA format. + pub pixels: &'a mut [u32], +} + +/// Convert a RGBA color to a u32 in the format 0xAABBGGRR. +#[inline(always)] +pub const fn rgba_to_u32(r: u8, g: u8, b: u8, a: u8) -> u32 { + ((a as u32) << 24) | ((b as u32) << 16) | ((g as u32) << 8) | (r as u32) +} /// Clamp `x` to be between `min` and `max` pub fn clamp(x: T, min: T, max: T) -> T { @@ -27,3 +322,12 @@ pub fn clamp(x: T, min: T, max: T) -> T { x } } + +#[derive(Clone, Debug)] +pub struct DebugState { + pub scene: Scene<'static>, + pub user: T, +} + +unsafe impl Send for DebugState where T: Sized + Send + Sync {} +unsafe impl Sync for DebugState where T: Sized + Send + Sync {} diff --git a/examples/curve_geometry/Cargo.toml b/examples/todos/curve_geometry/Cargo.toml similarity index 100% rename from examples/curve_geometry/Cargo.toml rename to examples/todos/curve_geometry/Cargo.toml diff --git a/examples/curve_geometry/src/main.rs b/examples/todos/curve_geometry/src/main.rs similarity index 99% rename from examples/curve_geometry/src/main.rs rename to examples/todos/curve_geometry/src/main.rs index 1e5168dac..5e3558961 100644 --- a/examples/curve_geometry/src/main.rs +++ b/examples/todos/curve_geometry/src/main.rs @@ -186,7 +186,7 @@ fn main() { for i in 0..img_dims.0 { let dir = camera.ray_dir((i as f32 + 0.5, j as f32 + 0.5)); let ray = Ray::new(camera.pos, dir); - let mut ray_hit = RayHit::new(ray); + let mut ray_hit = RayHit::from_ray(ray); rtscene.intersect(&mut intersection_ctx, &mut ray_hit); if ray_hit.hit.hit() { let h = ray_hit.hit; diff --git a/examples/obj_ao_parallel/Cargo.toml b/examples/todos/obj_ao_parallel/Cargo.toml similarity index 100% rename from examples/obj_ao_parallel/Cargo.toml rename to examples/todos/obj_ao_parallel/Cargo.toml diff --git a/examples/obj_ao_parallel/src/main.rs b/examples/todos/obj_ao_parallel/src/main.rs similarity index 99% rename from examples/obj_ao_parallel/src/main.rs rename to examples/todos/obj_ao_parallel/src/main.rs index ab131fd87..9b2c9bc6e 100644 --- a/examples/obj_ao_parallel/src/main.rs +++ b/examples/todos/obj_ao_parallel/src/main.rs @@ -104,7 +104,7 @@ impl<'embree> AOIntegrator<'embree> { pub fn render(&self, i: u32, j: u32, u: Point2) -> f32 { let dir = self.camera.ray_dir((i as f32 + 0.5, j as f32 + 0.5)); let ray = Ray::new(self.camera.pos, dir); - let mut ray_hit = RayHit::new(ray); + let mut ray_hit = RayHit::from_ray(ray); let mut intersection_ctx = IntersectContext::coherent(); self.rtscene.intersect(&mut intersection_ctx, &mut ray_hit); @@ -169,7 +169,7 @@ impl<'embree> AOIntegrator<'embree> { // Launch a second ray from the intersection point let ray = Ray::new(p, dir); - let mut ray_hit = RayHit::new(ray); + let mut ray_hit = RayHit::from_ray(ray); ray_hit.ray.tnear = 0.00001; // Avoid self intersection let mut intersection_ctx = IntersectContext::incoherent(); self.rtscene.intersect(&mut intersection_ctx, &mut ray_hit); diff --git a/examples/obj_viewer/Cargo.toml b/examples/todos/obj_viewer/Cargo.toml similarity index 100% rename from examples/obj_viewer/Cargo.toml rename to examples/todos/obj_viewer/Cargo.toml diff --git a/examples/obj_viewer/src/main.rs b/examples/todos/obj_viewer/src/main.rs similarity index 98% rename from examples/obj_viewer/src/main.rs rename to examples/todos/obj_viewer/src/main.rs index 4e3f53c5b..38685bee3 100644 --- a/examples/obj_viewer/src/main.rs +++ b/examples/todos/obj_viewer/src/main.rs @@ -81,7 +81,7 @@ fn main() { for i in 0..img_dims.0 { let dir = camera.ray_dir((i as f32 + 0.5, j as f32 + 0.5)); let ray = Ray::new(camera.pos, dir); - let mut ray_hit = RayHit::new(ray); + let mut ray_hit = RayHit::from_ray(ray); rtscene.intersect(&mut intersection_ctx, &mut ray_hit); if ray_hit.hit.hit() { let p = image.get_pixel_mut(i, j); diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index 3e24f7889..8dbba1a16 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -1,69 +1,84 @@ #![allow(dead_code)] -extern crate cgmath; extern crate embree; extern crate support; -use cgmath::{Vector3, Vector4}; -use embree::{Device, Geometry, IntersectContext, RayHitN, RayN, Scene, TriangleMesh}; -use std::sync::Arc; +use embree::{BufferUsage, Device, Format, IntersectContext, RayHitNp, RayNp, TriangleMesh}; +use support::{rgba_to_u32, DebugState}; fn main() { let display = support::Display::new(512, 512, "triangle"); - let device = Device::new(); + let device = Device::new().unwrap(); + + device.set_error_function(|error, message| { + println!("Embree error {}: {}", error, message); + }); // Make a triangle - let mut triangle = TriangleMesh::unanimated(device.clone(), 1, 3); - { - // TODO: API ergonomics are also pretty rough here w/ all the Arc::get_mut etc - let tri_mut = Arc::get_mut(&mut triangle).unwrap(); - { - let mut verts = tri_mut.vertex_buffer.map(); - let mut tris = tri_mut.index_buffer.map(); - verts[0] = [-1.0, 0.0, 0.0, 0.0]; - verts[1] = [0.0, 1.0, 0.0, 0.0]; - verts[2] = [1.0, 0.0, 0.0, 0.0]; + let mut triangle = TriangleMesh::new(&device).unwrap(); + triangle + .set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, 3) + .unwrap() + .view_mut::<[f32; 4]>() + .unwrap() + .copy_from_slice(&[ + [-1.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0], + [1.0, 0.0, 0.0, 0.0], + ]); + triangle + .set_new_buffer(BufferUsage::INDEX, 0, Format::UINT3, 12, 1) + .unwrap() + .view_mut::<[u32; 3]>() + .unwrap() + .copy_from_slice(&[[0, 1, 2]]); + triangle.commit(); - tris[0] = [0, 1, 2]; - } + let mut scene = device.create_scene().unwrap(); + scene.attach_geometry(&triangle); + scene.commit(); - tri_mut.commit(); - } + let state = DebugState { scene, user: () }; - let mut scene = Scene::new(device.clone()); - { - let scene_mut = Arc::get_mut(&mut scene).unwrap(); - scene_mut.attach_geometry(triangle); - scene_mut.commit(); - } + support::display::run( + display, + state, + |_, _| {}, + move |image, _, _, state| { + let mut intersection_ctx = IntersectContext::coherent(); + image.reinterpret_as_none_tiled(); - support::display::run(display, move |image, _, _| { - let mut intersection_ctx = IntersectContext::coherent(); + let img_dims = (image.width, image.height); + // Render the scene + for j in 0..img_dims.1 { + let y = -(j as f32 + 0.5) / img_dims.1 as f32 + 0.5; - let img_dims = image.dimensions(); - // Render the scene - for j in 0..img_dims.1 { - let y = -(j as f32 + 0.5) / img_dims.1 as f32 + 0.5; + // Try out streams of scanlines across x + let mut rays = RayNp::new(img_dims.0 as usize); + for (i, mut ray) in rays.iter_mut().enumerate() { + let x = (i as f32 + 0.5) / img_dims.0 as f32 - 0.5; + let dir_len = f32::sqrt(x * x + y * y + 1.0); + ray.set_origin([0.0, 0.5, 2.0]); + ray.set_dir([x / dir_len, y / dir_len, -1.0 / dir_len]); + } - // Try out streams of scanlines across x - let mut rays = RayN::new(img_dims.0 as usize); - for (i, mut ray) in rays.iter_mut().enumerate() { - let x = (i as f32 + 0.5) / img_dims.0 as f32 - 0.5; - let dir_len = f32::sqrt(x * x + y * y + 1.0); - ray.set_origin([0.0, 0.5, 2.0]); - ray.set_dir([x / dir_len, y / dir_len, -1.0 / dir_len]); + let mut ray_hit = RayHitNp::new(rays); + state + .scene + .intersect_stream_soa(&mut intersection_ctx, &mut ray_hit); + for (i, hit) in ray_hit + .hit + .iter() + .enumerate() + .filter(|(_i, h)| h.is_valid()) + { + let pixel = &mut image.pixels[i + (j * img_dims.0) as usize]; + let uv = hit.uv(); + *pixel = rgba_to_u32((uv[0] * 255.0) as u8, (uv[1] * 255.0) as u8, 0, 255); + } } - - let mut ray_hit = RayHitN::new(rays); - scene.intersect_stream_soa(&mut intersection_ctx, &mut ray_hit); - for (i, hit) in ray_hit.hit.iter().enumerate().filter(|(_i, h)| h.hit()) { - let p = image.get_pixel_mut(i as u32, j); - let uv = hit.uv(); - p[0] = (uv.0 * 255.0) as u8; - p[1] = (uv.1 * 255.0) as u8; - p[2] = 0; - } - } - }); + }, + |_| {}, + ); } diff --git a/examples/triangle_geometry/Cargo.toml b/examples/triangle_geometry/Cargo.toml index 843abbdcd..a31dd594c 100644 --- a/examples/triangle_geometry/Cargo.toml +++ b/examples/triangle_geometry/Cargo.toml @@ -5,7 +5,9 @@ authors = ["Will Usher "] edition = "2021" [dependencies] -embree = { path = "../../" } +glam = "0.23.0" +embree = { path = "../.." } support = { path = "../support" } -cgmath = "0.18.0" +[profile.release] +debug = 1 \ No newline at end of file diff --git a/examples/triangle_geometry/src/main.rs b/examples/triangle_geometry/src/main.rs index 6ab0acad2..f3bb50cbe 100644 --- a/examples/triangle_geometry/src/main.rs +++ b/examples/triangle_geometry/src/main.rs @@ -1,133 +1,209 @@ #![allow(dead_code)] -extern crate cgmath; extern crate embree; extern crate support; -use cgmath::{Vector3, Vector4}; -use embree::{Device, Geometry, IntersectContext, QuadMesh, Ray, RayHit, Scene, TriangleMesh}; -use support::Camera; +use embree::{ + BufferSlice, BufferUsage, Device, Format, IntersectContext, QuadMesh, Ray, RayHit, + TriangleMesh, INVALID_ID, +}; +use glam::Vec3; +use support::*; -fn make_cube<'a>(device: &'a Device) -> Geometry<'a> { - let mut mesh = TriangleMesh::unanimated(device, 12, 8); +const DISPLAY_WIDTH: u32 = 512; +const DISPLAY_HEIGHT: u32 = 512; + +fn make_cube(device: &Device, vertex_colors: &[[f32; 3]]) -> TriangleMesh<'static> { + let mut mesh = TriangleMesh::new(device).unwrap(); { - let mut verts = mesh.vertex_buffer.map(); - let mut tris = mesh.index_buffer.map(); - - verts[0] = Vector4::new(-1.0, -1.0, -1.0, 0.0); - verts[1] = Vector4::new(-1.0, -1.0, 1.0, 0.0); - verts[2] = Vector4::new(-1.0, 1.0, -1.0, 0.0); - verts[3] = Vector4::new(-1.0, 1.0, 1.0, 0.0); - verts[4] = Vector4::new(1.0, -1.0, -1.0, 0.0); - verts[5] = Vector4::new(1.0, -1.0, 1.0, 0.0); - verts[6] = Vector4::new(1.0, 1.0, -1.0, 0.0); - verts[7] = Vector4::new(1.0, 1.0, 1.0, 0.0); - - // left side - tris[0] = Vector3::new(0, 2, 1); - tris[1] = Vector3::new(1, 2, 3); - - // right side - tris[2] = Vector3::new(4, 5, 6); - tris[3] = Vector3::new(5, 7, 6); - - // bottom side - tris[4] = Vector3::new(0, 1, 4); - tris[5] = Vector3::new(1, 5, 4); - - // top side - tris[6] = Vector3::new(2, 6, 3); - tris[7] = Vector3::new(3, 6, 7); - - // front side - tris[8] = Vector3::new(0, 4, 2); - tris[9] = Vector3::new(2, 4, 6); - - // back side - tris[10] = Vector3::new(1, 3, 5); - tris[11] = Vector3::new(3, 7, 5); + mesh.set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 12, 8) + .unwrap() + .view_mut::<[f32; 3]>() + .unwrap() + .copy_from_slice(&[ + [-1.0, -1.0, -1.0], + [-1.0, -1.0, 1.0], + [-1.0, 1.0, -1.0], + [-1.0, 1.0, 1.0], + [1.0, -1.0, -1.0], + [1.0, -1.0, 1.0], + [1.0, 1.0, -1.0], + [1.0, 1.0, 1.0], + ]); + mesh.set_new_buffer(BufferUsage::INDEX, 0, Format::UINT3, 12, 12) + .unwrap() + .view_mut::<[u32; 3]>() + .unwrap() + .copy_from_slice(&[ + // left side + [0, 1, 2], + [1, 3, 2], + // right side + [4, 6, 5], + [5, 6, 7], + // bottom side + [0, 4, 1], + [1, 4, 5], + // top side + [2, 3, 6], + [3, 7, 6], + // front side + [0, 2, 4], + [2, 6, 4], + // back side + [1, 5, 3], + [3, 5, 7], + ]); + + mesh.set_vertex_attribute_count(1); + mesh.set_buffer( + BufferUsage::VERTEX_ATTRIBUTE, + 0, + Format::FLOAT3, + BufferSlice::from_slice(vertex_colors, ..8), + 12, + 8, + ) + .unwrap(); //.expect("failed to set vertex attribute buffer"); } - let mut mesh = Geometry::Triangle(mesh); mesh.commit(); mesh } -fn make_ground_plane<'a>(device: &'a Device) -> Geometry<'a> { - let mut mesh = QuadMesh::unanimated(device, 1, 4); + +fn make_ground_plane(device: &Device) -> QuadMesh<'static> { + let mut mesh = QuadMesh::new(device).unwrap(); { - let mut verts = mesh.vertex_buffer.map(); - let mut quads = mesh.index_buffer.map(); - verts[0] = Vector4::new(-10.0, -2.0, -10.0, 0.0); - verts[1] = Vector4::new(-10.0, -2.0, 10.0, 0.0); - verts[2] = Vector4::new(10.0, -2.0, 10.0, 0.0); - verts[3] = Vector4::new(10.0, -2.0, -10.0, 0.0); - - quads[0] = Vector4::new(0, 1, 2, 3); + mesh.set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, 4) + .unwrap() + .view_mut::<[f32; 4]>() + .unwrap() + .copy_from_slice(&[ + [-10.0, -2.0, -10.0, 0.0], + [-10.0, -2.0, 10.0, 0.0], + [10.0, -2.0, 10.0, 0.0], + [10.0, -2.0, -10.0, 0.0], + ]); + mesh.set_new_buffer(BufferUsage::INDEX, 0, Format::UINT4, 16, 1) + .unwrap() + .view_mut::<[u32; 4]>() + .unwrap() + .copy_from_slice(&[[0, 1, 2, 3]]); } - let mut mesh = Geometry::Quad(mesh); mesh.commit(); mesh } +type State = DebugState; + +struct UserState { + ground_id: u32, + cube_id: u32, + face_colors: Vec<[f32; 3]>, + light_dir: Vec3, +} + fn main() { - let mut display = support::Display::new(512, 512, "triangle geometry"); - let device = Device::new(); - let cube = make_cube(&device); + let display = Display::new(DISPLAY_WIDTH, DISPLAY_HEIGHT, "triangle geometry"); + let device = Device::new().unwrap(); + device.set_error_function(|err, msg| { + println!("{}: {}", err, msg); + }); + let scene = device.create_scene().unwrap(); + let vertex_colors = vec![ + [0.0, 0.0, 0.0], + [0.0, 0.0, 1.0], + [0.0, 1.0, 0.0], + [0.0, 1.0, 1.0], + [1.0, 0.0, 0.0], + [1.0, 0.0, 1.0], + [1.0, 1.0, 0.0], + [1.0, 1.0, 1.0], + ]; + + let user_state = UserState { + face_colors: vec![ + [1.0, 0.0, 0.0], + [1.0, 0.0, 0.0], + [0.0, 1.0, 0.0], + [0.0, 1.0, 0.0], + [0.5, 0.5, 0.5], + [0.5, 0.5, 0.5], + [1.0, 1.0, 1.0], + [1.0, 1.0, 1.0], + [0.0, 0.0, 1.0], + [0.0, 0.0, 1.0], + [1.0, 1.0, 0.0], + [1.0, 1.0, 0.0], + ], + ground_id: INVALID_ID, + cube_id: INVALID_ID, + light_dir: Vec3::new(1.0, 1.0, 1.0).normalize(), + }; + + let mut state = State { + scene: scene.clone(), + user: user_state, + }; + + let cube = make_cube(&device, &vertex_colors); let ground = make_ground_plane(&device); + state.user.cube_id = state.scene.attach_geometry(&cube); + state.user.ground_id = state.scene.attach_geometry(&ground); - // TODO: Support for Embree3's new vertex attributes - let face_colors = vec![ - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(0.0, 1.0, 0.0), - Vector3::new(0.0, 1.0, 0.0), - Vector3::new(1.0, 0.0, 1.0), - Vector3::new(1.0, 0.0, 1.0), - Vector3::new(1.0, 1.0, 1.0), - Vector3::new(1.0, 1.0, 1.0), - Vector3::new(0.0, 0.0, 1.0), - Vector3::new(0.0, 0.0, 1.0), - Vector3::new(1.0, 1.0, 0.0), - Vector3::new(1.0, 1.0, 0.0), - ]; + state.scene.commit(); + + display::run(display, state, move |_, _| {}, render_frame, |_| {}); +} + +// Task that renders a single pixel. +fn render_pixel(x: u32, y: u32, _time: f32, camera: &Camera, state: &State) -> u32 { + let mut ctx = IntersectContext::coherent(); + let dir = camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)); + let mut ray_hit = RayHit::from_ray(Ray::segment( + camera.pos.into(), + dir.into(), + 0.0, + f32::INFINITY, + )); + state.scene.intersect(&mut ctx, &mut ray_hit); + let mut pixel = 0; + if ray_hit.hit.is_valid() { + let diffuse = if ray_hit.hit.geomID == state.user.ground_id { + glam::vec3(0.6, 0.6, 0.6) + } else { + glam::Vec3::from(state.user.face_colors[ray_hit.hit.primID as usize]) + }; + + let mut shadow_ray = Ray::segment( + ray_hit.ray.hit_point(), + state.user.light_dir.into(), + 0.001, + f32::INFINITY, + ); - let mut scene = Scene::new(&device); - scene.attach_geometry(cube); - let ground_id = scene.attach_geometry(ground); - let rtscene = scene.commit(); - - let mut intersection_ctx = IntersectContext::coherent(); - - display.run(|image, camera_pose, _| { - for p in image.iter_mut() { - *p = 0; - } - let img_dims = image.dimensions(); - let camera = Camera::look_dir( - camera_pose.pos, - camera_pose.dir, - camera_pose.up, - 75.0, - img_dims, + // Check if the shadow ray is occluded. + let color = if !state.scene.occluded(&mut ctx, &mut shadow_ray) { + diffuse + } else { + diffuse * 0.5 + }; + + pixel = rgba_to_u32( + (color.x * 255.0) as u8, + (color.y * 255.0) as u8, + (color.z * 255.0) as u8, + 255, ); - // Render the scene - for j in 0..img_dims.1 { - for i in 0..img_dims.0 { - let dir = camera.ray_dir((i as f32 + 0.5, j as f32 + 0.5)); - let ray = Ray::new(camera.pos, dir); - let mut ray_hit = RayHit::new(ray); - rtscene.intersect(&mut intersection_ctx, &mut ray_hit); - if ray_hit.hit.hit() { - let mut p = image.get_pixel_mut(i, j); - let color = if ray_hit.hit.geomID == ground_id { - Vector3::new(0.6, 0.6, 0.6) - } else { - face_colors[ray_hit.hit.primID as usize] - }; - p[0] = (color.x * 255.0) as u8; - p[1] = (color.y * 255.0) as u8; - p[2] = (color.z * 255.0) as u8; - } - } - } + } + pixel +} + +fn render_frame(frame: &mut TiledImage, camera: &Camera, time: f32, state: &mut State) { + frame.par_tiles_mut().for_each(|tile| { + tile.pixels.iter_mut().enumerate().for_each(|(i, pixel)| { + let x = tile.x + (i % tile.w as usize) as u32; + let y = tile.y + (i / tile.w as usize) as u32; + *pixel = render_pixel(x, y, time, camera, state); + }); }); } diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000..055a1dc55 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,7 @@ +reorder_imports = true +imports_granularity = "Crate" +fn_single_line = true +format_code_in_doc_comments = true +format_strings = true +newline_style = "Unix" +wrap_comments = true \ No newline at end of file diff --git a/scripts/build-examples-linux-mac.sh b/scripts/build-examples-linux-mac.sh index 186ec98da..74b609557 100755 --- a/scripts/build-examples-linux-mac.sh +++ b/scripts/build-examples-linux-mac.sh @@ -2,14 +2,15 @@ # build the examples cd examples -#for d in `ls ./`; do -for d in ./triangle; do - cd $d - pwd - cargo build - if [[ "$?" != "0" ]]; then - exit 1 +for dir in */; do + if [[ -d "$dir" && "$dir" != "todos/" ]]; then + cd $dir + pwd + cargo build + if [[ "$?" != "0" ]]; then + exit 1 + fi + cd ../ fi - cd ../ done diff --git a/scripts/build-test-mac.sh b/scripts/build-test-mac.sh index b7fb09091..28f15992a 100755 --- a/scripts/build-test-mac.sh +++ b/scripts/build-test-mac.sh @@ -9,14 +9,15 @@ fi # build the examples cd examples -#for d in `ls ./`; do -for d in ./triangle; do - cd $d - pwd - cargo build - if [[ "$?" != "0" ]]; then - exit 1 +for dir in */; do + if [[ -d "$dir" && "$dir" != "todos/" ]]; then + cd $dir + pwd + cargo build + if [[ "$?" != "0" ]]; then + exit 1 + fi + cd ../ fi - cd ../ done diff --git a/scripts/build-test-windows.ps1 b/scripts/build-test-windows.ps1 index 4dd5057dd..fa1118265 100644 --- a/scripts/build-test-windows.ps1 +++ b/scripts/build-test-windows.ps1 @@ -15,14 +15,13 @@ if (!$?) { # build the examples cd examples -#Get-ChildItem .\ -Directory | ForEach-Object { -# Write-Output $_ - #cd $_ - cd triangle +Get-ChildItem .\ -Directory | Where-Object { $_.Name -ne "todos" } | ForEach-Object { + Write-Output $_ + cd $_ cargo build if (!$?) { exit 1 } cd .. -#} +} diff --git a/scripts/check-examples-formatting.sh b/scripts/check-examples-formatting.sh index dd2a5aea3..830c8ae09 100755 --- a/scripts/check-examples-formatting.sh +++ b/scripts/check-examples-formatting.sh @@ -2,12 +2,14 @@ # build the examples cd examples -for d in `ls ./`; do - cd $d +for dir in */; do + if [[ -d "$dir" && "$dir" != "todos/" ]]; then + cd $dir pwd cargo fmt -- --check if [[ "$?" != "0" ]]; then exit 1 fi cd ../ + fi done diff --git a/scripts/generate-sys-bindings.sh b/scripts/generate-sys-bindings.sh index 27d667389..8cfaa6741 100755 --- a/scripts/generate-sys-bindings.sh +++ b/scripts/generate-sys-bindings.sh @@ -3,9 +3,9 @@ bindgen $1 -o $2 \ --no-doc-comments \ --distrust-clang-mangling \ - --whitelist-function "rtc.*" \ - --whitelist-type "RTC.*" \ - --whitelist-var "RTC.*" \ + --allowlist-function "rtc.*" \ + --allowlist-type "RTC.*" \ + --allowlist-var "RTC.*" \ --rustified-enum "RTCDeviceProperty" \ --rustified-enum "RTCError" \ --rustified-enum "RTCBufferType" \ diff --git a/src/bezier_curve.rs b/src/bezier_curve.rs deleted file mode 100644 index b594c51d4..000000000 --- a/src/bezier_curve.rs +++ /dev/null @@ -1,146 +0,0 @@ -use std::sync::Arc; - -use crate::buffer::Buffer; -use crate::device::Device; -use crate::geometry::Geometry; -use crate::sys::*; -use crate::{BufferType, CurveType, Format, GeometryType}; - -pub struct BezierCurve { - device: Arc, - pub(crate) handle: RTCGeometry, - pub vertex_buffer: Buffer<[f32; 4]>, - pub index_buffer: Buffer, - pub normal_buffer: Option>, -} - -impl BezierCurve { - pub fn flat( - device: Arc, - num_segments: usize, - num_verts: usize, - use_normals: bool, - ) -> Arc { - BezierCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::Flat, - use_normals, - ) - } - pub fn round( - device: Arc, - num_segments: usize, - num_verts: usize, - use_normals: bool, - ) -> Arc { - BezierCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::Round, - use_normals, - ) - } - pub fn normal_oriented( - device: Arc, - num_segments: usize, - num_verts: usize, - ) -> Arc { - BezierCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::NormalOriented, - true, - ) - } - - fn unanimated( - device: Arc, - num_segments: usize, - num_verts: usize, - curve_type: CurveType, - use_normals: bool, - ) -> Arc { - let h: RTCGeometry; - match curve_type { - CurveType::NormalOriented => { - h = unsafe { - rtcNewGeometry(device.handle, GeometryType::NORMAL_ORIENTED_BEZIER_CURVE) - } - } - CurveType::Round => { - h = unsafe { rtcNewGeometry(device.handle, GeometryType::ROUND_BEZIER_CURVE) } - } - _ => h = unsafe { rtcNewGeometry(device.handle, GeometryType::FLAT_BEZIER_CURVE) }, - }; - let mut vertex_buffer = Buffer::new(device.clone(), num_verts); - let mut index_buffer = Buffer::new(device.clone(), num_segments); - let mut normal_buffer = None; - - unsafe { - rtcSetGeometryBuffer( - h, - BufferType::VERTEX, - 0, - Format::FLOAT4, - vertex_buffer.handle, - 0, - 16, - num_verts, - ); - vertex_buffer.set_attachment(h, BufferType::VERTEX, 0); - - rtcSetGeometryBuffer( - h, - BufferType::INDEX, - 0, - Format::UINT, - index_buffer.handle, - 0, - 4, - num_segments, - ); - index_buffer.set_attachment(h, BufferType::INDEX, 0); - - if use_normals { - let mut temp_normal_buffer = Buffer::new(device.clone(), num_verts); - rtcSetGeometryBuffer( - h, - BufferType::NORMAL, - 0, - Format::FLOAT3, - temp_normal_buffer.handle, - 0, - 12, - num_verts, - ); - temp_normal_buffer.set_attachment(h, BufferType::NORMAL, 0); - normal_buffer = Some(temp_normal_buffer); - } - } - Arc::new(BezierCurve { - device: device, - handle: h, - vertex_buffer: vertex_buffer, - index_buffer: index_buffer, - normal_buffer: normal_buffer, - }) - } -} - -impl Geometry for BezierCurve { - fn handle(&self) -> RTCGeometry { - self.handle - } -} - -impl Drop for BezierCurve { - fn drop(&mut self) { - unsafe { - rtcReleaseGeometry(self.handle); - } - } -} diff --git a/src/bspline_curve.rs b/src/bspline_curve.rs deleted file mode 100644 index ce1bfc6ce..000000000 --- a/src/bspline_curve.rs +++ /dev/null @@ -1,146 +0,0 @@ -use std::sync::Arc; - -use crate::buffer::Buffer; -use crate::device::Device; -use crate::geometry::Geometry; -use crate::sys::*; -use crate::{BufferType, CurveType, Format, GeometryType}; - -pub struct BsplineCurve { - device: Arc, - pub(crate) handle: RTCGeometry, - pub vertex_buffer: Buffer<[f32; 4]>, - pub index_buffer: Buffer, - pub normal_buffer: Option>, -} - -impl BsplineCurve { - pub fn flat( - device: Arc, - num_segments: usize, - num_verts: usize, - use_normals: bool, - ) -> Arc { - BsplineCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::Flat, - use_normals, - ) - } - pub fn round( - device: Arc, - num_segments: usize, - num_verts: usize, - use_normals: bool, - ) -> Arc { - BsplineCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::Round, - use_normals, - ) - } - pub fn normal_oriented( - device: Arc, - num_segments: usize, - num_verts: usize, - ) -> Arc { - BsplineCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::NormalOriented, - true, - ) - } - - fn unanimated( - device: Arc, - num_segments: usize, - num_verts: usize, - curve_type: CurveType, - use_normals: bool, - ) -> Arc { - let h: RTCGeometry; - match curve_type { - CurveType::NormalOriented => { - h = unsafe { - rtcNewGeometry(device.handle, GeometryType::NORMAL_ORIENTED_BSPLINE_CURVE) - } - } - CurveType::Round => { - h = unsafe { rtcNewGeometry(device.handle, GeometryType::ROUND_BSPLINE_CURVE) } - } - _ => h = unsafe { rtcNewGeometry(device.handle, GeometryType::FLAT_BSPLINE_CURVE) }, - }; - let mut vertex_buffer = Buffer::new(device.clone(), num_verts); - let mut index_buffer = Buffer::new(device.clone(), num_segments); - let mut normal_buffer = None; - - unsafe { - rtcSetGeometryBuffer( - h, - BufferType::VERTEX, - 0, - Format::FLOAT4, - vertex_buffer.handle, - 0, - 16, - num_verts, - ); - vertex_buffer.set_attachment(h, BufferType::VERTEX, 0); - - rtcSetGeometryBuffer( - h, - BufferType::INDEX, - 0, - Format::UINT, - index_buffer.handle, - 0, - 4, - num_segments, - ); - index_buffer.set_attachment(h, BufferType::INDEX, 0); - - if use_normals { - let mut temp_normal_buffer = Buffer::new(device.clone(), num_verts); - rtcSetGeometryBuffer( - h, - BufferType::NORMAL, - 0, - Format::FLOAT3, - temp_normal_buffer.handle, - 0, - 12, - num_verts, - ); - temp_normal_buffer.set_attachment(h, BufferType::NORMAL, 0); - normal_buffer = Some(temp_normal_buffer); - } - } - Arc::new(BsplineCurve { - device: device, - handle: h, - vertex_buffer: vertex_buffer, - index_buffer: index_buffer, - normal_buffer: normal_buffer, - }) - } -} - -impl Geometry for BsplineCurve { - fn handle(&self) -> RTCGeometry { - self.handle - } -} - -impl Drop for BsplineCurve { - fn drop(&mut self) { - unsafe { - rtcReleaseGeometry(self.handle); - } - } -} diff --git a/src/buffer.rs b/src/buffer.rs index 05d953c6b..f54282a9b 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,99 +1,122 @@ -use std::marker::PhantomData; -use std::ops::{Index, IndexMut}; -use std::sync::Arc; -use std::{mem, ptr}; - -use crate::device::Device; -use crate::sys::*; -use crate::BufferType; - -#[derive(Copy, Clone)] -struct BufferAttachment { - geom: RTCGeometry, - buf_type: BufferType, - slot: u32, -} - -impl BufferAttachment { - fn none() -> BufferAttachment { - BufferAttachment { - geom: ptr::null_mut(), - buf_type: BufferType::VERTEX, - slot: std::u32::MAX, - } - } - fn is_attached(&self) -> bool { - self.geom != ptr::null_mut() - } -} +use crate::Error; +use std::{ + marker::PhantomData, + mem, + num::NonZeroUsize, + ops::{Bound, Deref, DerefMut, RangeBounds}, +}; + +use crate::{device::Device, sys::*}; -// TODO: To handle this nicely for sharing/re-using/changing buffer views -// we basically need an API/struct for making buffer views of existing -// larger buffers. -pub struct Buffer { - device: Arc, +/// Non-zero integer type used to describe the size of a buffer. +pub type BufferSize = NonZeroUsize; + +/// Handle to a buffer managed by Embree. +#[derive(Debug)] +pub struct Buffer { + pub(crate) device: Device, pub(crate) handle: RTCBuffer, - // TODO: We need a list of RTCGeometry handles - // that we're attached to to mark buffers as updated on - // the geometries. - bytes: usize, - attachment: BufferAttachment, - marker: PhantomData, + pub(crate) size: BufferSize, } -impl Buffer { - /// Allocate a buffer with some raw capacity in bytes - pub fn raw(device: Arc, bytes: usize) -> Buffer { - // Pad to a multiple of 16 bytes - let bytes = if bytes % 16 == 0 { - bytes - } else { - bytes + bytes / 16 - }; - let h = unsafe { rtcNewBuffer(device.handle, bytes) }; +impl Clone for Buffer { + fn clone(&self) -> Self { + unsafe { rtcRetainBuffer(self.handle) }; Buffer { - device: device, - handle: h, - bytes: bytes, - attachment: BufferAttachment::none(), - marker: PhantomData, + device: self.device.clone(), + handle: self.handle, + size: self.size, } } - pub fn new(device: Arc, len: usize) -> Buffer { - let mut bytes = len * mem::size_of::(); +} + +impl Buffer { + /// Creates a new data buffer of the given size. + pub(crate) fn new(device: &Device, size: BufferSize) -> Result { // Pad to a multiple of 16 bytes - bytes = if bytes % 16 == 0 { - bytes + let size = if size.get() % 16 == 0 { + size.get() } else { - bytes + bytes / 16 + (size.get() + 15) & !15 }; - let h = unsafe { rtcNewBuffer(device.handle, bytes) }; - Buffer { - device: device, - handle: h, - bytes: bytes, - attachment: BufferAttachment::none(), - marker: PhantomData, + let handle = unsafe { rtcNewBuffer(device.handle, size) }; + if handle.is_null() { + Err(device.get_error()) + } else { + Ok(Buffer { + handle, + size: NonZeroUsize::new(size).unwrap(), + device: device.clone(), + }) } } - pub fn map<'a>(&'a mut self) -> MappedBuffer<'a, T> { - let len = self.bytes / mem::size_of::(); - let slice = unsafe { rtcGetBufferData(self.handle) as *mut T }; - MappedBuffer { - buffer: PhantomData, - attachment: self.attachment, - slice: slice, - len: len, + + /// Returns the raw handle to the buffer. + /// + /// # Safety + /// + /// Use this function only if you know what you are doing. The returned + /// handle is a raw pointer to an Embree reference-counted object. The + /// reference count is not increased by this function, so the caller must + /// ensure that the handle is not used after the buffer object is + /// destroyed. + pub unsafe fn handle(&self) -> RTCBuffer { self.handle } + + /// Returns a slice of the buffer. + /// + /// This function only returns a slice of the buffer, and does not + /// map the buffer into memory. To map the buffer into memory, use + /// [`Buffer::mapped_range`] or [`BufferSlice::view`] to create a + /// read-only view of the buffer, or [`Buffer::mapped_range_mut`] or + /// [`BufferSlice::view_mut`] to create a mutable view of the buffer. + /// + /// # Arguments + /// + /// * `bounds` - The range of bytes to slice into the buffer. + pub fn slice>(&self, bounds: S) -> BufferSlice { + let (offset, size) = range_bounds_to_offset_and_size(bounds); + let size = size.unwrap_or_else(|| self.size.get() - offset); + debug_assert!(offset + size <= self.size.get() && offset < self.size.get()); + BufferSlice::Buffer { + buffer: self, + offset, + size: NonZeroUsize::new(size).unwrap(), } } - pub(crate) fn set_attachment(&mut self, geom: RTCGeometry, buf_type: BufferType, slot: u32) { - self.attachment.geom = geom; - self.attachment.buf_type = buf_type; - self.attachment.slot = slot; + + /// Slices into the buffer for the given range. + /// + /// # Arguments + /// + /// * `bounds` - The range of indices to slice into the buffer. + /// - Ranges with no end will slice to the end of the buffer. + /// - Totally unbounded range (..) will slice the entire buffer. + pub fn mapped_range, T>(&self, bounds: S) -> BufferView<'_, T> { + let (offset, size) = range_bounds_to_offset_and_size(bounds); + let size = size.unwrap_or_else(|| self.size.get() - offset); + debug_assert!(offset + size <= self.size.get() && offset < self.size.get()); + BufferView::new(self, offset, BufferSize::new(size).unwrap()).unwrap() + } + + /// Mutable slice into the buffer for the given range. + /// + /// # Arguments + /// + /// * `bounds` - The range of indices to slice into the buffer. + /// - Ranges with no end will slice to the end of the buffer. + /// - Totally unbounded range (..) will slice the entire buffer. + pub fn mapped_range_mut, T>( + &mut self, + bounds: S, + ) -> BufferViewMut<'_, T> { + let (offset, size) = range_bounds_to_offset_and_size(bounds); + let size = size.unwrap_or_else(|| self.size.get() - offset); + debug_assert!(offset + size <= self.size.get() && offset < self.size.get()); + BufferViewMut::new(self, offset, BufferSize::new(size).unwrap()).unwrap() } } -impl Drop for Buffer { +impl Drop for Buffer { fn drop(&mut self) { unsafe { rtcReleaseBuffer(self.handle); @@ -101,51 +124,264 @@ impl Drop for Buffer { } } -pub struct MappedBuffer<'a, T: 'a> { - buffer: PhantomData<&'a mut Buffer>, - attachment: BufferAttachment, - slice: *mut T, - len: usize, +/// A read-only view into mapped buffer. +#[derive(Debug)] +pub struct BufferView<'buf, T: 'buf> { + mapped: BufferMappedRange<'buf, T>, + marker: PhantomData<&'buf T>, } -impl<'a, T: 'a> MappedBuffer<'a, T> { - pub fn len(&self) -> usize { - self.len - } +/// A write-only view into mapped buffer. +#[derive(Debug)] +pub struct BufferViewMut<'buf, T: 'buf> { + mapped: BufferMappedRange<'buf, T>, + marker: PhantomData<&'buf mut T>, } -impl<'a, T: 'a> Drop for MappedBuffer<'a, T> { - fn drop(&mut self) { - if self.attachment.is_attached() { - // TODO: support for attaching one buffer to multiple geoms? - unsafe { - rtcUpdateGeometryBuffer( - self.attachment.geom, - self.attachment.buf_type, - self.attachment.slot, +/// Slice into a region of memory. This can either be a slice to a [`Buffer`] or +/// a slice to memory managed by Embree (mostly created from +/// [`rtcSetNewGeometryBuffer`]) or from user owned/borrowed memory. +#[derive(Debug, Clone, Copy)] +pub enum BufferSlice<'src> { + /// Slice into a [`Buffer`] object. + Buffer { + buffer: &'src Buffer, + offset: usize, + size: BufferSize, + }, + /// Slice into memory created and managed internally inside [`RTCGeometry`]. + GeometryLocal { + ptr: *mut ::std::os::raw::c_void, + size: BufferSize, + marker: PhantomData<&'src mut [::std::os::raw::c_void]>, + }, + /// Slice into user borrowed/owned memory. + User { + ptr: *const u8, + offset: usize, + size: BufferSize, + marker: PhantomData<&'src mut [u8]>, + }, +} + +impl<'buf, T> From<&'buf [T]> for BufferSlice<'buf> { + fn from(vec: &'buf [T]) -> Self { BufferSlice::from_slice(vec, ..) } +} + +impl<'src> BufferSlice<'src> { + /// Creates a new [`BufferSlice`] from a user owned buffer. + /// + /// # Arguments + /// + /// * `buffer` - The buffer to create a slice from. + /// * `bounds` - The range of indices to slice into the buffer. Different + /// from [`Buffer::slice`], + pub fn from_slice>(slice: &[T], bounds: S) -> Self { + let (first, count) = range_bounds_to_offset_and_size(bounds); + let count = count.unwrap_or(slice.len() - first); + debug_assert!( + first + count <= slice.len() && first < slice.len(), + "Invalid slice range" + ); + let elem_size = mem::size_of::(); + BufferSlice::User { + ptr: slice.as_ptr() as *const u8, + offset: first * elem_size, + size: BufferSize::new((first + count) * elem_size).unwrap(), + marker: PhantomData, + } + } + + pub fn view(&self) -> Result, Error> { + match self { + BufferSlice::Buffer { + buffer, + offset, + size, + } => { + let mapped = BufferMappedRange::from_buffer(buffer, *offset, size.get())?; + Ok(BufferView { + // slice: *self, + mapped, + marker: PhantomData, + }) + } + BufferSlice::GeometryLocal { ptr, size, .. } => { + debug_assert!( + size.get() % mem::size_of::() == 0, + "Size of the range of the mapped buffer must be multiple of T!" + ); + let len = size.get() / mem::size_of::(); + let mapped = unsafe { BufferMappedRange::from_raw_parts(*ptr as *mut T, len) }; + Ok(BufferView { + mapped, + marker: PhantomData, + }) + } + BufferSlice::User { .. } => { + eprintln!("Creating a view from a user owned/borrowed memory is not allowed!"); + Err(Error::INVALID_OPERATION) + } + } + } + + pub fn view_mut(&self) -> Result, Error> { + match self { + BufferSlice::Buffer { + buffer, + offset, + size, + } => Ok(BufferViewMut { + mapped: BufferMappedRange::from_buffer(buffer, *offset, size.get())?, + marker: PhantomData, + }), + BufferSlice::GeometryLocal { ptr, size, .. } => { + debug_assert!( + size.get() % mem::size_of::() == 0, + "Size of the range of the mapped buffer must be multiple of T!" ); + let len = size.get() / mem::size_of::(); + let mapped = unsafe { BufferMappedRange::from_raw_parts(*ptr as *mut T, len) }; + Ok(BufferViewMut { + mapped, + marker: PhantomData, + }) + } + BufferSlice::User { .. } => { + eprintln!("Creating a view from a user owned/borrowed memory is not allowed!"); + Err(Error::INVALID_OPERATION) } } } } -impl<'a, T: 'a> Index for MappedBuffer<'a, T> { - type Output = T; +impl<'src, T> BufferView<'src, T> { + /// Creates a new slice from the given Buffer with the given offset and + /// size. Only used internally by [`Buffer::mapped_range`]. + fn new( + buffer: &'src Buffer, + offset: usize, + size: BufferSize, + ) -> Result, Error> { + Ok(BufferView { + mapped: BufferMappedRange::from_buffer(buffer, offset, size.into())?, + marker: PhantomData, + }) + } +} - fn index(&self, index: usize) -> &T { - // TODO: We should only check in debug build - if index >= self.len { - panic!("MappedBuffer index out of bounds"); - } - unsafe { &*self.slice.offset(index as isize) } +impl<'src, T> BufferViewMut<'src, T> { + /// Creates a new slice from the given Buffer with the given offset and + /// size. Only used internally by [`Buffer::mapped_range_mut`]. + fn new( + buffer: &'src Buffer, + offset: usize, + size: BufferSize, + ) -> Result, Error> { + Ok(BufferViewMut { + mapped: BufferMappedRange::from_buffer(buffer, offset, size.into())?, + marker: PhantomData, + }) } } -impl<'a, T: 'a> IndexMut for MappedBuffer<'a, T> { - fn index_mut(&mut self, index: usize) -> &mut T { - if index >= self.len { - panic!("MappedBuffer index out of bounds"); +/// A slice of a mapped [`Buffer`]. +#[derive(Debug)] +struct BufferMappedRange<'a, T: 'a> { + ptr: *mut T, + len: usize, + marker: PhantomData<&'a mut T>, // covariant without drop check +} + +impl<'a, T: 'a> BufferMappedRange<'a, T> { + /// Creates a new slice from the given Buffer with the given offset and + /// size. + /// + /// The offset and size must be in bytes and must be a multiple of the size + /// of `T`. + /// + /// # Safety + /// + /// The caller must ensure that the given offset and size are valid. + fn from_buffer( + buffer: &'a Buffer, + offset: usize, + size: usize, + ) -> Result, Error> { + debug_assert!( + size % mem::size_of::() == 0, + "Size of the range of the mapped buffer must be multiple of T!" + ); + debug_assert!( + offset % mem::size_of::() == 0, + "Offset must be multiple of T!" + ); + let ptr = unsafe { + let ptr = rtcGetBufferData(buffer.handle) as *const u8; + if ptr.is_null() { + return Err(buffer.device.get_error()); + } + ptr.add(offset) + } as *mut T; + Ok(BufferMappedRange { + ptr, + len: size / mem::size_of::(), + marker: PhantomData, + }) + } + + /// Creates a new slice from the given raw pointer and length. + unsafe fn from_raw_parts(ptr: *mut T, len: usize) -> BufferMappedRange<'a, T> { + BufferMappedRange { + ptr, + len, + marker: PhantomData, } - unsafe { &mut *self.slice.offset(index as isize) } } + + fn as_slice(&self) -> &[T] { unsafe { std::slice::from_raw_parts(self.ptr, self.len) } } + + fn as_mut_slice(&mut self) -> &mut [T] { + unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) } + } +} + +impl<'src, T> AsRef<[T]> for BufferView<'src, T> { + fn as_ref(&self) -> &[T] { self.mapped.as_slice() } +} + +impl<'src, T> AsMut<[T]> for BufferViewMut<'src, T> { + fn as_mut(&mut self) -> &mut [T] { self.mapped.as_mut_slice() } +} + +impl<'src, T> Deref for BufferView<'src, T> { + type Target = [T]; + + fn deref(&self) -> &Self::Target { self.mapped.as_slice() } +} + +impl<'src, T> Deref for BufferViewMut<'src, T> { + type Target = [T]; + + fn deref(&self) -> &Self::Target { self.mapped.as_slice() } +} + +impl<'src, T> DerefMut for BufferViewMut<'src, T> { + fn deref_mut(&mut self) -> &mut Self::Target { self.mapped.as_mut_slice() } +} + +/// Converts a range bounds into an offset and size. +fn range_bounds_to_offset_and_size>(bounds: S) -> (usize, Option) { + let offset = match bounds.start_bound() { + Bound::Included(&n) => n, + Bound::Excluded(&n) => n + 1, + Bound::Unbounded => 0, + }; + let size = match bounds.end_bound() { + Bound::Included(&n) => Some(n - offset + 1), + Bound::Excluded(&n) => Some(n - offset), + Bound::Unbounded => None, + }; + + (offset, size) } diff --git a/src/bvh.rs b/src/bvh.rs new file mode 100644 index 000000000..66c70dbfd --- /dev/null +++ b/src/bvh.rs @@ -0,0 +1,175 @@ +use crate::{sys::*, BuildFlags, BuildPrimitive, BuildQuality, Device, Error}; + +#[derive(Debug, Clone, Copy)] +pub struct ThreadLocalAllocator(RTCThreadLocalAllocator); + +pub struct Bvh { + handle: RTCBVH, +} + +impl Clone for Bvh { + fn clone(&self) -> Self { + unsafe { rtcRetainBVH(self.handle) } + Self { + handle: self.handle, + } + } +} + +impl Drop for Bvh { + fn drop(&mut self) { unsafe { rtcReleaseBVH(self.handle) } } +} + +impl Bvh { + pub(crate) fn new(device: &Device) -> Result { + let handle = unsafe { rtcNewBVH(device.handle) }; + if handle.is_null() { + Err(device.get_error()) + } else { + Ok(Self { handle }) + } + } +} + +pub trait Node {} + +pub trait LeafNode {} + +type CreateNodeFn = fn(ThreadLocalAllocator, u32, &mut T) -> Box; + +pub struct BvhBuilderUserData<'a, T> { + create_node_fn: CreateNodeFn, + set_node_children_fn: *mut std::os::raw::c_void, + set_node_bounds_fn: *mut std::os::raw::c_void, + create_leaf_fn: *mut std::os::raw::c_void, + split_primitive_fn: *mut std::os::raw::c_void, + progress_monitor_function: *mut std::os::raw::c_void, + user_data: &'a mut T, +} + +pub struct BvhBuilder<'a, T> { + quality: Option, + flags: Option, + max_branching_factor: Option, + max_depth: Option, + sah_block_size: Option, + min_leaf_size: Option, + max_leaf_size: Option, + traversal_cost: Option, + intersection_cost: Option, + primitives: Option>, + // create_node_fn: Option>, + user_data: Option<&'a mut T>, + ready: u32, +} + +impl<'a, T> BvhBuilder<'a, T> { + pub fn new() -> Self { + Self { + quality: None, + flags: None, + max_branching_factor: None, + max_depth: None, + sah_block_size: None, + min_leaf_size: None, + max_leaf_size: None, + traversal_cost: None, + intersection_cost: None, + primitives: None, + // create_node_fn: None, + user_data: None, + ready: 0, + } + } + + pub fn quality(mut self, quality: BuildQuality) -> Self { + self.quality = Some(quality); + self.ready |= 1; + self + } + + pub fn flags(mut self, flags: BuildFlags) -> Self { + self.flags = Some(flags); + self.ready |= 1 << 1; + self + } + + pub fn max_branching_factor(mut self, max_branching_factor: u32) -> Self { + self.max_branching_factor = Some(max_branching_factor); + self.ready |= 1 << 2; + self + } + + pub fn max_depth(mut self, max_depth: u32) -> Self { + self.max_depth = Some(max_depth); + self.ready |= 1 << 3; + self + } + + pub fn sah_block_size(mut self, sah_block_size: u32) -> Self { + self.sah_block_size = Some(sah_block_size); + self.ready |= 1 << 4; + self + } + + pub fn min_leaf_size(mut self, min_leaf_size: u32) -> Self { + self.min_leaf_size = Some(min_leaf_size); + self.ready |= 1 << 5; + self + } + + pub fn max_leaf_size(mut self, max_leaf_size: u32) -> Self { + self.max_leaf_size = Some(max_leaf_size); + self.ready |= 1 << 6; + self + } + + pub fn traversal_cost(mut self, traversal_cost: f32) -> Self { + self.traversal_cost = Some(traversal_cost); + self.ready |= 1 << 7; + self + } + + pub fn intersection_cost(mut self, intersection_cost: f32) -> Self { + self.intersection_cost = Some(intersection_cost); + self.ready |= 1 << 8; + self + } + + pub fn primitives(mut self, primitives: Vec) -> Self { + self.primitives = Some(primitives); + self.ready |= 1 << 9; + self + } + + // pub fn create_node_fn(mut self, func: CreateNodeFn) -> Self { + // self.create_node_fn = Some(func); + // self.ready |= 1 << 10; + // self + // } + // + // pub fn set_node_children_fn(mut self, set_node_children_fn: *mut + // std::os::raw::c_void) -> Self { self.ready |= 1 << 11; + // self + // } + // + // pub fn set_node_bounds_fn(mut self, set_node_bounds_fn: *mut + // std::os::raw::c_void) -> Self { self.ready |= 1 << 12; + // self + // } + // + // pub fn create_leaf_fn(mut self, create_leaf_fn: *mut std::os::raw::c_void) -> + // Self { self.ready |= 1 << 13; + // self + // } + // + // pub fn split_primitive_fn(mut self, split_primitive_fn: *mut + // std::os::raw::c_void) -> Self { self.ready |= 1 << 14; + // self + // } + // + // pub fn progress_monitor_fn(mut self, progress_monitor_fn: *mut + // std::os::raw::c_void) -> Self { self.ready |= 1 << 15; + // self + // } +} diff --git a/src/callback.rs b/src/callback.rs deleted file mode 100644 index 0c8a8364b..000000000 --- a/src/callback.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::os::raw::c_void; - -use crate::sys::*; - -/// Helper function to convert a Rust closure to `RTCProgressMonitorFunction` callback. -pub(crate) fn progress_monitor_function_helper(_f: &mut F) -> RTCProgressMonitorFunction -where - F: FnMut(f64) -> bool, -{ - unsafe extern "C" fn inner(f: *mut c_void, n: f64) -> bool - where - F: FnMut(f64) -> bool, - { - let cb = &mut *(f as *mut F); - cb(n) - } - - inner:: -} - -/// Helper function to convert a Rust closure to `RTCErrorFunction` callback. -pub(crate) fn error_function_helper(_f: &mut F) -> RTCErrorFunction -where - F: FnMut(RTCError, &'static str), -{ - unsafe extern "C" fn inner( - f: *mut c_void, - error: RTCError, - msg: *const ::std::os::raw::c_char, - ) where - F: FnMut(RTCError, &'static str), - { - let cb = &mut *(f as *mut F); - cb(error, std::ffi::CStr::from_ptr(msg).to_str().unwrap()) - } - - inner:: -} - -/// Helper function to convert a Rust closure to `RTCMemoryMonitorFunction` callback. -pub(crate) fn memory_monitor_function_helper(_f: &mut F) -> RTCMemoryMonitorFunction -where - F: FnMut(isize, bool) -> bool, -{ - unsafe extern "C" fn inner(f: *mut c_void, bytes: isize, post: bool) -> bool - where - F: FnMut(isize, bool) -> bool, - { - let cb = &mut *(f as *mut F); - cb(bytes, post) - } - - inner:: -} diff --git a/src/catmull_rom_curve.rs b/src/catmull_rom_curve.rs deleted file mode 100644 index ff1cfd510..000000000 --- a/src/catmull_rom_curve.rs +++ /dev/null @@ -1,149 +0,0 @@ -use std::sync::Arc; - -use crate::buffer::Buffer; -use crate::device::Device; -use crate::geometry::Geometry; -use crate::sys::*; -use crate::{BufferType, CurveType, Format, GeometryType}; - -pub struct CatmullRomCurve { - device: Arc, - pub(crate) handle: RTCGeometry, - pub vertex_buffer: Buffer<[f32; 4]>, - pub index_buffer: Buffer, - pub normal_buffer: Option>, -} - -impl CatmullRomCurve { - pub fn flat( - device: Arc, - num_segments: usize, - num_verts: usize, - use_normals: bool, - ) -> Arc { - CatmullRomCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::Flat, - use_normals, - ) - } - pub fn round( - device: Arc, - num_segments: usize, - num_verts: usize, - use_normals: bool, - ) -> Arc { - CatmullRomCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::Round, - use_normals, - ) - } - pub fn normal_oriented( - device: Arc, - num_segments: usize, - num_verts: usize, - ) -> Arc { - CatmullRomCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::NormalOriented, - true, - ) - } - - fn unanimated( - device: Arc, - num_segments: usize, - num_verts: usize, - curve_type: CurveType, - use_normals: bool, - ) -> Arc { - let h: RTCGeometry; - match curve_type { - CurveType::NormalOriented => { - h = unsafe { - rtcNewGeometry( - device.handle, - GeometryType::NORMAL_ORIENTED_CATMULL_ROM_CURVE, - ) - } - } - CurveType::Round => { - h = unsafe { rtcNewGeometry(device.handle, GeometryType::ROUND_CATMULL_ROM_CURVE) } - } - _ => h = unsafe { rtcNewGeometry(device.handle, GeometryType::FLAT_CATMULL_ROM_CURVE) }, - }; - let mut vertex_buffer = Buffer::new(device.clone(), num_verts); - let mut index_buffer = Buffer::new(device.clone(), num_segments); - let mut normal_buffer = None; - - unsafe { - rtcSetGeometryBuffer( - h, - BufferType::VERTEX, - 0, - Format::FLOAT4, - vertex_buffer.handle, - 0, - 16, - num_verts, - ); - vertex_buffer.set_attachment(h, BufferType::VERTEX, 0); - - rtcSetGeometryBuffer( - h, - BufferType::INDEX, - 0, - Format::UINT, - index_buffer.handle, - 0, - 4, - num_segments, - ); - index_buffer.set_attachment(h, BufferType::INDEX, 0); - - if use_normals { - let mut temp_normal_buffer = Buffer::new(device.clone(), num_verts); - rtcSetGeometryBuffer( - h, - BufferType::NORMAL, - 0, - Format::FLOAT3, - temp_normal_buffer.handle, - 0, - 12, - num_verts, - ); - temp_normal_buffer.set_attachment(h, BufferType::NORMAL, 0); - normal_buffer = Some(temp_normal_buffer); - } - } - Arc::new(CatmullRomCurve { - device: device, - handle: h, - vertex_buffer: vertex_buffer, - index_buffer: index_buffer, - normal_buffer: normal_buffer, - }) - } -} - -impl Geometry for CatmullRomCurve { - fn handle(&self) -> RTCGeometry { - self.handle - } -} - -impl Drop for CatmullRomCurve { - fn drop(&mut self) { - unsafe { - rtcReleaseGeometry(self.handle); - } - } -} diff --git a/src/context.rs b/src/context.rs new file mode 100644 index 000000000..92ac23a68 --- /dev/null +++ b/src/context.rs @@ -0,0 +1,173 @@ +use crate::sys::*; + +/// Trait for extended intersection context enabling passing of additional +/// ray-query specific data. +/// +/// # Safety +/// +/// Structs that implement this trait must guarantee that they are +/// layout-compatible with [`IntersectContext`] (i.e. pointer casts between the +/// two types are valid). The corresponding pattern in C is called poor man's +/// inheritance. See [`IntersectContextExt`] for an example of how to do this +pub unsafe trait AsIntersectContext { + type Ext; + + fn as_context(&self) -> &IntersectContext; + fn as_mut_context(&mut self) -> &mut IntersectContext; + + fn as_context_ptr(&self) -> *const IntersectContext { + self.as_context() as *const IntersectContext + } + + fn as_mut_context_ptr(&mut self) -> *mut IntersectContext { + self.as_mut_context() as *mut IntersectContext + } + + fn as_extended(&self) -> Option<&Self::Ext>; + fn as_mut_extended(&mut self) -> Option<&mut Self::Ext>; +} + +/// Per ray-query intersection context. +/// +/// This is used to configure intersection flags, specify a filter callback +/// function, and specify the chain of IDs of the current instance, and to +/// attach arbitrary user data to the query (e.g. per ray data). +/// +/// # Filter Callback +/// +/// A filter function can be specified inside the context. This function is +/// invoked as a second filter stage after the per-geometry intersect or +/// occluded filter function is invoked. Only rays that passed the first filter +/// stage are valid in this second filter stage. Having such a per ray-query +/// filter function can be useful to implement modifications of the behavior of +/// the query, such as collecting all hits or accumulating transparencies. +/// +/// ## Note +/// +/// The support for the context filter function must be enabled for a scene by +/// using the [`SceneFlags::CONTEXT_FILTER_FUNCTION`](`crate::SceneFlags::CONTEXT_FILTER_FUNCTION`) flag. +/// +/// In case of instancing this feature has to get enabled also for each +/// instantiated scene. +/// +/// # Hints +/// +/// Best primary ray performance can be obtained by using the ray stream API +/// and setting the intersect context flag to +/// [`IntersectContextFlags::COHERENT`](`crate::IntersectContextFlags`). For +/// secondary rays, it is typically better to use the +/// [`IntersectContextFlags::INCOHERENT`](`crate::IntersectContextFlags::INCOHERENT`), +/// unless the rays are known to be coherent(e.g. for primary transparency +/// rays). +pub type IntersectContext = RTCIntersectContext; + +impl IntersectContext { + /// Shortcut to create a IntersectContext with coherent flag set. + pub fn coherent() -> IntersectContext { + IntersectContext::new(RTCIntersectContextFlags::COHERENT) + } + + /// Shortcut to create a IntersectContext with incoherent flag set. + pub fn incoherent() -> IntersectContext { + IntersectContext::new(RTCIntersectContextFlags::INCOHERENT) + } + + pub fn new(flags: RTCIntersectContextFlags) -> IntersectContext { + RTCIntersectContext { + flags, + filter: None, + instID: [u32::MAX; 1], + } + } +} + +unsafe impl AsIntersectContext for IntersectContext { + type Ext = (); + + fn as_context(&self) -> &IntersectContext { self } + + fn as_mut_context(&mut self) -> &mut IntersectContext { self } + + fn as_extended(&self) -> Option<&Self::Ext> { None } + + fn as_mut_extended(&mut self) -> Option<&mut Self::Ext> { None } +} + +/// Extended intersection context with additional ray query specific data. +/// +/// As Embree 3 does not support placing additional data at the end of the ray +/// structure, and accessing that data inside user geometry callbacks and filter +/// callback functions, we have to attach the data to the ray query context. +#[repr(C)] +#[derive(Debug)] +pub struct IntersectContextExt +where + E: Sized, +{ + pub ctx: IntersectContext, + pub ext: E, +} + +impl Clone for IntersectContextExt +where + E: Sized + Clone, +{ + fn clone(&self) -> Self { + IntersectContextExt { + ctx: self.ctx, + ext: self.ext.clone(), + } + } +} + +impl Copy for IntersectContextExt where E: Sized + Copy {} + +unsafe impl AsIntersectContext for IntersectContextExt +where + E: Sized, +{ + type Ext = E; + + fn as_context(&self) -> &IntersectContext { &self.ctx } + + fn as_mut_context(&mut self) -> &mut IntersectContext { &mut self.ctx } + + fn as_extended(&self) -> Option<&Self::Ext> { Some(&self.ext) } + + fn as_mut_extended(&mut self) -> Option<&mut Self::Ext> { Some(&mut self.ext) } +} + +impl IntersectContextExt +where + E: Sized, +{ + pub fn new(flags: RTCIntersectContextFlags, extra: E) -> IntersectContextExt { + IntersectContextExt { + ctx: IntersectContext::new(flags), + ext: extra, + } + } + + pub fn coherent(extra: E) -> IntersectContextExt { + IntersectContextExt { + ctx: IntersectContext::coherent(), + ext: extra, + } + } + + pub fn incoherent(extra: E) -> IntersectContextExt { + IntersectContextExt { + ctx: IntersectContext::incoherent(), + ext: extra, + } + } +} + +/// A stack which stores the IDs and instance transformations during a BVH +/// traversal for a point query. +/// +/// The transformations are assumed to be affine transformations +/// (3×3 matrix plus translation) and therefore the last column is ignored. +pub type PointQueryContext = RTCPointQueryContext; + +// TODO: PointQueryContext::new diff --git a/src/curve.rs b/src/curve.rs deleted file mode 100644 index f55237965..000000000 --- a/src/curve.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub enum CurveType { - Flat, - NormalOriented, - Round, - Cone, -} diff --git a/src/device.rs b/src/device.rs index afc34726f..2d50ad7b4 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,36 +1,49 @@ -use std::ffi::CString; -use std::fmt::{Display, Error, Formatter}; -use std::ptr; -use std::sync::Arc; - -use crate::sys::*; - +use crate::{ + sys::*, Buffer, BufferSize, Bvh, DeviceProperty, Error, Geometry, GeometryKind, Scene, + SceneFlags, +}; +use std::{ + ffi::CString, + fmt::{self, Display, Formatter}, + ptr, +}; + +/// Handle to an Embree device. +#[derive(Debug)] pub struct Device { pub(crate) handle: RTCDevice, } -impl Device { - pub fn new() -> Arc { - enable_ftz_and_daz(); - Arc::new(Device { - handle: unsafe { rtcNewDevice(ptr::null()) }, - }) +impl Clone for Device { + fn clone(&self) -> Self { + unsafe { rtcRetainDevice(self.handle) } + Self { + handle: self.handle, + } } +} + +impl Drop for Device { + fn drop(&mut self) { + unsafe { + rtcReleaseDevice(self.handle); + } + } +} - pub fn debug() -> Arc { +unsafe impl Sync for Device {} + +impl Device { + pub fn new() -> Result { create_device(None) } + + pub fn debug() -> Result { let cfg = CString::new("verbose=4").unwrap(); - enable_ftz_and_daz(); - Arc::new(Device { - handle: unsafe { rtcNewDevice(cfg.as_ptr()) }, - }) + create_device(Some(cfg)) } - pub fn with_config(config: Config) -> Arc { - enable_ftz_and_daz(); + pub fn with_config(config: Config) -> Result { let cfg = config.to_c_string(); - Arc::new(Device { - handle: unsafe { rtcNewDevice(cfg.as_ptr()) }, - }) + create_device(Some(cfg)) } /// Register a callback function to be called when an error occurs. @@ -44,18 +57,20 @@ impl Device { /// /// # Arguments /// - /// * `error_fn` - A callback function that takes an error code and a message. + /// * `error_fn` - A callback function that takes an error code and a + /// message. /// - /// When the callback function is invoked, it gets the error code of the occurred - /// error, as well as a message of type `&'static str` that further describes the error. + /// When the callback function is invoked, it gets the error code of the + /// occurred error, as well as a message of type `&'static str` that + /// further describes the error. /// /// # Example /// /// ```no_run /// use embree::Device; - /// let device = Device::new(); + /// let device = Device::new().unwrap(); /// device.set_error_function(|error, msg| { - /// println!("Error: {:?} {}", error, msg); + /// println!("Error: {:?} {}", error, msg); /// }); /// ``` pub fn set_error_function(&self, error_fn: F) @@ -66,7 +81,7 @@ impl Device { unsafe { rtcSetDeviceErrorFunction( self.handle, - Some(crate::callback::error_function_helper(&mut closure)), + error_function(&mut closure), &mut closure as *mut _ as *mut ::std::os::raw::c_void, ); } @@ -81,42 +96,49 @@ impl Device { /// Register a callback function to track memory consumption of the device. /// - /// Only a single callback function can be registered per device, and further invocations - /// overwrite the previously registered callback. + /// Only a single callback function can be registered per device, and + /// further invocations overwrite the previously registered callback. /// - /// Once registered, the Embree device will invoke the callback function before or after - /// it allocates or frees important memory blocks. The callback function might get called - /// from multiple threads concurrently. + /// Once registered, the Embree device will invoke the callback function + /// before or after it allocates or frees important memory blocks. The + /// callback function might get called from multiple threads + /// concurrently. /// /// Unregister with [`Device::unset_memory_monitor_function`]. /// /// # Arguments + /// /// * `monitor_fn` - A callback function that takes two arguments: - /// * `bytes: isize` - The number of bytes allocated or deallocated - /// (> 0 for allocations and < 0 for deallocations). The Embree `Device` - /// atomically accumulating `bytes` input parameter. - /// * `post: bool` - Whether the callback is invoked after the allocation or deallocation took place. /// - /// Embree will continue its operation normally when the callback function returns `true`. If `false` - /// returned, Embree will cancel the current operation with `RTC_ERROR_OUT_OF_MEMORY` error code. - /// Issuing multiple cancel requests from different threads is allowed. Cancelling will only happen when - /// the callback was called for allocations (bytes > 0), otherwise the cancel request will be ignored. + /// * `bytes: isize` - The number of bytes allocated or deallocated (> 0 for + /// allocations and < 0 for deallocations). The Embree `Device` atomically + /// accumulating `bytes` input parameter. + /// * `post: bool` - Whether the callback is invoked after the allocation or + /// deallocation took place. + /// + /// Embree will continue its operation normally when the callback function + /// returns `true`. If `false` returned, Embree will cancel the current + /// operation with `RTC_ERROR_OUT_OF_MEMORY` error code. + /// Issuing multiple cancel requests from different threads is allowed. + /// Cancelling will only happen when the callback was called for + /// allocations (bytes > 0), otherwise the cancel request will be ignored. /// - /// If a callback to cancel was invoked before the allocation happens (`post == false`), then - /// the `bytes` parameter should not be accumulated, as the allocation will never happen. - /// If the callback to cancel was invoked after the allocation happened (`post == true`), then - /// the `bytes` parameter should be accumulated, as the allocation properly happened and a - /// deallocation will later free that data block. + /// If a callback to cancel was invoked before the allocation happens (`post + /// == false`), then the `bytes` parameter should not be accumulated, as + /// the allocation will never happen. If the callback to cancel was + /// invoked after the allocation happened (`post == true`), then + /// the `bytes` parameter should be accumulated, as the allocation properly + /// happened and a deallocation will later free that data block. /// /// # Example /// ```no_run /// use embree::Device; - /// let device = Device::new(); + /// let device = Device::new().unwrap(); /// device.set_memory_monitor_function(|bytes, post| { /// if bytes > 0 { - /// println!("allocated {} bytes", bytes); + /// println!("allocated {} bytes", bytes); /// } else { - /// println!("deallocated {} bytes", -bytes); + /// println!("deallocated {} bytes", -bytes); /// }; /// true /// }); @@ -129,9 +151,7 @@ impl Device { unsafe { rtcSetDeviceMemoryMonitorFunction( self.handle, - Some(crate::callback::memory_monitor_function_helper( - &mut closure, - )), + memory_monitor_function(&mut closure), &mut closure as *mut _ as *mut ::std::os::raw::c_void, ); } @@ -148,40 +168,62 @@ impl Device { /// /// # Arguments /// - /// * `prop` - The property to query. See `RTCDeviceProp` for possible values. + /// * `prop` - The property to query. See `RTCDeviceProp` for possible + /// values. /// /// # Returns /// /// An integer of type `isize`. - pub fn query_property(&self, prop: RTCDeviceProperty) -> isize { - unsafe { rtcGetDeviceProperty(self.handle, prop) } + pub fn get_property(&self, prop: DeviceProperty) -> Result { + let ret = unsafe { rtcGetDeviceProperty(self.handle, prop) }; + if ret == 0 { + Err(self.get_error()) + } else { + Ok(ret) + } } /// Query the error code of the device. /// - /// Each thread has its own error code per device. If an error occurs when calling - /// an API function, this error code is set to the occurred error if it stores no - /// previous error. The `error_code` function reads and returns the currently stored - /// error and clears the error code. This assures that the returned error code is - /// always the first error occurred since the last invocation of `error_code`. + /// Each thread has its own error code per device. If an error occurs when + /// calling an API function, this error code is set to the occurred + /// error if it stores no previous error. The [`Device::get_error`] function + /// reads and returns the currently stored error and clears the error + /// code. This assures that the returned error code is always the first + /// error occurred since the last invocation of [`Device::get_error`]. /// /// # Returns /// - /// Error code encoded as `RTCError`. - pub fn error_code(&self) -> RTCError { - unsafe { rtcGetDeviceError(self.handle) } + /// Error code encoded as [`Error`]. + pub fn get_error(&self) -> RTCError { unsafe { rtcGetDeviceError(self.handle) } } + + /// Creates a new scene bound to the device. + pub fn create_scene(&self) -> Result, Error> { Scene::new(self.clone()) } + + /// Creates a new scene bound to the device with the given configuration. + /// It's the same as calling [`Device::create_scene`] and then + /// [`Scene::set_flags`]. + /// + /// See [`SceneFlags`] for possible values. + pub fn create_scene_with_flags(&self, flags: SceneFlags) -> Result { + Scene::new_with_flags(self.clone(), flags) } -} -impl Drop for Device { - fn drop(&mut self) { - unsafe { - rtcReleaseDevice(self.handle); - } + /// Creates a new [`Bvh`] object. + pub fn create_bvh(&self) -> Result { Bvh::new(self) } + + /// Creates a new data buffer. + /// The created buffer is always aligned to 16 bytes. + pub fn create_buffer(&self, size: usize) -> Result { + Buffer::new(self, BufferSize::new(size).unwrap()) } -} -unsafe impl Sync for Device {} + /// Creates a [`Geometry`] object bound to the device without any + /// buffers attached. + pub fn create_geometry<'a>(&self, kind: GeometryKind) -> Result, Error> { + Geometry::new(self, kind) + } +} /// Instruction Set Architecture. #[derive(Debug, Clone, Copy)] @@ -194,7 +236,7 @@ pub enum Isa { } impl Display for Isa { - fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { match self { Isa::Sse2 => write!(f, "sse2"), Isa::Sse4_2 => write!(f, "sse4.2"), @@ -219,7 +261,7 @@ pub enum FrequencyLevel { } impl Display for FrequencyLevel { - fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { match self { FrequencyLevel::Simd128 => write!(f, "simd128"), FrequencyLevel::Simd256 => write!(f, "simd256"), @@ -238,36 +280,41 @@ pub struct Config { /// scene commit using `rtcJoinCommitScene`. pub user_threads: u32, - /// Whether build threads are affinitized to hardware threads. This is disabled - /// by default on standard CPUs, and enabled by default on Xeon Phi Processors. + /// Whether build threads are affinitized to hardware threads. This is + /// disabled by default on standard CPUs, and enabled by default on Xeon + /// Phi Processors. pub set_affinity: bool, - /// When enabled, the build threads are started upfront. Useful for benchmarking - /// to exclude thread creation time. This is disabled by default. + /// When enabled, the build threads are started upfront. Useful for + /// benchmarking to exclude thread creation time. This is disabled by + /// default. pub start_threads: bool, /// ISA selection. By default the ISA is chosen automatically. pub isa: Option, - /// Configures the automated ISA selection to use maximally the specified ISA. + /// Configures the automated ISA selection to use maximally the specified + /// ISA. pub max_isa: Isa, - /// Enables or disables usage of huge pages. Enabled by default under Linux but - /// disabled by default on Windows and macOS. + /// Enables or disables usage of huge pages. Enabled by default under Linux + /// but disabled by default on Windows and macOS. pub hugepages: bool, - /// Enables or disables the SeLockMemoryPrivilege privilege which is required to - /// use huge pages on Windows. This option has only effect on Windows and is ignored - /// on other platforms. + /// Enables or disables the SeLockMemoryPrivilege privilege which is + /// required to use huge pages on Windows. This option has only effect + /// on Windows and is ignored on other platforms. pub enable_selockmemoryprivilege: bool, - /// Verbosity of the output [0, 1, 2, 3]. No output when set to 0. The higher the - /// level, the more the output. By default the output is set to 0. + /// Verbosity of the output [0, 1, 2, 3]. No output when set to 0. The + /// higher the level, the more the output. By default the output is set + /// to 0. pub verbose: u32, /// Frequency level the application want to run on. See [`FrequencyLevel`]. - /// When some frequency level is specified, Embree will avoid doing optimizations - /// that may reduce the frequency level below the level specified. + /// When some frequency level is specified, Embree will avoid doing + /// optimizations that may reduce the frequency level below the level + /// specified. pub frequency_level: Option, } @@ -283,8 +330,8 @@ impl Config { .map(|frequency_level| format!("frequency_level={}", frequency_level)) .unwrap_or_default(); let formated = format!( - "threads={},verbose={},set_affinity={},start_threads={},\ - max_isa={},hugepages={},enable_selockmemoryprivilege={},{}{}", + "threads={},verbose={},set_affinity={},start_threads={},max_isa={},hugepages={},\ + enable_selockmemoryprivilege={},{}{}", self.threads, self.verbose, self.set_affinity as u32, @@ -318,7 +365,7 @@ impl Default for Config { } /// Set the flush zero and denormals modes from Embrees's perf. recommendations -/// https://embree.github.io/api.html#performance-recommendations +/// ``. pub fn enable_ftz_and_daz() { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { @@ -334,3 +381,58 @@ pub fn enable_ftz_and_daz() { } } } + +/// Default error function. +fn default_error_function(error: Error, msg: &str) { + eprintln!("[Embree] {:?} - {}", error, msg); +} + +/// Helper function to create a new Embree device. +fn create_device(config: Option) -> Result { + enable_ftz_and_daz(); + let config = config.unwrap_or_else(|| Config::default().to_c_string()); + let handle = unsafe { rtcNewDevice(config.as_ptr()) }; + if handle.is_null() { + Err(unsafe { rtcGetDeviceError(ptr::null_mut()) }) + } else { + let device = Device { handle }; + device.set_error_function(default_error_function); + Ok(device) + } +} + +/// Helper function to convert a Rust closure to `RTCErrorFunction` callback. +fn error_function(_f: &mut F) -> RTCErrorFunction +where + F: FnMut(RTCError, &'static str), +{ + unsafe extern "C" fn inner( + f: *mut std::os::raw::c_void, + error: RTCError, + msg: *const std::os::raw::c_char, + ) where + F: FnMut(RTCError, &'static str), + { + let cb = &mut *(f as *mut F); + cb(error, std::ffi::CStr::from_ptr(msg).to_str().unwrap()) + } + + Some(inner::) +} + +/// Helper function to convert a Rust closure to `RTCMemoryMonitorFunction` +/// callback. +fn memory_monitor_function(_f: &mut F) -> RTCMemoryMonitorFunction +where + F: FnMut(isize, bool) -> bool, +{ + unsafe extern "C" fn inner(f: *mut std::os::raw::c_void, bytes: isize, post: bool) -> bool + where + F: FnMut(isize, bool) -> bool, + { + let cb = &mut *(f as *mut F); + cb(bytes, post) + } + + Some(inner::) +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 000000000..c06ac0078 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,30 @@ +use std::fmt::Display; + +use crate::sys::RTCError; + +impl Display for RTCError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + RTCError::NONE => write!(f, "No error occurred."), + RTCError::UNKNOWN => write!(f, "An unknown error has occurred."), + RTCError::INVALID_ARGUMENT => write!(f, "An invalid argument was specified."), + RTCError::INVALID_OPERATION => { + write!(f, "The operation is not allowed for the specified object.") + } + RTCError::OUT_OF_MEMORY => write!( + f, + "There is not enough memory left to complete the operation." + ), + RTCError::UNSUPPORTED_CPU => write!( + f, + "The CPU is not supported as it does not support the lowest ISA Embree is \ + compiled for." + ), + RTCError::CANCELLED => write!( + f, + "The operation got canceled by a memory monitor callback or progress monitor \ + callback function." + ), + } + } +} diff --git a/src/geometry.rs b/src/geometry.rs index 48f90ca01..10e274a55 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,20 +1,1369 @@ -use crate::callback; -use crate::sys::*; +use std::{ + any::TypeId, collections::HashMap, marker::PhantomData, num::NonZeroUsize, ptr, sync::Mutex, +}; -/// Geometry trait implemented by all Embree Geometry types -pub trait Geometry { - fn handle(&self) -> RTCGeometry; - fn commit(&mut self) { +use crate::{ + sys::*, AsIntersectContext, Bounds, BufferSlice, BufferUsage, BuildQuality, Device, Error, + Format, GeometryKind, HitN, QuaternionDecomposition, RayHitN, RayN, Scene, SubdivisionMode, +}; + +use std::{ + borrow::Cow, + ops::{Deref, DerefMut, Index, IndexMut}, + sync::Arc, +}; + +// TODO(yang): maybe enforce format and stride when get the view? +/// Information about how a (part of) buffer is bound to a geometry. +#[derive(Debug, Clone)] +pub(crate) struct AttachedBuffer<'src> { + slot: u32, + #[allow(dead_code)] + format: Format, + #[allow(dead_code)] + stride: usize, + source: BufferSlice<'src>, +} + +/// Trait for user-defined data that can be attached to a geometry. +pub trait UserGeometryData: Sized + Send + Sync + 'static {} + +impl UserGeometryData for T where T: Sized + Send + Sync + 'static {} + +/// User-defined data for a geometry. +/// +/// This contains the pointer to the user-defined data and the type ID of the +/// user-defined data (which is used to check the type when getting the data). +#[derive(Debug, Clone)] +pub(crate) struct GeometryUserData { + /// Pointer to the user-defined data. + pub data: *mut std::os::raw::c_void, + /// Type ID of the user-defined data. + pub type_id: TypeId, +} + +/// Payloads for user-defined callbacks of a geometry of kind +/// [`GeometryKind::USER`]. +#[derive(Debug, Clone)] +pub(crate) struct UserGeometryPayloads { + /// Payload for the [`Geometry::set_intersect_function`] call. + pub intersect_fn: *mut std::os::raw::c_void, + /// Payload for the [`Geometry::set_occluded_function`] call. + pub occluded_fn: *mut std::os::raw::c_void, + /// Payload for the [`Geometry::set_bounds_function`] call. + pub bounds_fn: *mut std::os::raw::c_void, +} + +/// Payloads for subdivision callbacks of a geometry of kind +/// [`GeometryKind::SUBDIVISION`]. +#[derive(Debug, Clone)] +pub(crate) struct SubdivisionGeometryPayloads { + /// Payload for the [`Geometry::set_displacement_function`] call. + pub displacement_fn: *mut std::os::raw::c_void, +} + +impl Default for UserGeometryPayloads { + fn default() -> Self { + Self { + intersect_fn: ptr::null_mut(), + occluded_fn: ptr::null_mut(), + bounds_fn: ptr::null_mut(), + } + } +} + +impl Default for SubdivisionGeometryPayloads { + fn default() -> Self { + Self { + displacement_fn: ptr::null_mut(), + } + } +} + +/// User-defined data for a geometry. +/// +/// This contains also the payloads for different callbacks, which makes it +/// possible to pass Rust closures to Embree. +#[derive(Debug, Clone)] +pub(crate) struct GeometryData { + /// User-defined data. + pub user_data: Option, + /// Payload for the [`Geometry::set_intersect_filter_function`] call. + pub intersect_filter_fn: *mut std::os::raw::c_void, + /// Payload for the [`Geometry::set_occluded_filter_function`] call. + pub occluded_filter_fn: *mut std::os::raw::c_void, + /// Payloads only used for user geometry. + pub user_fns: Option, + /// Payloads only used for subdivision geometry. + pub subdivision_fns: Option, +} + +impl Default for GeometryData { + fn default() -> Self { + Self { + user_data: None, + intersect_filter_fn: ptr::null_mut(), + occluded_filter_fn: ptr::null_mut(), + user_fns: None, + subdivision_fns: None, + } + } +} + +/// Wrapper around an Embree geometry object. +/// +/// A new geometry is created using [`Device::create_geometry`] or +/// new methods of different geometry types. Depending on the geometry type, +/// different buffers must be bound (e.g. using [`Geometry::set_buffer`]) to set +/// up the geometry data. In most cases, binding of a vertex and index buffer is +/// required. The number of primitives and vertices of that geometry is +/// typically inferred from the size of these bound buffers. +/// +/// Changes to the geometry always must be committed using the +/// [`Geometry::commit`] call before using the geometry. After committing, a +/// geometry is not yet included in any scene. A geometry can be added to a +/// scene by using the [`Scene::attach_geometry`](crate::Scene::attach_geometry) +/// function (to automatically assign a geometry ID) or using the +/// [`Scene::attach_geometry_by_id`](crate::Scene::attach_geometry_by_id) +/// function (to specify the geometry ID manually). A geometry can get attached +/// to multiple scenes. +/// +/// All geometry types support multi-segment motion blur with an arbitrary +/// number of equidistant time steps (in the range of 2 to 129) inside a user +/// specified time range. Each geometry can have a different number of time +/// steps and a different time range. The motion blur geometry is defined by +/// linearly interpolating the geometries of neighboring time steps. To +/// construct a motion blur geometry, first the number of time steps of the +/// geometry must be specified using [`Geometry::set_time_step_count`], and then +/// a vertex buffer for each time step must be bound, e.g. using the +/// [`Geometry::set_buffer`] function. Optionally, a time range defining the +/// start (and end time) of the first (and last) time step can be set using the +/// rtcSetGeometryTimeRange function. This feature will also allow geometries to +/// appear and disappear during the camera shutter time if the time range is a +/// sub range of \[0,1\]. +/// +/// The API supports per-geometry filter callback functions (see +/// [`Geometry::set_intersect_filter_function`] +/// and set_occluded_filter_function) that are invoked for each intersection +/// found during the Scene::intersect or Scene::occluded calls. The former ones +/// are called geometry intersection filter functions, the latter ones geometry +/// occlusion filter functions. These filter functions are designed to be used +/// to ignore intersections outside of a user- defined silhouette of a +/// primitive, e.g. to model tree leaves using transparency textures +/// +/// It does not own the buffers that are bound to it, but it does own the +/// geometry object itself. +#[derive(Debug)] +pub struct Geometry<'buf> { + pub(crate) device: Device, + pub(crate) handle: RTCGeometry, + kind: GeometryKind, + /// Buffers that are attached to this geometry. + attachments: Arc>>>>, + /// Data associated with this geometry. + data: Arc>, +} + +impl<'buf> Clone for Geometry<'buf> { + fn clone(&self) -> Self { + unsafe { + rtcRetainGeometry(self.handle); + } + Self { + device: self.device.clone(), + handle: self.handle, + kind: self.kind, + attachments: self.attachments.clone(), + data: self.data.clone(), + } + } +} + +impl<'buf> Drop for Geometry<'buf> { + fn drop(&mut self) { + unsafe { + rtcReleaseGeometry(self.handle); + } + } +} + +impl<'buf> Geometry<'buf> { + /// Creates a new geometry object. + /// + /// # Examples + /// + /// ```no_run + /// use embree::{Device, Geometry, GeometryKind}; + /// + /// let device = Device::new().unwrap(); + /// let geometry = Geometry::new(&device, GeometryKind::TRIANGLE).unwrap(); + /// ``` + /// + /// or use the [`Device::create_geometry`] method: + /// + /// ```no_run + /// use embree::{Device, GeometryKind}; + /// + /// let device = Device::new().unwrap(); + /// let geometry = device.create_geometry(GeometryKind::TRIANGLE).unwrap(); + /// ``` + pub fn new<'dev>(device: &'dev Device, kind: GeometryKind) -> Result, Error> { + let handle = unsafe { rtcNewGeometry(device.handle, kind) }; + if handle.is_null() { + Err(device.get_error()) + } else { + let data = Arc::new(Mutex::new(GeometryData { + user_data: None, + intersect_filter_fn: ptr::null_mut(), + occluded_filter_fn: ptr::null_mut(), + user_fns: if kind == GeometryKind::USER { + Some(UserGeometryPayloads { + intersect_fn: ptr::null_mut(), + occluded_fn: ptr::null_mut(), + bounds_fn: ptr::null_mut(), + }) + } else { + None + }, + subdivision_fns: if kind == GeometryKind::SUBDIVISION { + Some(SubdivisionGeometryPayloads { + displacement_fn: ptr::null_mut(), + }) + } else { + None + }, + })); + unsafe { + rtcSetGeometryUserData( + handle, + Arc::into_raw(data.clone()) as *mut std::os::raw::c_void, + ); + } + Ok(Geometry { + device: device.clone(), + handle, + kind, + attachments: Arc::new(Mutex::new(HashMap::default())), + data, + }) + } + } + + /// Disables the geometry. + /// + /// A disabled geometry is not rendered. Each geometry is enabled by + /// default at construction time. + /// After disabling a geometry, the scene containing that geometry must + /// be committed using rtcCommitScene for the change to have effect. + pub fn disable(&self) { + unsafe { + rtcDisableGeometry(self.handle); + } + } + + /// Enables the geometry. + /// + /// Only enabled geometries are rendered. Each geometry is enabled by + /// default at construction time. + /// + /// After enabling a geometry, the scene containing that geometry must be + /// committed using [`Geometry::commit`] for the change to have effect. + pub fn enable(&self) { + unsafe { + rtcEnableGeometry(self.handle); + } + } + + /// Returns the raw Embree geometry handle. + /// + /// # Safety + /// + /// Use this function only if you know what you are doing. The returned + /// handle is a raw pointer to an Embree reference-counted object. The + /// reference count is not increased by this function, so the caller must + /// ensure that the handle is not used after the geometry object is + /// destroyed. + pub unsafe fn handle(&self) -> RTCGeometry { self.handle } + + /// Checks if the vertex attribute is allowed for the geometry. + /// + /// This function do not check if the slot of the vertex attribute. + fn check_vertex_attribute(&self) -> Result<(), Error> { + match self.kind { + GeometryKind::GRID | GeometryKind::USER | GeometryKind::INSTANCE => { + eprint!( + "Vertex attribute not allowed for geometries of type {:?}!", + self.kind + ); + Err(Error::INVALID_OPERATION) + } + _ => Ok(()), + } + } + + /// Binds a view of a buffer to the geometry. + /// + /// The buffer must be valid for the lifetime of the geometry. The buffer is + /// provided as a [`BufferSlice`], which is a view into a buffer object. + /// See the documentation of [`BufferSlice`] for more information. + /// + /// Under the hood, function call [`rtcSetGeometryBuffer`] is used to bind + /// [`BufferSlice::Buffer`] or [`BufferSlice::GeometryLocal`] to the + /// geometry, and [`rtcSetSharedGeometryBuffer`] is used to bind + /// [`BufferSlice::User`]. + /// + /// # Arguments + /// + /// * `usage` - The usage of the buffer. + /// + /// * `slot` - The slot to bind the buffer to. If the provided slot is + /// already bound to a buffer, + /// the old bound buffer will be overwritten with the new one. + /// + /// * `format` - The format of the buffer. + /// + /// * `slice` - The buffer slice to bind. + /// + /// * `stride` - The stride of the elements in the buffer. Must be a + /// multiple of 4. + /// + /// * `count` - The number of elements in the buffer. + pub fn set_buffer<'a>( + &'a mut self, + usage: BufferUsage, + slot: u32, + format: Format, + slice: BufferSlice<'buf>, + stride: usize, + count: usize, + ) -> Result<(), Error> { + debug_assert!(stride % 4 == 0, "Stride must be a multiple of 4!"); + if usage == BufferUsage::VERTEX { + self.check_vertex_attribute()?; + } + match slice { + BufferSlice::Buffer { + buffer, + offset, + size, + } => { + dbg!( + "Binding buffer slice to slot {}, offset {}, stride {}, count {}", + slot, + offset, + stride, + count + ); + let mut attachments = self.attachments.lock().unwrap(); + let bindings = attachments.entry(usage).or_insert_with(Vec::new); + match bindings.iter().position(|a| a.slot == slot) { + // If the slot is already bound, remove the old binding and + // replace it with the new one. + Some(i) => { + bindings.remove(i); + unsafe { + rtcSetGeometryBuffer( + self.handle, + usage, + slot, + format, + buffer.handle, + offset, + stride, + count, + ) + }; + bindings.push(AttachedBuffer { + slot, + source: BufferSlice::Buffer { + buffer, + offset, + size, + }, + format, + stride, + }); + Ok(()) + } + // If the slot is not bound, just bind the new buffer. + None => { + unsafe { + rtcSetGeometryBuffer( + self.handle, + usage, + slot, + format, + buffer.handle, + offset, + stride, + count, + ) + }; + bindings.push(AttachedBuffer { + slot, + source: BufferSlice::Buffer { + buffer, + offset, + size, + }, + format, + stride, + }); + Ok(()) + } + } + } + BufferSlice::GeometryLocal { .. } => { + eprint!("Sharing geometry local buffer is not allowed!"); + Err(Error::INVALID_ARGUMENT) + } + BufferSlice::User { + ptr, offset, size, .. + } => { + let mut attachments = self.attachments.lock().unwrap(); + let bindings = attachments.entry(usage).or_insert_with(Vec::new); + match bindings.iter().position(|a| a.slot == slot) { + // If the slot is already bound, remove the old binding and + // replace it with the new one. + Some(i) => { + bindings.remove(i); + unsafe { + rtcSetSharedGeometryBuffer( + self.handle, + usage, + slot, + format, + ptr.add(offset) as *mut _, + offset, + stride, + count, + ); + }; + bindings.push(AttachedBuffer { + slot, + source: BufferSlice::User { + ptr, + offset, + size, + marker: PhantomData, + }, + format, + stride, + }); + Ok(()) + } + // If the slot is not bound, just bind the new buffer. + None => { + unsafe { + rtcSetSharedGeometryBuffer( + self.handle, + usage, + slot, + format, + ptr.add(offset) as *mut _, + offset, + stride, + count, + ); + }; + bindings.push(AttachedBuffer { + slot, + source: BufferSlice::User { + ptr, + offset, + size, + marker: PhantomData, + }, + format, + stride, + }); + Ok(()) + } + } + } + } + } + + /// Creates a new [`Buffer`](`crate::Buffer`) and binds it as a specific + /// attribute for this geometry. + /// + /// Analogous to [`rtcSetNewGeometryBuffer`](https://spec.oneapi.io/oneart/0.5-rev-1/embree-spec.html#rtcsetnewgeometrybuffer). + /// + /// The allocated buffer will be automatically over-allocated slightly when + /// used as a [`BufferUsage::VERTEX`] buffer, where a requirement is + /// that each buffer element should be readable using 16-byte SSE load + /// instructions. This means that the buffer will be padded to a multiple of + /// 16 bytes. + /// + /// The allocated buffer is managed internally and automatically released + /// when the geometry is destroyed by Embree. + /// + /// # Arguments + /// + /// * `usage` - The usage of the buffer. + /// + /// * `slot` - The slot to bind the buffer to. + /// + /// * `format` - The format of the buffer items. See [`Format`] for more + /// information. + /// + /// * `count` - The number of items in the buffer. + /// + /// * `stride` - The stride of the buffer items. MUST be a multiple of 4. + pub fn set_new_buffer( + &mut self, + usage: BufferUsage, + slot: u32, + format: Format, + stride: usize, + count: usize, + ) -> Result, Error> { + debug_assert!(stride % 4 == 0, "Stride must be a multiple of 4!"); + if usage == BufferUsage::VERTEX_ATTRIBUTE { + self.check_vertex_attribute()?; + } + { + let mut attachments = self.attachments.lock().unwrap(); + let bindings = attachments.entry(usage).or_insert_with(Vec::new); + if !bindings.iter().any(|a| a.slot == slot) { + let raw_ptr = unsafe { + rtcSetNewGeometryBuffer(self.handle, usage, slot, format, stride, count) + }; + if raw_ptr.is_null() { + Err(self.device.get_error()) + } else { + let slice = BufferSlice::GeometryLocal { + ptr: raw_ptr, + size: NonZeroUsize::new(count * stride).unwrap(), + marker: PhantomData, + }; + bindings.push(AttachedBuffer { + slot, + source: slice, + format, + stride, + }); + Ok(slice) + } + } else { + eprint!("Buffer already attached to slot {}", slot); + Err(Error::INVALID_ARGUMENT) + } + } + } + + /// Returns the buffer bound to the given slot and usage. + pub fn get_buffer(&self, usage: BufferUsage, slot: u32) -> Option { + let attachments = self.attachments.lock().unwrap(); + attachments + .get(&usage) + .and_then(|v| v.iter().find(|a| a.slot == slot)) + .map(|a| a.source) + } + + /// Marks a buffer slice bound to this geometry as modified. + /// + /// If a data buffer is changed by the application, this function must be + /// called for the buffer to be updated in the geometry. Each buffer slice + /// assigned to a buffer slot is initially marked as modified, thus this + /// method needs to be called only when doing buffer modifications after the + /// first [`Scene::commit`] call. + pub fn update_buffer(&self, usage: BufferUsage, slot: u32) { + unsafe { + rtcUpdateGeometryBuffer(self.handle, usage, slot); + } + } + + /// Returns the type of geometry of this geometry. + pub fn kind(&self) -> GeometryKind { self.kind } + + pub fn commit(&mut self) { + unsafe { + rtcCommitGeometry(self.handle); + } + } + + /// Sets the build quality for the geometry. + /// + /// The per-geometry build quality is only a hint and may be ignored. Embree + /// currently uses the per-geometry build quality when the scene build + /// quality is set to [`BuildQuality::LOW`]. In this mode a two-level + /// acceleration structure is build, and geometries build a separate + /// acceleration structure using the geometry build quality. + /// + /// The build quality can be one of the following: + /// + /// - [`BuildQuality::LOW`]: Creates lower quality data structures, e.g. for + /// dynamic scenes. + /// + /// - [`BuildQuality::MEDIUM`]: Default build quality for most usages. Gives + /// a good balance between quality and performance. + /// + /// - [`BuildQuality::HIGH`]: Creates higher quality data structures for + /// final frame rendering. Enables a spatial split builder for certain + /// primitive types. + /// + /// - [`BuildQuality::REFIT`]: Uses a BVH refitting approach when changing + /// only the vertex buffer. + pub fn set_build_quality(&mut self, quality: BuildQuality) { + unsafe { + rtcSetGeometryBuildQuality(self.handle, quality); + } + } + + /// Registers an intersection filter callback function for the geometry. + /// + /// Only a single callback function can be registered per geometry, and + /// further invocations overwrite the previously set callback function. + /// Unregister the callback function by calling + /// [`Geometry::unset_intersect_filter_function`]. + /// + /// The registered filter function is invoked for every hit encountered + /// during the intersect-type ray queries and can accept or reject that + /// hit. The feature can be used to define a silhouette for a primitive + /// and reject hits that are outside the silhouette. E.g. a tree leaf + /// could be modeled with an alpha texture that decides whether hit + /// points lie inside or outside the leaf. + /// + /// If [`BuildQuality::HIGH`] is set, the filter functions may be called + /// multiple times for the same primitive hit. Further, rays hitting + /// exactly the edge might also report two hits for the same surface. For + /// certain use cases, the application may have to work around this + /// limitation by collecting already reported hits (geomID/primID pairs) + /// and ignoring duplicates. + /// + /// The filter function callback of type [`RTCFilterFunctionN`] gets passed + /// a number of arguments through the [`RTCFilterFunctionNArguments`] + /// structure. The valid parameter of that structure points to an + /// integer valid mask (0 means invalid and -1 means valid). The + /// `geometryUserPtr` member is a user pointer optionally set per + /// geometry through the [`Geometry::set_user_data`] function. The + /// context member points to the intersection context passed to + /// the ray query function. The ray parameter points to N rays in SOA layout + /// (see `RayN`, `HitN`). + /// The hit parameter points to N hits in SOA layout to test. The N + /// parameter is the number of rays and hits in ray and hit. The hit + /// distance is provided as the tfar value of the ray. If the hit + /// geometry is instanced, the `instID` member of the ray is valid, and + /// the ray and the potential hit are in object space. + /// + /// The filter callback function has the task to check for each valid ray + /// whether it wants to accept or reject the corresponding hit. To + /// reject a hit, the filter callback function just has to *write 0* to + /// the integer valid mask of the corresponding ray. To accept the hit, + /// it just has to *leave the valid mask set to -1*. The filter function + /// is further allowed to change the hit and decrease the tfar value of the + /// ray but it should not modify other ray data nor any inactive + /// components of the ray or hit. + /// + /// When performing ray queries using [`Scene::intersect`], it is + /// *guaranteed* that the packet size is 1 when the callback is invoked. + /// When performing ray queries using the [`Scene::intersect4/8/16`] + /// functions, it is not generally guaranteed that the ray packet size + /// (and order of rays inside the packet) passed to the callback matches + /// the initial ray packet. However, under some circumstances these + /// properties are guaranteed, and whether this is the case can be + /// queried using [`Device::get_property`]. When performing ray queries + /// using the stream API such as [`Scene::intersect_stream_aos`], + /// [`Scene::intersect_stream_soa`], the order of rays and ray packet size + /// of the callback function might change to either 1, 4, 8, or 16. + /// + /// For many usage scenarios, repacking and re-ordering of rays does not + /// cause difficulties in implementing the callback function. However, + /// algorithms that need to extend the ray with additional data must use + /// the rayID component of the ray to identify the original ray to + /// access the per-ray data. + pub fn set_intersect_filter_function(&mut self, filter: F) + where + D: UserGeometryData, + C: AsIntersectContext, + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidityN<'a>, &mut C, Option<&mut D>), + { + let mut geom_data = self.data.lock().unwrap(); unsafe { - rtcCommitGeometry(self.handle()); + let mut closure = filter; + geom_data.intersect_filter_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; + rtcSetGeometryIntersectFilterFunction( + self.handle, + intersect_filter_function(&mut closure), + ); + } + } + + /// Unsets the intersection filter function for the geometry. + pub fn unset_intersect_filter_function(&mut self) { + unsafe { + rtcSetGeometryIntersectFilterFunction(self.handle, None); + } + } + + /// Sets the occlusion filter for the geometry. + /// + /// Only a single callback function can be registered per geometry, and + /// further invocations overwrite the previously set callback function. + /// Unregister the callback function by calling + /// [`Geometry::unset_occluded_filter_function`]. + /// + /// The registered intersection filter function is invoked for every hit + /// encountered during the occluded-type ray queries and can accept or + /// reject that hit. + /// + /// The feature can be used to define a silhouette for a primitive and + /// reject hits that are outside the silhouette. E.g. a tree leaf could + /// be modeled with an alpha texture that decides whether hit points lie + /// inside or outside the leaf. Please see the description of the + /// [`Geometry::set_intersect_filter_function`] for a description of the + /// filter callback function. + pub fn set_occluded_filter_function(&mut self, filter: F) + where + D: UserGeometryData, + C: AsIntersectContext, + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidityN<'a>, &mut C, Option<&mut D>), + { + let mut geom_data = self.data.lock().unwrap(); + unsafe { + let mut closure = filter; + geom_data.occluded_filter_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; + rtcSetGeometryOccludedFilterFunction( + self.handle, + occluded_filter_function(&mut closure), + ); + } + } + + /// Unsets the occlusion filter function for the geometry. + pub fn unset_occluded_filter_function(&mut self) { + unsafe { + rtcSetGeometryOccludedFilterFunction(self.handle, None); + } + } + + // TODO(yang): how to handle the closure? RTCPointQueryFunctionArguments has a + // user pointer but we can't set it here, instead we can only set it in the + // rtcPointQuery function which is attached to the scene. This requires the + // user to call [`Scene::point_query`] first and then call + // [`Geometry::set_point_query_function`] to set the closure. Or we can + // make the closure a member of the [`GeometryData`] and set it here. + + /// Sets the point query callback function for a geometry. + /// + /// Only a single callback function can be registered per geometry and + /// further invocations overwrite the previously set callback function. + /// Unregister the callback function by calling + /// [`Geometry::unset_point_query_function`]. + /// + /// The registered callback function is invoked by rtcPointQuery for every + /// primitive of the geometry that intersects the corresponding point query + /// domain. The callback function of type `RTCPointQueryFunction` gets + /// passed a number of arguments through the + /// `RTCPointQueryFunctionArguments` structure. The query object is the + /// original point query object passed into rtcPointQuery, us- + /// rPtr is an arbitrary pointer to pass input into and store results of the + /// callback function. The primID, geomID and context (see + /// rtcInitPointQueryContext for details) can be used to identify the + /// geometry data of the primitive. 122Embree API Reference + /// A RTCPointQueryFunction can also be passed directly as an argument to + /// rtcPointQuery. In this case the callback is invoked for all primitives + /// in the scene that intersect the query domain. If a callback function + /// is passed as an argument to rtcPointQuery and (a potentially + /// different) callback function is set for a ge- ometry with + /// rtcSetGeometryPointQueryFunction both callback functions are in- + /// voked and the callback function passed to rtcPointQuery will be called + /// before the geometry specific callback function. + /// If instancing is used, the parameter simliarityScale indicates whether + /// the current instance transform (top element of the stack in context) + /// is a similarity transformation or not. Similarity transformations + /// are composed of translation, rotation and uniform scaling and if a + /// matrix M defines a similarity transformation, there is a scaling + /// factor D such that for all x,y: dist(Mx, My) = D * dist(x, + /// y). In this case the parameter scalingFactor is this scaling factor D + /// and other- wise it is 0. A valid similarity scale (similarityScale > + /// 0) allows to compute distance information in instance space and + /// scale the distances into world space (for example, to update the + /// query radius, see below) by dividing the instance space distance + /// with the similarity scale. If the current instance transform is not + /// a similarity transform (similarityScale is 0), the distance computation + /// has to be performed in world space to ensure correctness. In this + /// case the instance to world transformations given with the context + /// should be used to transform the primitive data into world space. + /// Otherwise, the query location can be trans- formed into instance + /// space which can be more efficient. If there is no instance + /// transform, the similarity scale is 1. + /// The callback function will potentially be called for primitives outside + /// the query domain for two reasons: First, the callback is invoked for + /// all primitives inside a BVH leaf node since no geometry data of + /// primitives is determined internally and therefore individual + /// primitives are not culled (only their (aggregated) bounding boxes). + /// Second, in case non similarity transformations are used, the + /// resulting ellipsoidal query domain (in instance space) is approximated + /// by its axis aligned bounding box internally and therefore inner + /// nodes that do not intersect the original domain might intersect the + /// approximative bounding box which results in unnecessary callbacks. + /// In any case, the callbacks are conservative, i.e. if a primitive is + /// inside the query domain a callback will be invoked but the reverse + /// is not necessarily true. + /// For efficiency, the radius of the query object can be decreased (in + /// world space) inside the callback function to improve culling of + /// geometry during BVH traversal. If the query radius was updated, the + /// callback function should return true to issue an update of internal + /// traversal information. Increasing the radius or modifying + /// the time or position of the query results in undefined behaviour. + /// Within the callback function, it is safe to call rtcPointQuery again, + /// for ex- ample when implementing instancing manually. In this case + /// the instance trans- formation should be pushed onto the stack in + /// context. Embree will internally compute the point query information + /// in instance space using the top element of the stack in context when + /// rtcPointQuery is called. For a reference implementation of a closest + /// point traversal of triangle meshes using instancing and user defined + /// instancing see the tutorial *ClosestPoint*. + pub unsafe fn set_point_query_function(&mut self, query_fn: RTCPointQueryFunction) { + rtcSetGeometryPointQueryFunction(self.handle, query_fn); + } + + /// Unsets the point query function for the geometry. + pub fn unset_point_query_function(&mut self) { + unsafe { + rtcSetGeometryPointQueryFunction(self.handle, None); + } + } + + /// Sets the tessellation rate for a subdivision mesh or flat curves. + /// + /// For curves, the tessellation rate specifies the number of ray-facing + /// quads per curve segment. For subdivision surfaces, the tessellation + /// rate specifies the number of quads along each edge. + pub fn set_tessellation_rate(&mut self, rate: f32) { + match self.kind { + GeometryKind::SUBDIVISION + | GeometryKind::FLAT_LINEAR_CURVE + | GeometryKind::FLAT_BEZIER_CURVE + | GeometryKind::ROUND_LINEAR_CURVE + | GeometryKind::ROUND_BEZIER_CURVE => unsafe { + rtcSetGeometryTessellationRate(self.handle, rate); + }, + _ => panic!( + "Geometry::set_tessellation_rate is only supported for subdivision meshes and \ + flat curves" + ), + } + } + + /// Sets the mask for the geometry. + /// + /// This geometry mask is used together with the ray mask stored inside the + /// mask field of the ray. The primitives of the geometry are hit by the ray + /// only if the bitwise and operation of the geometry mask with the ray mask + /// is not 0. + /// This feature can be used to disable selected geometries for specifically + /// tagged rays, e.g. to disable shadow casting for certain geometries. + /// + /// Ray masks are disabled in Embree by default at compile time, and can be + /// enabled through the `EMBREE_RAY_MASK` parameter in CMake. One can query + /// whether ray masks are enabled by querying the + /// [`DeviceProperty::RAY_MASK_SUPPORTED`](`crate::DeviceProperty::RAY_MASK_SUPPORTED`) + /// device property using [`Device::get_property`]. + pub fn set_mask(&mut self, mask: u32) { + unsafe { + rtcSetGeometryMask(self.handle, mask); + } + } + + /// Sets the number of time steps for multi-segment motion blur for the + /// geometry. + /// + /// For triangle meshes, quad meshes, curves, points, and subdivision + /// geometries, the number of time steps directly corresponds to the + /// number of vertex buffer slots available [`BufferUsage::VERTEX`]. + /// + /// For instance geometries, a transformation must be specified for each + /// time step (see [`Geometry::set_transform`]). + /// + /// For user geometries, the registered bounding callback function must + /// provide a bounding box per primitive and time step, and the + /// intersection and occlusion callback functions should properly + /// intersect the motion-blurred geometry at the ray time. + pub fn set_time_step_count(&mut self, count: u32) { + unsafe { + rtcSetGeometryTimeStepCount(self.handle, count); + } + } + + /// Sets the time range for a motion blur geometry. + /// + /// The time range is defined relative to the camera shutter interval + /// \[0,1\] but it can be arbitrary. Thus the `start` time can be + /// smaller, equal, or larger 0, indicating a geometry whose animation + /// definition start before, at, or after the camera shutter opens. + /// Similar the `end` time can be smaller, equal, or larger than 1, + /// indicating a geometry whose animation definition ends after, at, or + /// before the camera shutter closes. The `start` time has to be smaller + /// or equal to the `end` time. + /// + /// The default time range when this function is not called is the entire + /// camera shutter \[0,1\]. For best performance at most one time segment + /// of the piece wise linear definition of the motion should fall + /// outside the shutter window to the left and to the right. Thus do not + /// set the `start` time or `end` time too far outside the + /// \[0,1\] interval for best performance. + /// + /// This time range feature will also allow geometries to appear and + /// disappear during the camera shutter time if the specified time range + /// is a sub range of \[0,1\]. + /// + /// Please also have a look at the [`Geometry::set_time_step_count`] to + /// see how to define the time steps for the specified time range. + pub fn set_time_range(&mut self, start: f32, end: f32) { + unsafe { + rtcSetGeometryTimeRange(self.handle, start, end); + } + } + + /// Sets the user-defined data pointer of the geometry. + /// + /// The user data pointer is intended to be pointing to the application's + /// representation of the geometry, and is passed to various callback + /// functions. + /// + /// The application can use this pointer inside the callback functions to + /// access its geometry representation. + pub fn set_user_data(&mut self, user_data: &mut D) + where + D: UserGeometryData, + { + let mut geom_data = self.data.lock().unwrap(); + geom_data.user_data = Some(GeometryUserData { + data: user_data as *mut D as *mut std::os::raw::c_void, + type_id: TypeId::of::(), + }); + unsafe { + rtcSetGeometryUserData( + self.handle, + geom_data.deref_mut() as *mut GeometryData as *mut _, + ); + } + } + + /// Returns the user data pointer of the geometry. + pub fn get_user_data(&self) -> Option<&mut D> + where + D: UserGeometryData, + { + unsafe { + let ptr = rtcGetGeometryUserData(self.handle) as *mut GeometryData; + if ptr.is_null() { + None + } else { + match (*ptr).user_data.as_mut() { + None => None, + Some(user_data @ GeometryUserData { .. }) => { + if user_data.type_id == TypeId::of::() { + Some(&mut *(user_data.data as *mut D)) + } else { + None + } + } + } + } + } + } + + /// Sets the number of vertex attributes of the geometry. + /// + /// This function sets the number of slots for vertex attributes buffers + /// (BufferUsage::VERTEX_ATTRIBUTE) that can be used for the specified + /// geometry. + /// + /// Only supported by triangle meshes, quad meshes, curves, points, and + /// subdivision geometries. + /// + /// # Arguments + /// + /// * `count` - The number of vertex attribute slots. + pub fn set_vertex_attribute_count(&mut self, count: u32) { + match self.kind { + GeometryKind::GRID | GeometryKind::USER | GeometryKind::INSTANCE => { + eprint!( + "Vertex attribute not allowed for geometries of type {:?}!", + self.kind + ); + } + _ => { + // Update the vertex attribute count. + unsafe { + rtcSetGeometryVertexAttributeCount(self.handle, count); + } + } + } + } + + /// Binds a vertex attribute to a topology of the geometry. + /// + /// This function binds a vertex attribute buffer slot to a topology for the + /// specified subdivision geometry. Standard vertex buffers are always bound + /// to the default topology (topology 0) and cannot be bound + /// differently. A vertex attribute buffer always uses the topology it + /// is bound to when used in the `rtcInterpolate` and `rtcInterpolateN` + /// calls. + /// + /// A topology with ID `i` consists of a subdivision mode set through + /// `Geometry::set_subdivision_mode` and the index buffer bound to the index + /// buffer slot `i`. This index buffer can assign indices for each face of + /// the subdivision geometry that are different to the indices of the + /// default topology. These new indices can for example be used to + /// introduce additional borders into the subdivision mesh to map + /// multiple textures onto one subdivision geometry. + pub fn set_vertex_attribute_topology(&self, vertex_attribute_id: u32, topology_id: u32) { + unsafe { + rtcSetGeometryVertexAttributeTopology(self.handle, vertex_attribute_id, topology_id); + } + } + + /// Smoothly interpolates per-vertex data over the geometry. + /// + /// This interpolation is supported for triangle meshes, quad meshes, curve + /// geometries, and subdivision geometries. Apart from interpolating the + /// vertex at- tribute itself, it is also possible to get the first and + /// second order derivatives of that value. This interpolation ignores + /// displacements of subdivision surfaces and always interpolates the + /// underlying base surface. + /// + /// Interpolated values are written to `args.p`, `args.dp_du`, `args.dp_dv`, + /// `args.ddp_du_du`, `args.ddp_dv_dv`, and `args.ddp_du_dv`. Set them to + /// `None` if you do not need to interpolate them. + /// + /// All output arrays must be padded to 16 bytes. + pub fn interpolate(&self, input: InterpolateInput, output: &mut InterpolateOutput) { + let args = RTCInterpolateArguments { + geometry: self.handle, + primID: input.prim_id, + u: input.u, + v: input.v, + bufferType: input.usage, + bufferSlot: input.slot, + P: output + .p_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + dPdu: output + .dp_du_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + dPdv: output + .dp_dv_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + ddPdudu: output + .ddp_du_du_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + ddPdvdv: output + .ddp_dv_dv_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + ddPdudv: output + .ddp_du_dv_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + valueCount: output.value_count(), + }; + unsafe { + rtcInterpolate(&args as _); + } + } + + /// Performs N interpolations of vertex attribute data. + /// + /// Similar to [`Geometry::interpolate`], but performs N many interpolations + /// at once. It additionally gets an array of u/v coordinates + /// [`InterpolateNInput::u/v`]and a valid mask + /// [`InterpolateNInput::valid`] that specifies which of these + /// coordinates are valid. The valid mask points to `n` integers, and a + /// value of -1 denotes valid and 0 invalid. + /// + /// If [`InterpolateNInput::valid`] is `None`, all coordinates are + /// assumed to be valid. + /// + /// The destination arrays are filled in structure of array (SOA) layout. + /// The value [`InterpolateNInput::n`] must be divisible by 4. + /// + /// All changes to that geometry must be properly committed. + pub fn interpolate_n(&self, input: InterpolateNInput, output: &mut InterpolateOutput) { + assert_eq!(input.n % 4, 0, "N must be a multiple of 4!"); + let args = RTCInterpolateNArguments { + geometry: self.handle, + N: input.n, + valid: input + .valid + .as_ref() + .map(|v| v.as_ptr() as *const _) + .unwrap_or(ptr::null()), + primIDs: input.prim_id.as_ptr(), + u: input.u.as_ptr(), + v: input.v.as_ptr(), + bufferType: input.usage, + bufferSlot: input.slot, + P: output + .p_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + dPdu: output + .dp_du_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + dPdv: output + .dp_dv_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + ddPdudu: output + .ddp_du_du_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + ddPdvdv: output + .ddp_dv_dv_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + ddPdudv: output + .ddp_du_dv_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + valueCount: output.value_count(), + }; + unsafe { + rtcInterpolateN(&args as _); + } + } + + /// Sets a callback to query the bounding box of user-defined primitives. + /// + /// Only a single callback function can be registered per geometry, and + /// further invocations overwrite the previously set callback function. + /// + /// Unregister the callback function by calling + /// [`Geometry::unset_bounds_function`]. + /// + /// The registered bounding box callback function is invoked to calculate + /// axis- aligned bounding boxes of the primitives of the user-defined + /// geometry during spatial acceleration structure construction. + /// + /// The arguments of the callback closure are: + /// + /// - a mutable reference to the user data of the geometry + /// + /// - the ID of the primitive to calculate the bounds for + /// + /// - the time step at which to calculate the bounds + /// + /// - a mutable reference to the bounding box where the result should be + /// written to + /// + /// In a typical usage scenario one would store a pointer to the internal + /// representation of the user geometry object using + /// [`Geometry::set_user_data`]. The callback function can then read + /// that pointer from the `geometryUserPtr` field and calculate the + /// proper bounding box for the requested primitive and time, and store + /// that bounding box to the destination structure (`bounds_o` member). + pub fn set_bounds_function(&mut self, bounds: F) + where + D: UserGeometryData, + F: FnMut(&mut Bounds, u32, u32, Option<&mut D>), + { + match self.kind { + GeometryKind::USER => unsafe { + let mut geom_data = self.data.lock().unwrap(); + let mut closure = bounds; + geom_data.user_fns.as_mut().unwrap().bounds_fn = + &mut closure as *mut _ as *mut std::os::raw::c_void; + rtcSetGeometryBoundsFunction( + self.handle, + bounds_function(&mut closure), + ptr::null_mut(), + ); + }, + _ => panic!("Only user geometries can have a bounds function!"), + } + } + + /// Unsets the callback to calculate the bounding box of user-defined + /// geometry. + pub fn unset_bounds_function(&mut self) { + match self.kind { + GeometryKind::USER => unsafe { + rtcSetGeometryBoundsFunction(self.handle, None, ptr::null_mut()); + }, + _ => panic!("Only user geometries can have a bounds function!"), + } + } + + /// Sets the callback function to intersect a user geometry. + /// + /// The registered + /// callback function is invoked by intersect-type ray queries to + /// calculate the intersection of a ray packet of variable size with one + /// user-defined primitive. + /// Only a single callback function can be registered per geometry and + /// further invocations overwrite the previously set callback function. + /// Unregister the callback function by calling + /// [`Geometry::unset_intersect_function`]. + /// + /// + /// # Arguments + /// + /// - `intersect`: The callback function to register. The task of the + /// callback function is to intersect each active ray from the ray packet + /// with the specified user primitive. If the user-defined primitive is + /// missed by a ray of the ray packet, the function should return without + /// modifying the ray or hit. If an intersection of the user-defined + /// primitive with the ray is found in the range `tnear` to `tfar`, it + /// should update the hit distance of the ray (`tfar` member) and the + /// hit(`u`, `v`, `instID`, `geomID`, `primID` members). In particular, + /// the currently intersected instance is stored in the `instID` field of + /// the intersection context, which must be deep-copied into the `instID` + /// member of the hit structure. + /// + /// The callback function gets passed a number of arguments: + /// - the ray hit packet of variable size N (see [`RayHitN`]); it + /// contains valid data, in particular the `tfar` value is the current + /// closest hit distance found. All data inside the `hit` component of + /// the ray hit structure are undefined and should **NOT** be read by + /// the function. + /// - the valid masks for each ray in the packet (see [`ValidityN`]) + /// - a mutable reference to the intersection context (see + /// [`IntersectContext`](`crate::IntersectContext`) and + /// [`IntersectContextExt`](`crate::IntersectContextExt`)) + /// - the geometry ID of the geometry to intersect + /// - the primitive ID of the primitive to intersect + /// - a mutable reference to the user data of the geometry (if any); the + /// user data can be set using [`Geometry::set_user_data`] + /// + /// The ray component of the ray hit structure contains valid data, in + /// particular the tfar value is the current closest hit distance found. + /// All data inside the hit component of the [`RayHitN`] structure are + /// undefined and should **NOT** be *read* by the function (writing is ok). + /// + /// As a primitive might have multiple intersections with a ray, the + /// intersection filter function needs to be invoked by the user + /// geometry intersection callback for each encountered intersection, if + /// filtering of intersections is desired. This can be achieved through + /// the [`Geometry::set_intersect_filter_function`]. + /// + /// - Within the user geometry intersect function, it is safe to trace new + /// rays and create new scenes and geometries. + /// + /// - When performing ray queries using [`Scene::intersect`], it is + /// guaranteed that the packet size is 1 when the callback is invoked. + /// + /// - When performing ray queries using the + /// [`Scene::intersect4`]/[`Scene::intersect8`]/[`Scene::intersect16`] + /// functions, it is **not** generally guaranteed that the ray packet size + /// (and order of rays inside the packet) passed to the callback matches + /// the initial ray packet. However, under some circumstances these + /// properties are guaranteed, and whether this is the case can be queried + /// using [`Device::get_property`]. + /// + /// - When performing ray queries using the stream API such as + /// [`Scene::intersect_stream_soa`], [`Scene::intersect_stream_aos`], the + /// order of rays and ray packet size of the callback function might + /// change to either 1, 4, 8, or 16. + /// + /// - For many usage scenarios, repacking and re-ordering of rays does not + /// cause difficulties in implementing the callback function. However, + /// algorithms that need to extend the ray with additional data must use + /// the rayID component of the ray to identify the original ray to access + /// the per-ray data. + pub fn set_intersect_function(&mut self, intersect: F) + where + D: UserGeometryData, + C: AsIntersectContext, + F: for<'a> FnMut(RayHitN<'a>, ValidityN<'a>, &mut C, u32, u32, Option<&mut D>), + { + match self.kind { + GeometryKind::USER => unsafe { + let mut geom_data = self.data.lock().unwrap(); + let mut closure = intersect; + geom_data.user_fns.as_mut().unwrap().intersect_fn = + &mut closure as *mut _ as *mut std::os::raw::c_void; + rtcSetGeometryIntersectFunction(self.handle, intersect_function(&mut closure)); + }, + _ => panic!("Only user geometries can have an intersect function!"), + } + } + + /// Unsets the callback to intersect user-defined geometry. + pub fn unset_intersect_function(&mut self) { + match self.kind { + GeometryKind::USER => unsafe { + rtcSetGeometryIntersectFunction(self.handle, None); + }, + _ => panic!("Only user geometries can have an intersect function!"), + } + } + + /// Sets the callback function to occlude a user geometry. + /// + /// Similar to [`Geometry::set_intersect_function`], but for occlusion + /// queries. + /// + /// # Arguments + /// + /// - `occluded`: The callback function to register, which is invoked by + /// occlusion queries to test whether the rays of a packet of variable + /// size are occluded by a user-defined primitive. The callback function + /// gets passed a number of arguments: + /// + /// - the ray packet of variable size N (see [`RayN`]) + /// - the valid masks for each ray in the packet (see [`ValidityN`]) + /// - a mutable reference to the intersection context (see + /// [`IntersectContext`](`crate::IntersectContext`) and + /// [`IntersectContextExt`](`crate::IntersectContextExt`)) + /// - the geometry ID of the geometry to intersect + /// - the primitive ID of the primitive to intersect + /// - a mutable reference to the user data of the geometry (if any); the + /// user data can be set using [`Geometry::set_user_data`] + pub fn set_occluded_function(&mut self, occluded: F) + where + D: UserGeometryData, + C: AsIntersectContext, + F: for<'a> FnMut(RayN<'a>, ValidityN<'a>, &mut C, u32, u32, Option<&mut D>), + { + match self.kind { + GeometryKind::USER => { + let mut geom_data = self.data.lock().unwrap(); + let mut closure = occluded; + geom_data.user_fns.as_mut().unwrap().occluded_fn = + &mut closure as *mut _ as *mut std::os::raw::c_void; + unsafe { + rtcSetGeometryOccludedFunction(self.handle, occluded_function(&mut closure)) + }; + } + _ => panic!("Only user geometries can have an occluded function!"), + } + } + + /// Unsets the callback to occlude user-defined geometry. + pub fn unset_occluded_function(&mut self) { + match self.kind { + GeometryKind::USER => unsafe { + rtcSetGeometryOccludedFunction(self.handle, None); + }, + _ => panic!("Only user geometries can have an occluded function!"), + } + } + + /// Sets the number of primitives of a user-defined geometry. + pub fn set_primitive_count(&mut self, count: u32) { + match self.kind { + GeometryKind::USER => unsafe { + rtcSetGeometryUserPrimitiveCount(self.handle, count); + }, + _ => panic!("Only user geometries can have a primitive count!"), } } /// Set the subdivision mode for the topology of the specified subdivision /// geometry. /// - /// The subdivision modes can be used to force linear interpolation for certain - /// parts of the subdivision mesh: + /// The subdivision modes can be used to force linear interpolation for + /// certain parts of the subdivision mesh: /// /// * [`RTCSubdivisionMode::NO_BOUNDARY`]: Boundary patches are ignored. /// This way each rendered patch has a full set of control vertices. @@ -28,35 +1377,1027 @@ pub trait Geometry { /// /// * [`RTCSubdivisionMode::PIN_BOUNDARY`]: All vertices at the border are /// pinned to their location during subdivision. This way the boundary is - /// interpolated linearly. This mode is typically used for texturing to also map - /// texels at the border of the texture to the mesh. + /// interpolated linearly. This mode is typically used for texturing to also + /// map texels at the border of the texture to the mesh. /// /// * [`RTCSubdivisionMode::PIN_ALL`]: All vertices at the border are pinned /// to their location during subdivision. This way all patches are linearly /// interpolated. - fn set_subdivision_mode(&self, topology_id: u32, mode: RTCSubdivisionMode) { - unsafe { - rtcSetGeometrySubdivisionMode(self.handle(), topology_id, mode); + pub fn set_subdivision_mode(&self, topology_id: u32, mode: SubdivisionMode) { + match self.kind { + GeometryKind::SUBDIVISION => unsafe { + rtcSetGeometrySubdivisionMode(self.handle, topology_id, mode) + }, + _ => panic!("Only subdivision geometries can have a subdivision mode!"), } } - /// Binds a vertex attribute to a topology of the geometry. + /// Sets the number of topologies of a subdivision geometry. /// - /// This function binds a vertex attribute buffer slot to a topology for the - /// specified subdivision geometry. Standard vertex buffers are always bound to - /// the default topology (topology 0) and cannot be bound differently. A vertex - /// attribute buffer always uses the topology it is bound to when used in the - /// `rtcInterpolate` and `rtcInterpolateN` calls. + /// The number of topologies of a subdivision geometry must be greater + /// or equal to 1. /// - /// A topology with ID `i` consists of a subdivision mode set through - /// `Geometry::set_subdivision_mode` and the index buffer bound to the index - /// buffer slot `i`. This index buffer can assign indices for each face of the - /// subdivision geometry that are different to the indices of the default topology. - /// These new indices can for example be used to introduce additional borders into - /// the subdivision mesh to map multiple textures onto one subdivision geometry. - fn set_vertex_attribute_topology(&self, vertex_attribute_id: u32, topology_id: u32) { - unsafe { - rtcSetGeometryVertexAttributeTopology(self.handle(), vertex_attribute_id, topology_id); + /// To use multiple topologies, first the number of topologies must be + /// specified, then the individual topologies can be configured using + /// [`Geometry::set_subdivision_mode`] and by setting an index buffer + /// ([`BufferUsage::INDEX`]) using the topology ID as the buffer slot. + pub fn set_topology_count(&mut self, count: u32) { + match self.kind { + GeometryKind::SUBDIVISION => unsafe { + rtcSetGeometryTopologyCount(self.handle, count); + }, + _ => panic!("Only subdivision geometries can have multiple topologies!"), + } + } + + /// Returns the first half edge of a face. + /// + /// This function can only be used for subdivision meshes. As all topologies + /// of a subdivision geometry share the same face buffer the function does + /// not depend on the topology ID. + pub fn get_first_half_edge(&self, face_id: u32) -> u32 { + match self.kind { + GeometryKind::SUBDIVISION => unsafe { + rtcGetGeometryFirstHalfEdge(self.handle, face_id) + }, + _ => panic!("Only subdivision geometries can have half edges!"), + } + } + + /// Returns the face of some half edge. + /// + /// This function can only be used for subdivision meshes. As all topologies + /// of a subdivision geometry share the same face buffer the function does + /// not depend on the topology ID. + pub fn get_face(&self, half_edge_id: u32) -> u32 { + match self.kind { + GeometryKind::SUBDIVISION => unsafe { rtcGetGeometryFace(self.handle, half_edge_id) }, + _ => panic!("Only subdivision geometries can have half edges!"), + } + } + + /// Returns the next half edge of some half edge. + /// + /// This function can only be used for subdivision meshes. As all topologies + /// of a subdivision geometry share the same face buffer the function does + /// not depend on the topology ID. + pub fn get_next_half_edge(&self, half_edge_id: u32) -> u32 { + match self.kind { + GeometryKind::SUBDIVISION => unsafe { + rtcGetGeometryNextHalfEdge(self.handle, half_edge_id) + }, + _ => panic!("Only subdivision geometries can have half edges!"), + } + } + + /// Returns the previous half edge of some half edge. + pub fn get_previous_half_edge(&self, half_edge_id: u32) -> u32 { + match self.kind { + GeometryKind::SUBDIVISION => unsafe { + rtcGetGeometryPreviousHalfEdge(self.handle, half_edge_id) + }, + _ => panic!("Only subdivision geometries can have half edges!"), + } + } + + /// Returns the opposite half edge of some half edge. + pub fn get_opposite_half_edge(&self, topology_id: u32, edge_id: u32) -> u32 { + match self.kind { + GeometryKind::SUBDIVISION => unsafe { + rtcGetGeometryOppositeHalfEdge(self.handle, topology_id, edge_id) + }, + _ => panic!("Only subdivision geometries can have half edges!"), + } + } + + /// Sets the displacement function for a subdivision geometry. + /// + /// Only one displacement function can be set per geometry, further calls to + /// this will overwrite the previous displacement function. Use + /// [`Geometry::unset_displacement_function`] to remove the displacement + /// function. + /// + /// The registered function is invoked to displace points on the subdivision + /// geometry during spatial acceleration structure construction, + /// during the [`Scene::commit`] call. + /// + /// # Arguments + /// + /// * `displacement`: The displacement function. The displacement function + /// is called for each vertex of the subdivision geometry. + /// + /// The function is called with the following parameters: + /// + /// * `geometry`: The raw geometry handle [`sys::RTCGeometry`]. + /// * `vertices`: The information about the vertices to displace. See + /// [`Vertices`]. + /// * `prim_id`: The ID of the primitive that contains the vertices to + /// displace. + /// * `time_step`: The time step for which the displacement function is + /// evaluated. Important for time dependent displacement and motion + /// blur. + /// * `user_data`: The geometry user data. See + /// [`Geometry::set_user_data`]. + /// + /// # Safety + /// + /// The callback function provided to this function contains a raw pointer + /// to Embree geometry. + pub unsafe fn set_displacement_function(&mut self, displacement: F) + where + D: UserGeometryData, + F: for<'a> FnMut(RTCGeometry, Vertices<'a>, u32, u32, Option<&mut D>), + { + match self.kind { + GeometryKind::SUBDIVISION => { + let mut geom_data = self.data.lock().unwrap(); + unsafe { + let mut closure = displacement; + geom_data + .subdivision_fns + .replace(SubdivisionGeometryPayloads { + displacement_fn: &mut closure as *mut _ as *mut std::os::raw::c_void, + }); + rtcSetGeometryDisplacementFunction( + self.handle, + displacement_function(&mut closure), + ) + } + } + _ => panic!("Only subdivision geometries can have displacement functions!"), + } + } + + /// Removes the displacement function for a subdivision geometry. + pub fn unset_displacement_function(&mut self) { + match self.kind { + GeometryKind::SUBDIVISION => unsafe { + rtcSetGeometryDisplacementFunction(self.handle, None); + }, + _ => panic!("Only subdivision geometries can have displacement functions!"), + } + } + + /// Sets the instanced scene of an instance geometry. + pub fn set_instanced_scene(&mut self, scene: &Scene) { + match self.kind { + GeometryKind::INSTANCE => unsafe { + rtcSetGeometryInstancedScene(self.handle, scene.handle) + }, + _ => panic!("Only instance geometries can have instanced scenes!"), + } + } + + /// Returns the interpolated instance transformation for the specified time + /// step. + /// + /// The transformation is returned as a 4x4 column-major matrix. + pub fn get_transform(&mut self, time: f32) -> [f32; 16] { + match self.kind { + GeometryKind::INSTANCE => unsafe { + let mut transform = [0.0; 16]; + rtcGetGeometryTransform( + self.handle, + time, + Format::FLOAT4X4_COLUMN_MAJOR, + transform.as_mut_ptr() as *mut _, + ); + transform + }, + _ => panic!("Only instance geometries can have instanced scenes!"), + } + } + + /// Sets the transformation for a particular time step of an instance + /// geometry. + /// + /// The transformation is specified as a 4x4 column-major matrix. + pub fn set_transform(&mut self, time_step: u32, transform: &[f32; 16]) { + match self.kind { + GeometryKind::INSTANCE => unsafe { + rtcSetGeometryTransform( + self.handle, + time_step, + Format::FLOAT4X4_COLUMN_MAJOR, + transform.as_ptr() as *const _, + ); + }, + _ => panic!("Only instance geometries can have instanced scenes!"), + } + } + + /// Sets the transformation for a particular time step of an instance + /// geometry as a decomposition of the transformation matrix using + /// quaternions to represent the rotation. + pub fn set_transform_quaternion( + &mut self, + time_step: u32, + transform: &QuaternionDecomposition, + ) { + match self.kind { + GeometryKind::INSTANCE => unsafe { + rtcSetGeometryTransformQuaternion( + self.handle, + time_step, + transform as &QuaternionDecomposition as *const _, + ); + }, + _ => panic!("Only instance geometries can have instanced scenes!"), + } + } +} + +/// The arguments for the `Geometry::interpolate` function. +pub struct InterpolateInput { + pub prim_id: u32, + pub u: f32, + pub v: f32, + pub usage: BufferUsage, + pub slot: u32, +} + +/// The arguments for the `Geometry::interpolate_n` function. +pub struct InterpolateNInput<'a> { + pub valid: Option>, + pub prim_id: Cow<'a, [u32]>, + pub u: Cow<'a, [f32]>, + pub v: Cow<'a, [f32]>, + pub usage: BufferUsage, + pub slot: u32, + pub n: u32, +} + +/// The output of the `Geometry::interpolate` and `Geometry::interpolate_n` +/// functions in structure of array (SOA) layout. +pub struct InterpolateOutput { + /// The buffer containing the interpolated values. + buffer: Vec, + /// The number of values per attribute. + count_per_attribute: u32, + /// The offset of the `p` attribute in the buffer. + p_offset: Option, + /// The offset of the `dp_du` attribute in the buffer. + dp_du_offset: Option, + /// The offset of the `dp_dv` attribute in the buffer. + dp_dv_offset: Option, + /// The offset of the `ddp_du_du` attribute in the buffer. + ddp_du_du_offset: Option, + /// The offset of the `ddp_dv_dv` attribute in the buffer. + ddp_dv_dv_offset: Option, + /// The offset of the `ddp_du_dv` attribute in the buffer. + ddp_du_dv_offset: Option, +} + +impl InterpolateOutput { + pub fn new(count: u32, zeroth_order: bool, first_order: bool, second_order: bool) -> Self { + assert!( + count > 0, + "The number of interpolated values must be greater than 0!" + ); + assert!( + zeroth_order || first_order || second_order, + "At least one of the origin value, first order derivative, or second order derivative \ + must be true!" + ); + let mut offset = 0; + let p_offset = zeroth_order.then(|| { + let _offset = offset; + offset += count; + _offset + }); + let dp_du_offset = first_order.then(|| { + let _offset = offset; + offset += count; + _offset + }); + let dp_dv_offset = first_order.then(|| { + let _offset = offset; + offset += count; + _offset + }); + let ddp_du_du_offset = second_order.then(|| { + let _offset = offset; + offset += count; + _offset + }); + let ddp_dv_dv_offset = second_order.then(|| { + let _offset = offset; + offset += count; + _offset + }); + let ddp_du_dv_offset = second_order.then(|| { + let _offset = offset; + offset += count; + _offset + }); + + Self { + buffer: vec![0.0; (offset + count) as usize], + count_per_attribute: count, + p_offset, + dp_du_offset, + dp_dv_offset, + ddp_du_du_offset, + ddp_dv_dv_offset, + ddp_du_dv_offset, + } + } + + /// Returns the interpolated `p` attribute. + pub fn p(&self) -> Option<&[f32]> { + self.p_offset.map(|offset| { + &self.buffer[offset as usize..(offset + self.count_per_attribute) as usize] + }) + } + + /// Returns the mutable interpolated `p` attribute. + pub fn p_mut(&mut self) -> Option<&mut [f32]> { + self.p_offset.map(move |offset| { + &mut self.buffer[offset as usize..(offset + self.count_per_attribute) as usize] + }) + } + + /// Returns the interpolated `dp_du` attribute. + pub fn dp_du(&self) -> Option<&[f32]> { + self.dp_du_offset.map(|offset| { + &self.buffer[offset as usize..(offset + self.count_per_attribute) as usize] + }) + } + + /// Returns the mutable interpolated `dp_du` attribute. + pub fn dp_du_mut(&mut self) -> Option<&mut [f32]> { + self.dp_du_offset.map(|offset| { + &mut self.buffer[offset as usize..(offset + self.count_per_attribute) as usize] + }) + } + + /// Returns the interpolated `dp_dv` attribute. + pub fn dp_dv(&self) -> Option<&[f32]> { + self.dp_dv_offset.map(|offset| { + &self.buffer[offset as usize..(offset + self.count_per_attribute) as usize] + }) + } + + /// Returns the mutable interpolated `dp_dv` attribute. + pub fn dp_dv_mut(&mut self) -> Option<&mut [f32]> { + self.dp_dv_offset.map(|offset| { + &mut self.buffer[offset as usize..(offset + self.count_per_attribute) as usize] + }) + } + + /// Returns the interpolated `ddp_du_du` attribute. + pub fn ddp_du_du(&self) -> Option<&[f32]> { + self.ddp_du_du_offset.map(|offset| { + &self.buffer[offset as usize..(offset + self.count_per_attribute) as usize] + }) + } + + /// Returns the mutable interpolated `ddp_du_du` attribute. + pub fn ddp_du_du_mut(&mut self) -> Option<&mut [f32]> { + self.ddp_du_du_offset.map(|offset| { + &mut self.buffer[offset as usize..(offset + self.count_per_attribute) as usize] + }) + } + + /// Returns the interpolated `ddp_dv_dv` attribute. + pub fn ddp_dv_dv(&self) -> Option<&[f32]> { + self.ddp_dv_dv_offset.map(|offset| { + &self.buffer[offset as usize..(offset + self.count_per_attribute) as usize] + }) + } + + /// Returns the mutable interpolated `ddp_dv_dv` attribute. + pub fn ddp_dv_dv_mut(&mut self) -> Option<&mut [f32]> { + self.ddp_dv_dv_offset.map(move |offset| { + &mut self.buffer[offset as usize..(offset + self.count_per_attribute) as usize] + }) + } + + /// Returns the interpolated `ddp_du_dv` attribute. + pub fn ddp_du_dv(&self) -> Option<&[f32]> { + self.ddp_du_dv_offset.map(|offset| { + &self.buffer[offset as usize..(offset + self.count_per_attribute) as usize] + }) + } + + /// Returns the mutable interpolated `ddp_du_dv` attribute. + pub fn ddp_du_dv_mut(&mut self) -> Option<&mut [f32]> { + self.ddp_du_dv_offset.map(move |offset| { + &mut self.buffer[offset as usize..(offset + self.count_per_attribute) as usize] + }) + } + + /// Returns the number of values per attribute. + pub fn value_count(&self) -> u32 { self.count_per_attribute } +} + +macro_rules! impl_geometry_type { + ($name:ident, $kind:path, $(#[$meta:meta])*) => { + #[derive(Debug)] + pub struct $name<'a>(Geometry<'a>); + + impl<'a> Deref for $name<'a> { + type Target = Geometry<'a>; + + fn deref(&self) -> &Self::Target { &self.0 } + } + + impl<'a> DerefMut for $name<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } + } + + $(#[$meta])* + impl<'a> $name<'a> { + pub fn new(device: &Device) -> Result { + Ok(Self(Geometry::new(device, $kind)?)) + } + } + }; +} + +impl_geometry_type!(TriangleMesh, GeometryKind::TRIANGLE, + /// A triangle mesh geometry. + /// + /// The index buffer must contain an array of three 32-bit indices per triangle + /// ([`Format::UINT3`]), and the number of primitives is inferred from the size + /// of the index buffer. + /// + /// The vertex buffer must contain an array of single precision x, y, + /// and z floating point coordinates per vertex ([`Format::FLOAT3`]), and the + /// number of vertices is inferred from the size of the vertex buffer. + /// The vertex buffer can be at most 16 GB in size. + /// + /// The parameterization of a triangle uses the first vertex `p0` as the + /// base point, the vector `p1 - p0` as the u-direction, and the vector + /// `p2 - p0` as the v-direction. Thus vertex attributes t0, t1, and t2 + /// can be linearly interpolated over the triangle using the barycentric + /// coordinates `(u,v)` of the hit point: + /// + /// t_uv = (1-u-v) * t0 + u * t1 + v * t2 + /// = t0 + u * (t1 - t0) + v * (t2 - t0) + /// + /// A triangle whose vertices are laid out counter-clockwise has its geometry + /// normal pointing upwards outside the front face. + /// + /// For multi-segment motion blur, the number of time steps must be first + /// specified using the [`Geometry::set_time_step_count`] call. Then a vertex + /// buffer for each time step can be set using different buffer slots, and all + /// these buffers have to have the same stride and size. +); + +impl_geometry_type!(QuadMesh, GeometryKind::QUAD, + /// A quad mesh geometry. + /// + /// The index buffer must contain an array of four 32-bit indices per triangle + /// ([`Format::UINT4`]), and the number of primitives is inferred from the size + /// of the index buffer. + /// + /// The vertex buffer must contain an array of single precision x, y, + /// and z floating point coordinates per vertex ([`Format::FLOAT3`]), and the + /// number of vertices is inferred from the size of the vertex buffer. + /// The vertex buffer can be at most 16 GB in size. + /// + /// A quad is internally handled as a pair of two triangles `v0`, `v1`, `v3` + /// and `v2`, `v3`, `v1`, with the `u'/v'` coordinates of the second triangle + /// corrected by `u = 1-u'` and `v = 1-v'` to produce a quad parametrization + /// where `u` and `v` are in the range 0 to 1. Thus the parametrization of a quad + /// uses the first vertex `p0` as base point, and the vector `p1 - p0` as + /// u-direction, and `p3 - p0` as v-direction. Thus vertex attributes t0, t1, t2, t3 + /// can be bilinearly interpolated over the quadrilateral the following way: + /// + /// t_uv = (1-v)((1-u) * t0 + u * t1) + v * ((1-u) * t3 + u * t2) + /// + /// Mixed triangle/quad meshes are supported by encoding a triangle as a quad, + /// which can be achieved by replicating the last triangle vertex (v0,v1,v2 -> + /// v0,v1,v2,v2). This way the second triangle is a line (which can never get + /// hit), and the parametrization of the first triangle is compatible with the + /// standard triangle parametrization. + /// A quad whose vertices are laid out counter-clockwise has its geometry + /// normal pointing upwards outside the front face. + /// + /// p3 ------- p2 + /// ^ | + /// v | | + /// | | + /// p0 ------> p1 + /// u +); + +impl_geometry_type!(UserGeometry, GeometryKind::USER, + /// A user geometry. +); + +impl_geometry_type!(Instance, GeometryKind::INSTANCE, + /// An instance geometry. +); + +/// Helper function to convert a Rust closure to `RTCFilterFunctionN` callback +/// for intersect. +fn intersect_filter_function(_f: &mut F) -> RTCFilterFunctionN +where + D: UserGeometryData, + C: AsIntersectContext, + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidityN<'a>, &mut C, Option<&mut D>), +{ + unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) + where + D: UserGeometryData, + C: AsIntersectContext, + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidityN<'a>, &mut C, Option<&mut D>), + { + let raw_data = + &mut *((*args).geometryUserPtr as *mut Mutex) as &mut Mutex; + let geom_data = raw_data.get_mut().unwrap(); + let cb_ptr = geom_data.intersect_filter_fn as *mut F; + if !cb_ptr.is_null() { + let cb = &mut *cb_ptr; + let user_data = { + match geom_data.user_data { + Some(ref user_data) => { + if user_data.data.is_null() || user_data.type_id != TypeId::of::() { + None + } else { + Some(&mut *(user_data.data as *mut D)) + } + } + None => None, + } + }; + let len = (*args).N as usize; + cb( + RayN { + ptr: (*args).ray, + len, + marker: PhantomData, + }, + HitN { + ptr: (*args).hit, + len, + marker: PhantomData, + }, + ValidityN { + ptr: (*args).valid, + len, + marker: PhantomData, + }, + &mut *((*args).context as *mut _ as *mut C), + user_data, + ); + } + } + Some(inner::) +} + +/// Helper function to convert a Rust closure to `RTCFilterFunctionN` callback +/// for occluded. +fn occluded_filter_function(_f: &mut F) -> RTCFilterFunctionN +where + D: UserGeometryData, + C: AsIntersectContext, + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidityN<'a>, &mut C, Option<&mut D>), +{ + unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) + where + D: UserGeometryData, + C: AsIntersectContext, + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidityN<'a>, &mut C, Option<&mut D>), + { + let raw_data = + &mut *((*args).geometryUserPtr as *mut Mutex) as &mut Mutex; + let geom_data = raw_data.get_mut().unwrap(); + let len = (*args).N as usize; + let cb_ptr = geom_data.occluded_filter_fn as *mut F; + if !cb_ptr.is_null() { + let cb = &mut *cb_ptr; + let user_data = { + match geom_data.user_data { + Some(ref user_data) => { + if user_data.data.is_null() || user_data.type_id != TypeId::of::() { + None + } else { + Some(&mut *(user_data.data as *mut D)) + } + } + None => None, + } + }; + cb( + RayN { + ptr: (*args).ray, + len, + marker: PhantomData, + }, + HitN { + ptr: (*args).hit, + len, + marker: PhantomData, + }, + ValidityN { + ptr: (*args).valid, + len, + marker: PhantomData, + }, + &mut *((*args).context as *mut _ as *mut C), + user_data, + ); + } + } + + Some(inner::) +} + +/// Helper function to convert a Rust closure to `RTCBoundsFunction` callback. +fn bounds_function(_f: &mut F) -> RTCBoundsFunction +where + D: UserGeometryData, + F: FnMut(&mut Bounds, u32, u32, Option<&mut D>), +{ + unsafe extern "C" fn inner(args: *const RTCBoundsFunctionArguments) + where + D: UserGeometryData, + F: FnMut(&mut Bounds, u32, u32, Option<&mut D>), + { + let raw_data = + &mut *((*args).geometryUserPtr as *mut Mutex) as &mut Mutex; + let geom_data = raw_data.get_mut().unwrap(); + let cb_ptr = geom_data + .user_fns + .as_ref() + .expect( + "User payloads not set! Make sure the geometry was created with kind \ + GeometryKind::USER", + ) + .bounds_fn as *mut F; + if !cb_ptr.is_null() { + let cb = &mut *cb_ptr; + let user_data = { + match geom_data.user_data { + Some(ref user_data) => { + if user_data.data.is_null() || user_data.type_id != TypeId::of::() { + None + } else { + Some(&mut *(user_data.data as *mut D)) + } + } + None => None, + } + }; + cb( + &mut *(*args).bounds_o, + (*args).primID, + (*args).timeStep, + user_data, + ); + } + } + + Some(inner::) +} + +/// Helper function to convert a Rust closure to `RTCIntersectFunctionN` +/// callback. +fn intersect_function(_f: &mut F) -> RTCIntersectFunctionN +where + D: UserGeometryData, + C: AsIntersectContext, + F: for<'a> FnMut(RayHitN<'a>, ValidityN<'a>, &mut C, u32, u32, Option<&mut D>), +{ + unsafe extern "C" fn inner(args: *const RTCIntersectFunctionNArguments) + where + D: UserGeometryData, + C: AsIntersectContext, + F: for<'a> FnMut(RayHitN<'a>, ValidityN<'a>, &mut C, u32, u32, Option<&mut D>), + { + let raw_data = + &mut *((*args).geometryUserPtr as *mut Mutex) as &mut Mutex; + let geom_data = raw_data.get_mut().unwrap(); + let cb_ptr = geom_data + .user_fns + .as_ref() + .expect( + "User payloads not set! Make sure the geometry was created with kind \ + GeometryKind::USER", + ) + .intersect_fn as *mut F; + let len = (*args).N as usize; + if !cb_ptr.is_null() { + let cb = &mut *cb_ptr; + let user_data = { + match geom_data.user_data { + Some(ref user_data) => { + if user_data.data.is_null() || user_data.type_id != TypeId::of::() { + None + } else { + Some(&mut *(user_data.data as *mut D)) + } + } + None => None, + } + }; + cb( + RayHitN { + ptr: (*args).rayhit, + len, + marker: PhantomData, + }, + ValidityN { + ptr: (*args).valid, + len, + marker: PhantomData, + }, + &mut *((*args).context as *mut _ as *mut C), + (*args).geomID, + (*args).primID, + user_data, + ); + } + } + + Some(inner::) +} + +/// Helper function to convert a Rust closure to `RTCOccludedFunctionN` +/// callback. +fn occluded_function(_f: &mut F) -> RTCOccludedFunctionN +where + D: UserGeometryData, + C: AsIntersectContext, + F: for<'a> FnMut(RayN<'a>, ValidityN<'a>, &mut C, u32, u32, Option<&mut D>), +{ + unsafe extern "C" fn inner(args: *const RTCOccludedFunctionNArguments) + where + D: UserGeometryData, + C: AsIntersectContext, + F: for<'a> FnMut(RayN<'a>, ValidityN<'a>, &mut C, u32, u32, Option<&mut D>), + { + let raw_data = + &mut *((*args).geometryUserPtr as *mut Mutex) as &mut Mutex; + let geom_data = raw_data.get_mut().unwrap(); + let cb_ptr = geom_data + .user_fns + .as_ref() + .expect( + "User payloads not set! Make sure the geometry was created with kind \ + GeometryKind::USER", + ) + .occluded_fn as *mut F; + if !cb_ptr.is_null() { + let cb = &mut *cb_ptr; + let user_data = { + match geom_data.user_data { + Some(ref user_data) => { + if user_data.data.is_null() || user_data.type_id != TypeId::of::() { + None + } else { + Some(&mut *(user_data.data as *mut D)) + } + } + None => None, + } + }; + cb( + RayN { + ptr: (*args).ray, + len: (*args).N as usize, + marker: PhantomData, + }, + ValidityN { + ptr: (*args).valid, + len: (*args).N as usize, + marker: PhantomData, + }, + &mut *((*args).context as *mut _ as *mut C), + (*args).geomID, + (*args).primID, + user_data, + ) + } + } + + Some(inner::) +} + +/// Struct holding data for a set of vertices in SoA layout. +/// This is used as a parameter to the callback function set by +/// [`Geometry::set_displacement_function`]. +pub struct Vertices<'a> { + /// The number of vertices. + len: usize, + /// The u coordinates of points to displace. + u: *const f32, + /// The v coordinates of points to displace. + v: *const f32, + /// The x components of normal of vertices to displace (normalized). + ng_x: *const f32, + ///The y component of normal of vertices to displace (normalized). + ng_y: *const f32, + /// The z component of normal of vertices to displace (normalized). + ng_z: *const f32, + /// The x components of points to displace. + p_x: *mut f32, + /// The y components of points to displace. + p_y: *mut f32, + /// The z components of points to displace. + p_z: *mut f32, + /// To make sure we don't outlive the lifetime of the pointers. + marker: PhantomData<&'a mut f32>, +} + +impl<'a> Vertices<'a> { + pub fn into_iter_mut(self) -> VerticesIterMut<'a> { + VerticesIterMut { + inner: self, + cur: 0, + } + } +} + +pub struct VerticesIterMut<'a> { + inner: Vertices<'a>, + cur: usize, +} + +impl<'a> Iterator for VerticesIterMut<'a> { + type Item = ([f32; 2], [f32; 3], [&'a mut f32; 3]); + + fn next(&mut self) -> Option { + if self.cur < self.inner.len { + unsafe { + let u = *self.inner.u.add(self.cur); + let v = *self.inner.v.add(self.cur); + let ng_x = *self.inner.ng_x.add(self.cur); + let ng_y = *self.inner.ng_y.add(self.cur); + let ng_z = *self.inner.ng_z.add(self.cur); + let p_x = self.inner.p_x.add(self.cur); + let p_y = self.inner.p_y.add(self.cur); + let p_z = self.inner.p_z.add(self.cur); + self.cur += 1; + Some(( + [u, v], + [ng_x, ng_y, ng_z], + [&mut *p_x, &mut *p_y, &mut *p_z], + )) + } + } else { + None + } + } +} + +impl<'a> ExactSizeIterator for VerticesIterMut<'a> { + fn len(&self) -> usize { self.inner.len - self.cur } +} + +/// Helper function to convert a Rust closure to `RTCDisplacementFunctionN` +/// callback. +fn displacement_function(_f: &mut F) -> RTCDisplacementFunctionN +where + D: UserGeometryData, + F: for<'a> FnMut(RTCGeometry, Vertices<'a>, u32, u32, Option<&mut D>), +{ + unsafe extern "C" fn inner(args: *const RTCDisplacementFunctionNArguments) + where + D: UserGeometryData, + F: for<'a> FnMut(RTCGeometry, Vertices<'a>, u32, u32, Option<&mut D>), + { + let raw_data = + &mut *((*args).geometryUserPtr as *mut Mutex) as &mut Mutex; + let geom_data = raw_data.get_mut().unwrap(); + let cb_ptr = geom_data + .subdivision_fns + .as_ref() + .expect( + "User payloads not set! Make sure the geometry was created with kind \ + GeometryKind::SUBDIVISION", + ) + .displacement_fn as *mut F; + if !cb_ptr.is_null() { + let cb = &mut *cb_ptr; + let user_data = { + match geom_data.user_data { + Some(ref user_data) => { + if user_data.data.is_null() || user_data.type_id != TypeId::of::() { + None + } else { + Some(&mut *(user_data.data as *mut D)) + } + } + None => None, + } + }; + let len = (*args).N as usize; + let vertices = Vertices { + len, + u: (*args).u, + v: (*args).v, + ng_x: (*args).Ng_x, + ng_y: (*args).Ng_y, + ng_z: (*args).Ng_z, + p_x: (*args).P_x, + p_y: (*args).P_y, + p_z: (*args).P_z, + marker: PhantomData, + }; + cb( + (*args).geometry, + vertices, + (*args).primID, + (*args).timeStep, + user_data, + ); + } + } + + Some(inner::) +} + +/// Struct holding data for validity masks used in the callback function set by +/// [`Geometry::set_intersect_filter_function`], +/// [`Geometry::set_occluded_filter_function`], +/// [`Geometry::set_intersect_function`] and +/// [`Geometry::set_occluded_function`]. +/// +/// - 0 means it is invalid +/// - -1 means the ray/hit is valid +pub struct ValidityN<'a> { + ptr: *const i32, + len: usize, + marker: PhantomData<&'a [i32]>, +} + +pub struct ValidityNIter<'a, 'b> { + inner: &'b ValidityN<'a>, + cur: usize, +} + +impl<'a> ValidityN<'a> { + pub fn iter<'b>(&'b self) -> ValidityNIter<'a, 'b> { + ValidityNIter { + inner: self, + cur: 0, + } + } + + pub fn iter_mut<'b>(&'b mut self) -> ValidityNIterMut<'a, 'b> { + ValidityNIterMut { + inner: self, + cur: 0, + } + } + + pub const fn len(&self) -> usize { self.len } + + pub const fn is_empty(&self) -> bool { self.len == 0 } +} + +impl<'a> Index for ValidityN<'a> { + type Output = i32; + + fn index(&self, index: usize) -> &Self::Output { + debug_assert!(index < self.len, "index out of bounds"); + unsafe { &*self.ptr.add(index) } + } +} + +impl<'a> IndexMut for ValidityN<'a> { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + unsafe { &mut *(self.ptr.add(index) as *mut i32) } + } +} + +impl<'a, 'b> Iterator for ValidityNIter<'a, 'b> { + type Item = i32; + + fn next(&mut self) -> Option { + if self.cur < self.inner.len { + unsafe { + let valid = *self.inner.ptr.add(self.cur); + self.cur += 1; + Some(valid) + } + } else { + None + } + } +} + +pub struct ValidityNIterMut<'a, 'b> { + inner: &'b mut ValidityN<'a>, + cur: usize, +} + +impl<'a, 'b> Iterator for ValidityNIterMut<'a, 'b> { + type Item = &'a mut i32; + + fn next(&mut self) -> Option { + if self.cur < self.inner.len { + unsafe { + let valid = self.inner.ptr.add(self.cur); + self.cur += 1; + Some(&mut *(valid as *mut i32)) + } + } else { + None } } } diff --git a/src/hermite_curve.rs b/src/hermite_curve.rs deleted file mode 100644 index 86207beb1..000000000 --- a/src/hermite_curve.rs +++ /dev/null @@ -1,178 +0,0 @@ -use std::sync::Arc; - -use crate::buffer::Buffer; -use crate::device::Device; -use crate::geometry::Geometry; -use crate::sys::*; -use crate::{BufferType, CurveType, Format, GeometryType}; - -pub struct HermiteCurve { - device: Arc, - pub(crate) handle: RTCGeometry, - pub vertex_buffer: Buffer<[f32; 4]>, - pub index_buffer: Buffer, - pub tangent_buffer: Buffer<[f32; 4]>, - pub normal_derivative_buffer: Option>, - pub normal_buffer: Option>, -} - -impl HermiteCurve { - pub fn flat( - device: Arc, - num_segments: usize, - num_verts: usize, - use_normals: bool, - ) -> Arc { - HermiteCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::Flat, - use_normals, - ) - } - pub fn round( - device: Arc, - num_segments: usize, - num_verts: usize, - use_normals: bool, - ) -> Arc { - HermiteCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::Round, - use_normals, - ) - } - pub fn normal_oriented( - device: Arc, - num_segments: usize, - num_verts: usize, - ) -> Arc { - HermiteCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::NormalOriented, - true, - ) - } - - fn unanimated( - device: Arc, - num_segments: usize, - num_verts: usize, - curve_type: CurveType, - use_normals: bool, - ) -> Arc { - let h: RTCGeometry; - match curve_type { - CurveType::NormalOriented => { - h = unsafe { - rtcNewGeometry(device.handle, GeometryType::NORMAL_ORIENTED_HERMITE_CURVE) - } - } - CurveType::Round => { - h = unsafe { rtcNewGeometry(device.handle, GeometryType::ROUND_HERMITE_CURVE) } - } - _ => h = unsafe { rtcNewGeometry(device.handle, GeometryType::FLAT_HERMITE_CURVE) }, - }; - let mut vertex_buffer = Buffer::new(device.clone(), num_verts); - let mut index_buffer = Buffer::new(device.clone(), num_segments); - let mut tangent_buffer = Buffer::new(device.clone(), num_verts); - let mut normal_derivative_buffer = None; - let mut normal_buffer = None; - - unsafe { - rtcSetGeometryBuffer( - h, - BufferType::VERTEX, - 0, - Format::FLOAT4, - vertex_buffer.handle, - 0, - 16, - num_verts, - ); - vertex_buffer.set_attachment(h, BufferType::VERTEX, 0); - - rtcSetGeometryBuffer( - h, - BufferType::INDEX, - 0, - Format::UINT, - index_buffer.handle, - 0, - 4, - num_segments, - ); - index_buffer.set_attachment(h, BufferType::INDEX, 0); - - rtcSetGeometryBuffer( - h, - BufferType::TANGENT, - 0, - Format::FLOAT4, - tangent_buffer.handle, - 0, - 16, - num_verts, - ); - tangent_buffer.set_attachment(h, BufferType::TANGENT, 0); - - if use_normals { - let mut temp_normal_buffer = Buffer::new(device.clone(), num_verts); - rtcSetGeometryBuffer( - h, - BufferType::NORMAL, - 0, - Format::FLOAT3, - temp_normal_buffer.handle, - 0, - 12, - num_verts, - ); - temp_normal_buffer.set_attachment(h, BufferType::NORMAL, 0); - normal_buffer = Some(temp_normal_buffer); - - let mut temp_normal_derivative_buffer = Buffer::new(device.clone(), num_verts); - rtcSetGeometryBuffer( - h, - BufferType::NORMAL_DERIVATIVE, - 0, - Format::FLOAT3, - temp_normal_derivative_buffer.handle, - 0, - 12, - num_verts, - ); - temp_normal_derivative_buffer.set_attachment(h, BufferType::NORMAL_DERIVATIVE, 0); - normal_derivative_buffer = Some(temp_normal_derivative_buffer); - } - } - Arc::new(HermiteCurve { - device: device, - handle: h, - vertex_buffer: vertex_buffer, - index_buffer: index_buffer, - tangent_buffer: tangent_buffer, - normal_derivative_buffer: normal_derivative_buffer, - normal_buffer: normal_buffer, - }) - } -} - -impl Geometry for HermiteCurve { - fn handle(&self) -> RTCGeometry { - self.handle - } -} - -impl Drop for HermiteCurve { - fn drop(&mut self) { - unsafe { - rtcReleaseGeometry(self.handle); - } - } -} diff --git a/src/instance.rs b/src/instance.rs deleted file mode 100644 index be90a4aa2..000000000 --- a/src/instance.rs +++ /dev/null @@ -1,52 +0,0 @@ -use std::os::raw; -use std::sync::Arc; - -use crate::geometry::Geometry; -use crate::scene::Scene; -use crate::sys::*; -use crate::{Format, GeometryType}; - -pub struct Instance { - /// The scene being instanced - scene: Arc, - pub(crate) handle: RTCGeometry, -} - -impl Instance { - pub fn unanimated(scene: Arc) -> Arc { - let h = unsafe { rtcNewGeometry(scene.device.handle, GeometryType::INSTANCE) }; - unsafe { - rtcSetGeometryInstancedScene(h, scene.handle); - } - Arc::new(Instance { - handle: h, - scene: scene, - }) - } - pub fn set_transform>(&mut self, transform: Transform) { - let mat: &[f32; 16] = transform.as_ref(); - // Will this be fine if we don't set the number of timesteps? Default should be 1? - unsafe { - rtcSetGeometryTransform( - self.handle, - 0, - Format::FLOAT4X4_COLUMN_MAJOR, - mat.as_ptr() as *const raw::c_void, - ); - } - } -} - -impl Geometry for Instance { - fn handle(&self) -> RTCGeometry { - self.handle - } -} - -impl Drop for Instance { - fn drop(&mut self) { - unsafe { - rtcReleaseGeometry(self.handle); - } - } -} diff --git a/src/intersect_context.rs b/src/intersect_context.rs deleted file mode 100644 index 1ae8db2b5..000000000 --- a/src/intersect_context.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::sys::*; - -/// Per ray-query intersection context. -/// -/// This is used to configure intersection flags, specify a filter callback function, -/// and specify the chain of IDs of the current instance, and to attach arbitrary user -/// data to the query (e.g. per ray data). -/// -/// # Filter Callback -/// -/// A filter function can be specified inside the context. This function is invoked as -/// a second filter stage after the per-geometry intersect or occluded filter function -/// is invoked. Only rays that passed the first filter stage are valid in this second -/// filter stage. Having such a per ray-query filter function can be useful -/// to implement modifications of the behavior of the query, such as collecting all -/// hits or accumulating transparencies. -/// -/// ## Note -/// -/// The support for the context filter function must be enabled for a scene by using -/// the [`RTCSceneFlags::CONTEXT_FILTER_FUNCTION`] flag. -/// -/// In case of instancing this feature has to get enabled also for each instantiated scene. -/// -/// # Hints -/// -/// Best primary ray performance can be obtained by using the ray stream API -/// and setting the intersect context flag to [`RTCIntersectContextFlags::COHERENT`]. -/// For secondary rays, it is typically better to use the [`RTCIntersectContextFlags::INCOHERENT`], -/// unless the rays are known to be coherent(e.g. for primary transparency rays). -pub type IntersectContext = RTCIntersectContext; - -impl IntersectContext { - /// Shortcut to create a IntersectContext with coherent flag set. - pub fn coherent() -> IntersectContext { - IntersectContext::new(RTCIntersectContextFlags::COHERENT) - } - - /// Shortcut to create a IntersectContext with incoherent flag set. - pub fn incoherent() -> IntersectContext { - IntersectContext::new(RTCIntersectContextFlags::INCOHERENT) - } - - pub fn new(flags: RTCIntersectContextFlags) -> IntersectContext { - RTCIntersectContext { - flags, - filter: None, - instID: [u32::MAX; 1], - } - } -} diff --git a/src/lib.rs b/src/lib.rs index c5f866997..9f5f3b2c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,96 +11,354 @@ //! See the [examples/](https://github.com/Twinklebear/embree-rs/tree/master/examples) //! for some example applications using the bindings. -use std::{alloc, mem}; - -pub mod bezier_curve; -pub mod bspline_curve; -pub mod buffer; -mod callback; -pub mod catmull_rom_curve; -pub mod curve; -pub mod device; -pub mod geometry; -pub mod hermite_curve; -pub mod instance; -pub mod intersect_context; -pub mod linear_curve; -pub mod quad_mesh; -pub mod ray; -pub mod ray_packet; -pub mod ray_stream; -pub mod scene; -pub mod soa_ray; +extern crate core; + +use std::{ + alloc, mem, + ops::{Deref, DerefMut}, +}; + +mod buffer; +mod bvh; +mod context; +mod device; +mod error; +mod geometry; +mod ray; +mod scene; + +/// Automatically generated bindings to the Embree C API. #[allow(non_upper_case_globals)] #[allow(non_camel_case_types)] #[allow(non_snake_case)] pub mod sys; -pub mod triangle_mesh; - -pub use bezier_curve::BezierCurve; -pub use bspline_curve::BsplineCurve; -pub use buffer::{Buffer, MappedBuffer}; -pub use catmull_rom_curve::CatmullRomCurve; -pub use curve::CurveType; -pub use device::{Config, Device, FrequencyLevel, Isa}; -pub use geometry::Geometry; -pub use hermite_curve::HermiteCurve; -pub use instance::Instance; -pub use intersect_context::IntersectContext; -pub use linear_curve::LinearCurve; -pub use quad_mesh::QuadMesh; -pub use ray::{Hit, Ray, RayHit}; -pub use ray_packet::{Hit4, Ray4, RayHit4}; -pub use ray_stream::{HitN, RayHitN, RayN}; -pub use scene::Scene; -pub use soa_ray::{ - SoAHit, SoAHitIter, SoAHitIterMut, SoAHitRef, SoARay, SoARayIter, SoARayIterMut, SoARayRef, - SoARayRefMut, -}; -pub use triangle_mesh::TriangleMesh; + +pub use buffer::*; +pub use bvh::*; +pub use context::*; +pub use device::*; +pub use error::*; +pub use geometry::*; +pub use ray::*; +pub use scene::*; // Pull in some cleaned up enum and bitfield types directly, // with prettier aliases -pub use sys::RTCBufferType as BufferType; -pub use sys::RTCBuildQuality as BuildQuality; -pub use sys::RTCDeviceProperty as DeviceProperty; -pub use sys::RTCError as Error; -pub use sys::RTCFormat as Format; -pub use sys::RTCGeometryType as GeometryType; -pub use sys::RTCSubdivisionMode as SubdivisionMode; - -pub use sys::RTCBuildFlags as BuildFlags; -pub use sys::RTCCurveFlags as CurveFlags; -pub use sys::RTCIntersectContextFlags as IntersectContextFlags; -pub use sys::RTCSceneFlags as SceneFlags; - -/// Utility for making specifically aligned vectors -pub fn aligned_vector(len: usize, align: usize) -> Vec { - let t_size = mem::size_of::(); - let t_align = mem::align_of::(); - let layout = if t_align >= align { - alloc::Layout::from_size_align(t_size * len, t_align).unwrap() - } else { - alloc::Layout::from_size_align(t_size * len, align).unwrap() - }; - unsafe { - let mem = alloc::alloc(layout); - assert_eq!((mem as usize) % 16, 0); - Vec::::from_raw_parts(mem as *mut T, len, len) +pub type Bounds = sys::RTCBounds; + +/// Defines the type of slots to assign data buffers to. +/// +/// For most geometry types the [`BufferUsage::INDEX`] slot is used to assign +/// an index buffer, while the [`BufferUsage::VERTEX`] is used to assign the +/// corresponding vertex buffer. +///car +/// The [`BufferUsage::VERTEX_ATTRIBUTE`] slot can get used to assign +/// arbitrary additional vertex data which can get interpolated using the +/// [`Geometry::interpolate`] and [`Geometry::interpolate_n`] API calls. +/// +/// The [`BufferUsage::NORMAL`], [`BufferUsage::TANGENT`], and +/// [`BufferUsage::NORMAL_DERIVATIVE`] are special buffers required to assign +/// per vertex normals, tangents, and normal derivatives for some curve types. +/// +/// The [`BufferUsage::GRID`] buffer is used to assign the grid primitive buffer +/// for grid geometries (see [`GeometryKind::GRID`]). +/// +/// The [`BufferUsage::FACE`], [`BufferUsage::LEVEL`], +/// [`BufferUsage::EDGE_CREASE_INDEX`], [`BufferUsage::EDGE_CREASE_WEIGHT`], +/// [`BufferUsage::VERTEX_CREASE_INDEX`], [`BufferUsage::VERTEX_CREASE_WEIGHT`], +/// and [`BufferUsage::HOLE`] are special buffers required to create subdivision +/// meshes (see [`GeometryKind::SUBDIVISION`]). +/// +/// [`BufferUsage::FLAGS`] can get used to add additional flag per primitive of +/// a geometry, and is currently only used for linear curves. +pub type BufferUsage = sys::RTCBufferType; +pub type BuildQuality = sys::RTCBuildQuality; +pub type BuildFlags = sys::RTCBuildFlags; +pub type CurveFlags = sys::RTCCurveFlags; +pub type DeviceProperty = sys::RTCDeviceProperty; +pub type Error = sys::RTCError; +pub type Format = sys::RTCFormat; +pub type IntersectContextFlags = sys::RTCIntersectContextFlags; +pub type SceneFlags = sys::RTCSceneFlags; +pub type SubdivisionMode = sys::RTCSubdivisionMode; +/// The type of a geometry, used to determine which geometry type to create. +pub type GeometryKind = sys::RTCGeometryType; + +/// Structure that represents a quaternion decomposition of an affine +/// transformation. +/// +/// The affine transformation can be decomposed into three parts: +/// +/// 1. A upper triangular scaling/skew/shift matrix +/// +/// ```text +/// | scale_x skew_xy skew_xz shift_x | +/// | 0 scale_y skew_yz shift_y | +/// | 0 0 scale_z shitf_z | +/// | 0 0 0 1 | +/// ``` +/// +/// 2. A translation matrix +/// ```text +/// | 1 0 0 translation_x | +/// | 0 1 0 translation_y | +/// | 0 0 1 translation_z | +/// | 0 0 0 1 | +/// ``` +/// +/// 3. A rotation matrix R, represented as a quaternion +/// ```text +/// quaternion_r + i * quaternion_i + j * quaternion_j + k * quaternion_k +/// ``` +/// where i, j, k are the imaginary unit vectors. The passed quaternion will +/// be normalized internally. +/// +/// The affine transformation matrix corresponding to a quaternion decomposition +/// is TRS and a point `p = (x, y, z, 1)^T` is transformed as follows: +/// +/// ```text +/// p' = T * R * S * p +/// ``` +pub type QuaternionDecomposition = sys::RTCQuaternionDecomposition; + +impl Default for QuaternionDecomposition { + fn default() -> Self { QuaternionDecomposition::identity() } +} + +impl QuaternionDecomposition { + /// Create a new quaternion decomposition with the identity transformation. + pub fn identity() -> Self { + QuaternionDecomposition { + scale_x: 1.0, + scale_y: 1.0, + scale_z: 1.0, + skew_xy: 0.0, + skew_xz: 0.0, + skew_yz: 0.0, + shift_x: 0.0, + shift_y: 0.0, + shift_z: 0.0, + quaternion_r: 1.0, + quaternion_i: 0.0, + quaternion_j: 0.0, + quaternion_k: 0.0, + translation_x: 0.0, + translation_y: 0.0, + translation_z: 0.0, + } + } + + /// Returns the scale part of the decomposition. + pub fn scale(&self) -> [f32; 3] { [self.scale_x, self.scale_y, self.scale_z] } + + /// Returns the skew part of the decomposition. + pub fn skew(&self) -> [f32; 3] { [self.skew_xy, self.skew_xz, self.skew_yz] } + + /// Returns the shift part of the decomposition. + pub fn shift(&self) -> [f32; 3] { [self.shift_x, self.shift_y, self.shift_z] } + + /// Returns the translation part of the decomposition. + pub fn quaternion(&self) -> [f32; 4] { + [ + self.quaternion_r, + self.quaternion_i, + self.quaternion_j, + self.quaternion_k, + ] + } + + /// Set the quaternion part of the decomposition. + pub fn set_quaternion(&mut self, quaternion: [f32; 4]) { + self.quaternion_r = quaternion[0]; + self.quaternion_i = quaternion[1]; + self.quaternion_j = quaternion[2]; + self.quaternion_k = quaternion[3]; + } + + /// Set the scaling part of the decomposition. + pub fn set_scale(&mut self, scale: [f32; 3]) { + self.scale_x = scale[0]; + self.scale_y = scale[1]; + self.scale_z = scale[2]; + } + + /// Set the skew part of the decomposition. + pub fn set_skew(&mut self, skew: [f32; 3]) { + self.skew_xy = skew[0]; + self.skew_xz = skew[1]; + self.skew_yz = skew[2]; + } + + /// Set the shift part of the decomposition. + pub fn set_shift(&mut self, shift: [f32; 3]) { + self.shift_x = shift[0]; + self.shift_y = shift[1]; + self.shift_z = shift[2]; + } + + /// Set the translation part of the decomposition. + pub fn set_translation(&mut self, translation: [f32; 3]) { + self.translation_x = translation[0]; + self.translation_y = translation[1]; + self.translation_z = translation[2]; } } -pub fn aligned_vector_init(len: usize, align: usize, init: T) -> Vec { - let mut v = aligned_vector::(len, align); - for x in v.iter_mut() { - *x = init; + +/// The invalid ID for Embree intersection results (e.g. `Hit::geomID`, +/// `Hit::primID`, etc.) +pub const INVALID_ID: u32 = u32::MAX; + +impl Default for Bounds { + fn default() -> Self { + Bounds { + lower_x: f32::INFINITY, + lower_y: f32::INFINITY, + lower_z: f32::INFINITY, + align0: 0.0, + upper_x: f32::INFINITY, + upper_y: f32::INFINITY, + upper_z: f32::INFINITY, + align1: 0.0, + } + } +} + +impl Bounds { + /// Returns the lower bounds of the bounding box. + pub fn lower(&self) -> [f32; 3] { [self.lower_x, self.lower_y, self.lower_z] } + + /// Returns the upper bounds of the bounding box. + pub fn upper(&self) -> [f32; 3] { [self.upper_x, self.upper_y, self.upper_z] } +} + +/// Object used to traverses the BVH and calls a user defined callback function +/// for each primitive of the scene that intersects the query domain. +/// +/// See [`Scene::point_query`] for more information. +pub type PointQuery = sys::RTCPointQuery; + +/// Primitives that can be used to build a BVH. +pub type BuildPrimitive = sys::RTCBuildPrimitive; + +/// Utility for making specifically aligned vector. +/// +/// This is a growable, dynamically allocated, arbitrarily aligned container. +/// Please use [`AlignedArray`] if you only need a 16 bytes aligned, fix-sized +/// storage. +/// +/// This is a wrapper around `Vec` that ensures the alignment of the vector. +/// The reason for this is that memory must be deallocated with the +/// same alignment as it was allocated with. This is not guaranteed if +/// we allocate a memory block with the alignment then cast it to a +/// `Vec` of `T` and then drop it, since the `Vec` will deallocate the +/// memory with the alignment of `T`. +pub struct AlignedVector { + vec: Vec, + layout: alloc::Layout, +} + +impl AlignedVector { + pub fn new(len: usize, align: usize) -> Self { + let t_size = mem::size_of::(); + let t_align = mem::align_of::(); + let layout = if t_align >= align { + alloc::Layout::from_size_align(t_size * len, t_align).unwrap() + } else { + alloc::Layout::from_size_align(t_size * len, align).unwrap() + }; + unsafe { + AlignedVector { + vec: Vec::from_raw_parts(alloc::alloc(layout) as *mut T, len, len), + layout, + } + } + } + + pub fn new_init(len: usize, align: usize, init: T) -> Self + where + T: Copy, + { + let mut v = Self::new(len, align); + for x in v.iter_mut() { + *x = init; + } + v + } + + /// Returns the alignment of the vector. + pub fn alignment(&self) -> usize { self.layout.align() } +} + +impl Deref for AlignedVector { + type Target = Vec; + + fn deref(&self) -> &Self::Target { &self.vec } +} + +impl DerefMut for AlignedVector { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.vec } +} + +impl Drop for AlignedVector { + fn drop(&mut self) { + unsafe { + let mut vec = mem::replace(&mut self.vec, Vec::new()); + let raw = vec.as_mut_ptr() as *mut u8; + alloc::dealloc(raw, self.layout); + mem::forget(vec); + } } - v } #[test] fn test_aligned_vector_alloc() { - let v = aligned_vector_init::(24, 16, 1.0); + let v = AlignedVector::::new_init(24, 16, 1.0); for x in v.iter() { assert_eq!(*x, 1.0); } } + +/// 16 bytes aligned with known size at compile time. +#[repr(align(16))] +pub struct AlignedArray(pub [T; N]); + +impl Deref for AlignedArray { + type Target = [T; N]; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl DerefMut for AlignedArray { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } +} + +/// Utility function to normalise a vector. +#[inline(always)] +fn normalise_vector3(v: [f32; 3]) -> [f32; 3] { + let len_sq = v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; + let len_inv = if len_sq.is_finite() && len_sq != 0.0 { + len_sq.sqrt().recip() + } else { + 0.0 + }; + + [v[0] * len_inv, v[1] * len_inv, v[2] * len_inv] +} + +#[test] +fn test_normalise_vector3() { + let v = normalise_vector3([1.0, 2.0, 3.0]); + assert_eq!(v[0], 0.26726124); + assert_eq!(v[1], 0.5345225); + assert_eq!(v[2], 0.8017837); + + let v = normalise_vector3([0.0, 0.0, 0.0]); + assert_eq!(v[0], 0.0); + assert_eq!(v[1], 0.0); + assert_eq!(v[2], 0.0); + + let v = normalise_vector3([1.0, 0.0, 0.0]); + assert_eq!(v[0], 1.0); + assert_eq!(v[1], 0.0); + assert_eq!(v[2], 0.0); +} diff --git a/src/linear_curve.rs b/src/linear_curve.rs deleted file mode 100644 index c50c1ab55..000000000 --- a/src/linear_curve.rs +++ /dev/null @@ -1,160 +0,0 @@ -use std::sync::Arc; - -use crate::buffer::Buffer; -use crate::device::Device; -use crate::geometry::Geometry; -use crate::sys::*; -use crate::{BufferType, CurveType, Format, GeometryType}; - -pub struct LinearCurve { - device: Arc, - pub(crate) handle: RTCGeometry, - pub vertex_buffer: Buffer<[f32; 4]>, - pub index_buffer: Buffer, - pub flag_buffer: Buffer, - pub normal_buffer: Option>, -} - -impl LinearCurve { - pub fn flat( - device: Arc, - num_segments: usize, - num_verts: usize, - use_normals: bool, - ) -> Arc { - LinearCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::Flat, - use_normals, - ) - } - pub fn round( - device: Arc, - num_segments: usize, - num_verts: usize, - use_normals: bool, - ) -> Arc { - LinearCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::Round, - use_normals, - ) - } - pub fn cone( - device: Arc, - num_segments: usize, - num_verts: usize, - use_normals: bool, - ) -> Arc { - LinearCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::Cone, - use_normals, - ) - } - fn unanimated( - device: Arc, - num_segments: usize, - num_verts: usize, - curve_type: CurveType, - use_normals: bool, - ) -> Arc { - let h: RTCGeometry; - match curve_type { - CurveType::Cone => { - h = unsafe { rtcNewGeometry(device.handle, GeometryType::CONE_LINEAR_CURVE) } - } - CurveType::Round => { - h = unsafe { rtcNewGeometry(device.handle, GeometryType::ROUND_LINEAR_CURVE) } - } - _ => h = unsafe { rtcNewGeometry(device.handle, GeometryType::FLAT_LINEAR_CURVE) }, - }; - let mut vertex_buffer = Buffer::new(device.clone(), num_verts); - let mut index_buffer = Buffer::new(device.clone(), num_segments); - let mut flag_buffer = Buffer::new(device.clone(), num_segments); - let mut normal_buffer = None; - - unsafe { - rtcSetGeometryBuffer( - h, - BufferType::VERTEX, - 0, - Format::FLOAT4, - vertex_buffer.handle, - 0, - 16, - num_verts, - ); - vertex_buffer.set_attachment(h, BufferType::VERTEX, 0); - - rtcSetGeometryBuffer( - h, - BufferType::INDEX, - 0, - Format::UINT, - index_buffer.handle, - 0, - 4, - num_segments, - ); - index_buffer.set_attachment(h, BufferType::INDEX, 0); - - rtcSetGeometryBuffer( - h, - BufferType::FLAGS, - 0, - Format::UCHAR, - flag_buffer.handle, - 0, - 1, - num_verts, - ); - flag_buffer.set_attachment(h, BufferType::FLAGS, 0); - - if use_normals { - let mut temp_normal_buffer = Buffer::new(device.clone(), num_verts); - rtcSetGeometryBuffer( - h, - BufferType::NORMAL, - 0, - Format::FLOAT3, - temp_normal_buffer.handle, - 0, - 12, - num_verts, - ); - temp_normal_buffer.set_attachment(h, BufferType::NORMAL, 0); - normal_buffer = Some(temp_normal_buffer); - }; - } - - Arc::new(LinearCurve { - device: device, - handle: h, - vertex_buffer: vertex_buffer, - index_buffer: index_buffer, - flag_buffer: flag_buffer, - normal_buffer: normal_buffer, - }) - } -} - -impl Geometry for LinearCurve { - fn handle(&self) -> RTCGeometry { - self.handle - } -} - -impl Drop for LinearCurve { - fn drop(&mut self) { - unsafe { - rtcReleaseGeometry(self.handle); - } - } -} diff --git a/src/quad_mesh.rs b/src/quad_mesh.rs deleted file mode 100644 index 8fb3854a4..000000000 --- a/src/quad_mesh.rs +++ /dev/null @@ -1,67 +0,0 @@ -use std::sync::Arc; - -use crate::buffer::Buffer; -use crate::device::Device; -use crate::geometry::Geometry; -use crate::sys::*; -use crate::{BufferType, Format, GeometryType}; - -pub struct QuadMesh { - device: Arc, - pub(crate) handle: RTCGeometry, - pub vertex_buffer: Buffer<[f32; 4]>, - pub index_buffer: Buffer<[u32; 4]>, -} - -impl QuadMesh { - pub fn unanimated(device: Arc, num_quads: usize, num_verts: usize) -> Arc { - let h = unsafe { rtcNewGeometry(device.handle, GeometryType::QUAD) }; - let mut vertex_buffer = Buffer::new(device.clone(), num_verts); - let mut index_buffer = Buffer::new(device.clone(), num_quads); - unsafe { - rtcSetGeometryBuffer( - h, - BufferType::VERTEX, - 0, - Format::FLOAT3, - vertex_buffer.handle, - 0, - 16, - num_verts, - ); - vertex_buffer.set_attachment(h, BufferType::VERTEX, 0); - - rtcSetGeometryBuffer( - h, - BufferType::INDEX, - 0, - Format::UINT4, - index_buffer.handle, - 0, - 16, - num_quads, - ); - index_buffer.set_attachment(h, BufferType::INDEX, 0); - } - Arc::new(QuadMesh { - device: device, - handle: h, - vertex_buffer: vertex_buffer, - index_buffer: index_buffer, - }) - } -} - -impl Geometry for QuadMesh { - fn handle(&self) -> RTCGeometry { - self.handle - } -} - -impl Drop for QuadMesh { - fn drop(&mut self) { - unsafe { - rtcReleaseGeometry(self.handle); - } - } -} diff --git a/src/ray.rs b/src/ray.rs index 1364ba0d8..66814b3a2 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -1,26 +1,122 @@ -use std::{f32, u32}; +use crate::{normalise_vector3, sys, INVALID_ID}; -use crate::sys; +pub mod packet; +mod soa; +pub mod stream; +pub use packet::*; +pub use soa::*; +pub use stream::*; + +/// New type alias for [`sys::RTCRay`] that provides some convenience +/// methods. +/// +/// The ray contains the origin ([`org_x`](`sys::RTCRay::org_x`), +/// [`org_y`](`sys::RTCRay::org_y`), [`org_z`](`sys::RTCRay::org_z`) members), +/// the direction vector ([`dir_x`](`sys::RTCRay::dir_x`), +/// [`dir_y`](`sys::RTCRay::dir_y`), [`dir_z`](`sys::RTCRay::dir_z`) members), +/// the ray segment ([`tnear`](`sys::RTCRay::tnear`) and +/// [`tfar`](`sys::RTCRay::tfar`) members). The ray direction does NOT need +/// to be normalized, and only the parameter range specified by +/// [`tnear`](`sys::RTCRay::tnear`) and [`tfar`](`sys::RTCRay::tfar`) is +/// considered valid. +/// +/// The ray segment must be in the range \[0, \inf\], thus ranges start +/// behind the ray origin are not allowed, but ranges can reach to infinity. +/// For rays inside a ray stream, `tfar` < `tnear` identifies an *inactive* +/// ray. +/// +/// Ray identifiers are used to identify rays inside a callback function, +/// event if the order of rays inside a ray packet or stream has changed. +/// +/// [`packet`](`crate::ray::packet`) defines types in SOA (structure of array) +/// layout for ray packets of size 4 ([`Ray4`]), size 8 ([`Ray8`]), +/// and size 16 ([`Ray16`]). +/// +/// See [`sys::RTCRay`] for more details. pub type Ray = sys::RTCRay; -pub type Hit = sys::RTCHit; -pub type RayHit = sys::RTCRayHit; impl Ray { - /// Create a new ray starting at `origin` and heading in direction `dir` - pub fn new(origin: [f32; 3], dir: [f32; 3]) -> Ray { - Ray::segment(origin, dir, 0.0, f32::INFINITY) - } - pub fn segment(origin: [f32; 3], dir: [f32; 3], tnear: f32, tfar: f32) -> Ray { - sys::RTCRay { + /// Creates a new ray. + /// + /// Basic constructor that initializes all fields of the ray. + pub fn new( + origin: [f32; 3], + direction: [f32; 3], + near: f32, + far: f32, + time: f32, + mask: u32, + id: u32, + ) -> Ray { + Ray { org_x: origin[0], org_y: origin[1], org_z: origin[2], - dir_x: dir[0], - dir_y: dir[1], - dir_z: dir[2], - tnear: tnear, - tfar: tfar, + tnear: near, + dir_x: direction[0], + dir_y: direction[1], + dir_z: direction[2], + time, + tfar: far, + mask, + id, + flags: 0, + } + } + + /// Creates a new ray segment. + /// + /// The ray starting at `origin` and heading in direction `dir` + /// with a segment starting at `tnear` and ending at `tfar`. + pub fn segment(origin: [f32; 3], direction: [f32; 3], tnear: f32, tfar: f32) -> Ray { + Self::new(origin, direction, tnear, tfar, 0.0, u32::MAX, 0) + } + + /// Creates a new segment of ray with an ID. + pub fn segment_with_id( + origin: [f32; 3], + direction: [f32; 3], + tnear: f32, + tfar: f32, + id: u32, + ) -> Ray { + Self::new(origin, direction, tnear, tfar, 0.0, u32::MAX, id) + } + + /// Returns the origin of the ray. + pub fn org(&self) -> [f32; 3] { [self.org_x, self.org_y, self.org_z] } + + /// Returns the direction (un-normalized) of the ray. + pub fn dir(&self) -> [f32; 3] { [self.dir_x, self.dir_y, self.dir_z] } + + /// Returns the normalized direction of the ray. + /// + /// Do not use this method to calculate the hit point, use [`dir`] instead. + pub fn unit_dir(&self) -> [f32; 3] { normalise_vector3(self.dir()) } + + /// Calculates the hit point from the ray and the hit distance. + pub fn hit_point(&self) -> [f32; 3] { + let t = self.tfar; + [ + self.org_x + self.dir_x * t, + self.org_y + self.dir_y * t, + self.org_z + self.dir_z * t, + ] + } +} + +impl Default for Ray { + fn default() -> Self { + Ray { + org_x: 0.0, + org_y: 0.0, + org_z: 0.0, + tnear: 0.0, + dir_x: 0.0, + dir_y: 0.0, + dir_z: 0.0, + tfar: f32::INFINITY, time: 0.0, mask: u32::MAX, id: 0, @@ -29,29 +125,90 @@ impl Ray { } } -impl Hit { - pub fn new() -> Hit { - sys::RTCHit { +/// New type alias for [`sys::RTCHit`] that provides some convenience +/// methods. +/// +/// [`Hit`] defines the type of a ray/primitive intersection result. The +/// hit contains the un-normalized geometric normal in object space at the +/// hit location ([`Ng_x`]([`sys::RTCHit::Ng_x`]), +/// [`Ng_y`]([`sys::RTCHit::Ng_y`]), [`Ng_z`]([`sys::RTCHit::Ng_z`]) members), +/// the barycentric u/v coordinates of the hit ([`u`]([`sys::RTCHit::u`]) and +/// [`v`]([`sys::RTCHit::v`]) members), as well as the primitive ID +/// ([`primID`]([`sys::RTCHit::primID`]) member), geometry ID +/// (`geomID`, [`sys::RTCHit::geomID`] member), and instance ID +/// stack (`instID`, [`sys::RTCHit::instID`] member) of the hit. +/// The parametric intersection distance is not stored inside the hit, +/// but stored inside the `tfar`([`sys::RTCRay::tfar`]) member of the ray. +/// +/// There exists structures in SOA (structure of array) layout for hit packets +/// of size 4 ([`Hit4`]), size 8 ([`Hit8`]), and size 16 ([`Hit16`]). +/// +/// See [`sys::RTCHit`] for more details. +pub type Hit = sys::RTCHit; + +impl Default for Hit { + fn default() -> Self { + Hit { Ng_x: 0.0, Ng_y: 0.0, Ng_z: 0.0, u: 0.0, v: 0.0, - primID: u32::MAX, - geomID: u32::MAX, - instID: [u32::MAX; 1], + primID: INVALID_ID, + geomID: INVALID_ID, + instID: [INVALID_ID; 1], } } - pub fn hit(&self) -> bool { - self.geomID != u32::MAX - } } +impl Hit { + /// Returns the normal at the hit point (un-normalized). + pub fn normal(&self) -> [f32; 3] { [self.Ng_x, self.Ng_y, self.Ng_z] } + + /// Returns the normalized normal at the hit point. + pub fn unit_normal(&self) -> [f32; 3] { normalise_vector3(self.normal()) } + + /// Returns the barycentric u/v coordinates of the hit. + pub fn barycentric(&self) -> [f32; 2] { [self.u, self.v] } + + /// Returns if the hit is valid, i.e. the ray hit something. + pub fn is_valid(&self) -> bool { self.geomID != INVALID_ID } +} + +/// New type alias for [`sys::RTCRayHit`] that provides some convenience +/// methods. +/// +/// A combined single ray/hit structure. This structure is used as input +/// for the `intersect-type` functions and stores the ray to intersect +/// and some hit fields that hold the intersection result afterwards. +/// +/// [`packet`](`crate::ray::packet`) defines types in SOA (structure of array) +/// layout for ray/hit packets of size 4 [`RayHit4`], size 8 [`RayHit8`], and +/// size 16 [`RayHit16`]. +/// +/// See [`sys::RTCRayHit`] for more details. +pub type RayHit = sys::RTCRayHit; + impl RayHit { - pub fn new(ray: Ray) -> RayHit { - sys::RTCRayHit { + /// Creates a new [`RayHit`] ready to be used in an intersection query. + pub fn from_ray(ray: Ray) -> RayHit { + RayHit { ray, - hit: Hit::new(), + hit: Hit::default(), } } } + +#[allow(clippy::derivable_impls)] +impl Default for RayHit { + fn default() -> Self { + RayHit { + ray: Ray::default(), + hit: Hit::default(), + } + } +} + +impl From for RayHit { + fn from(value: Ray) -> Self { RayHit::from_ray(value) } +} diff --git a/src/ray/packet.rs b/src/ray/packet.rs new file mode 100644 index 000000000..77f4d0beb --- /dev/null +++ b/src/ray/packet.rs @@ -0,0 +1,589 @@ +//! Ray packet types and traits. + +use crate::{ + normalise_vector3, sys, Hit, Ray, RayHit, SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, + SoARayIterMut, INVALID_ID, +}; +use std::marker::PhantomData; + +/// A ray packet of size 4. +pub type Ray4 = sys::RTCRay4; + +/// A hit packet of size 4. +pub type Hit4 = sys::RTCHit4; + +/// A ray/hit packet of size 4. +pub type RayHit4 = sys::RTCRayHit4; + +/// A ray packet of size 8. +pub type Ray8 = sys::RTCRay8; + +/// A hit packet of size 8. +pub type Hit8 = sys::RTCHit8; + +/// A ray/hit packet of size 8. +pub type RayHit8 = sys::RTCRayHit8; + +/// A ray packet of size 16. +pub type Ray16 = sys::RTCRay16; + +/// A hit packet of size 16. +pub type Hit16 = sys::RTCHit16; + +/// A ray/hit packet of size 16. +pub type RayHit16 = sys::RTCRayHit16; + +/// Represents a packet of rays. +/// +/// Used as a trait bound for functions that operate on ray packets. +/// See [`occluded_stream_aos`](`crate::Scene::occluded_stream_aos`) and +/// [`intersect_stream_aos`](`crate::Scene::intersect_stream_aos`). +pub trait RayPacket: Sized { + const LEN: usize; +} + +/// Represents a packet of hits. +pub trait HitPacket: Sized { + const LEN: usize; +} + +/// Represents a packet of ray/hit pairs. +pub trait RayHitPacket: Sized { + type Ray: RayPacket; + type Hit: HitPacket; + const LEN: usize = Self::Ray::LEN; +} + +macro_rules! impl_packet_traits { + ($($ray:ident, $hit:ident, $rayhit:ident, $n:expr);*) => { + $( + impl RayPacket for $ray { + const LEN: usize = $n; + } + + impl HitPacket for $hit { + const LEN: usize = $n; + } + + impl RayHitPacket for $rayhit { + type Ray = $ray; + type Hit = $hit; + } + )* + } +} + +impl_packet_traits! { + Ray, Hit, RayHit, 1; + Ray4, Hit4, RayHit4, 4; + Ray8, Hit8, RayHit8, 8; + Ray16, Hit16, RayHit16, 16 +} + +macro_rules! impl_ray_packets { + ($($t:ident, $n:expr);*) => { + $( + impl $t { + pub const fn new(origin: [[f32; 3]; $n], dir: [[f32; 3]; $n]) -> $t { + $t::segment(origin, dir, [0.0; $n], [f32::INFINITY; $n]) + } + + pub const fn segment(origin: [[f32; 3]; $n], dir: [[f32; 3]; $n], tnear: [f32; $n], tfar: [f32; $n]) -> $t { + let [org_x, org_y, org_z, dir_x, dir_y, dir_z] = { + let mut elems = [[0.0f32; $n]; 6]; + let mut i = 0; + while i < $n { + elems[0][i] = origin[i][0]; + elems[1][i] = origin[i][1]; + elems[2][i] = origin[i][2]; + elems[3][i] = dir[i][0]; + elems[4][i] = dir[i][1]; + elems[5][i] = dir[i][2]; + i += 1; + } + elems + }; + Self { + org_x, + org_y, + org_z, + dir_x, + dir_y, + dir_z, + tnear, + tfar, + time: [0.0; $n], + mask: [u32::MAX; $n], + id: [0; $n], + flags: [0; $n], + } + } + + pub const fn empty() -> $t { + $t::segment( + [[0.0, 0.0, 0.0]; $n], + [[0.0, 0.0, 0.0]; $n], + [0.0; $n], + [f32::INFINITY; $n], + ) + } + + pub fn iter(&self) -> SoARayIter<$t> { SoARayIter::new(self, $n) } + + pub fn iter_mut(&mut self) -> SoARayIterMut<$t> { SoARayIterMut::new(self, $n) } + } + + impl Default for $t { + fn default() -> Self { Self::empty() } + } + + impl SoARay for $t { + fn org(&self, i: usize) -> [f32; 3] { [self.org_x[i], self.org_y[i], self.org_z[i]] } + + fn set_org(&mut self, i: usize, o: [f32; 3]) { + self.org_x[i] = o[0]; + self.org_y[i] = o[1]; + self.org_z[i] = o[2]; + } + + fn tnear(&self, i: usize) -> f32 { self.tnear[i] } + + fn set_tnear(&mut self, i: usize, t: f32) { self.tnear[i] = t } + + fn dir(&self, i: usize) -> [f32; 3] { [self.dir_x[i], self.dir_y[i], self.dir_z[i]] } + + fn set_dir(&mut self, i: usize, d: [f32; 3]) { + self.dir_x[i] = d[0]; + self.dir_y[i] = d[1]; + self.dir_z[i] = d[2]; + } + + fn time(&self, i: usize) -> f32 { self.time[i] } + + fn set_time(&mut self, i: usize, t: f32) { self.time[i] = t } + + fn tfar(&self, i: usize) -> f32 { self.tfar[i] } + + fn set_tfar(&mut self, i: usize, t: f32) { self.tfar[i] = t} + + fn mask(&self, i: usize) -> u32 { self.mask[i] } + + fn set_mask(&mut self, i: usize, m: u32) { self.mask[i] = m } + + fn id(&self, i: usize) -> u32 { self.id[i] } + + fn set_id(&mut self, i: usize, id: u32) { self.id[i] = id } + + fn flags(&self, i: usize) -> u32 { self.flags[i] } + + fn set_flags(&mut self, i: usize, f: u32) { self.flags[i] = f } + } + )* + }; +} + +impl_ray_packets!(Ray4, 4; Ray8, 8; Ray16, 16); + +macro_rules! impl_hit_packets { + ($($t:ident, $n:expr);*) => { + $( + impl $t { + pub fn new() -> $t { + $t { + Ng_x: [0.0; $n], + Ng_y: [0.0; $n], + Ng_z: [0.0; $n], + u: [0.0; $n], + v: [0.0; $n], + primID: [INVALID_ID; $n], + geomID: [INVALID_ID; $n], + instID: [[INVALID_ID; $n]], + } + } + pub fn any_hit(&self) -> bool { self.iter_validity().any(|h| h) } + + pub fn iter_validity(&self) -> impl Iterator + '_ { + self.geomID.iter().map(|g| *g != INVALID_ID) + } + + pub fn iter(&self) -> SoAHitIter<$t> { SoAHitIter::new(self, $n) } + + pub fn iter_hits(&self) -> impl Iterator> { + SoAHitIter::new(self, 4).filter(|h| h.is_valid()) + } + } + + impl Default for $t { + fn default() -> Self { Self::new() } + } + + impl SoAHit for $t { + fn normal(&self, i: usize) -> [f32; 3] { [self.Ng_x[i], self.Ng_y[i], self.Ng_z[i]] } + + fn unit_normal(&self, i: usize) -> [f32; 3] { + let n = self.normal(i); + let len = n[0] * n[0] + n[1] * n[1] + n[2] * n[2]; + if len > 0.0 { + let inv_len = 1.0 / len.sqrt(); + [n[0] * inv_len, n[1] * inv_len, n[2] * inv_len] + } else { + [0.0, 0.0, 0.0] + } + } + + fn set_normal(&mut self, i: usize, n: [f32; 3]) { + self.Ng_x[i] = n[0]; + self.Ng_y[i] = n[1]; + self.Ng_z[i] = n[2]; + } + + fn u(&self, i: usize) -> f32 { self.u[i] } + + fn v(&self, i: usize) -> f32 { self.v[i] } + + fn uv(&self, i: usize) -> [f32; 2] { [self.u[i], self.v[i]] } + + fn set_u(&mut self, i: usize, u: f32) { self.u[i] = u; } + + fn set_v(&mut self, i: usize, v: f32) { self.v[i] = v; } + + fn set_uv(&mut self, i: usize, uv: [f32; 2]) { + self.u[i] = uv[0]; + self.v[i] = uv[1]; + } + + + fn prim_id(&self, i: usize) -> u32 { self.primID[i] } + + fn set_prim_id(&mut self, i: usize, id: u32) { self.primID[i] = id; } + + fn geom_id(&self, i: usize) -> u32 { self.geomID[i] } + + fn set_geom_id(&mut self, i: usize, id: u32) { self.geomID[i] = id; } + + fn inst_id(&self, i: usize) -> u32 { self.instID[0][i] } + + fn set_inst_id(&mut self, i: usize, id: u32) { self.instID[0][i] = id; } + } + )* + }; +} + +impl_hit_packets!(Hit4, 4; Hit8, 8; Hit16, 16); + +impl RayHit4 { + pub fn new(ray: Ray4) -> RayHit4 { + sys::RTCRayHit4 { + ray, + hit: Hit4::new(), + } + } + pub fn iter(&self) -> std::iter::Zip, SoAHitIter> { + self.ray.iter().zip(self.hit.iter()) + } +} + +/// Ray packet of runtime size. +/// +/// It is used to represent a packet of rays that is not known at compile +/// time, generally used as an argument to callback functions. The size +/// of the packet can only be either 1, 4, 8, or 16. +/// +/// For ray streams, use [`RayNp`](`crate::ray::RayNp`). +pub struct RayN<'a> { + pub(crate) ptr: *mut sys::RTCRayN, + pub(crate) len: usize, + pub(crate) marker: PhantomData<&'a mut sys::RTCRayN>, +} + +impl<'a> RayN<'a> { + /// Returns the number of rays in the packet. + /// + /// Can be either 1, 4, 8, or 16. + pub fn len(&self) -> usize { self.len } + + /// Returns true if the packet is empty. + pub fn is_empty(&self) -> bool { self.len == 0 } + + /// Returns the hit point of the `i`th ray. + pub fn hit_point(&self, i: usize) -> [f32; 3] { + debug_assert!(i < self.len, "index out of bounds"); + let mut p = self.org(i); + let d = self.dir(i); + let t = self.tfar(i); + p[0] += d[0] * t; + p[1] += d[1] * t; + p[2] += d[2] * t; + p + } +} + +impl<'a> SoARay for RayN<'a> { + fn org(&self, i: usize) -> [f32; 3] { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + let ptr = self.ptr as *const f32; + [ + *ptr.add(i), + *ptr.add(self.len + i), + *ptr.add(2 * self.len + i), + ] + } + } + + fn set_org(&mut self, i: usize, o: [f32; 3]) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + let ptr = self.ptr as *mut f32; + *ptr.add(i) = o[0]; + *ptr.add(self.len + i) = o[1]; + *ptr.add(2 * self.len + i) = o[2]; + } + } + + fn dir(&self, i: usize) -> [f32; 3] { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + let ptr = self.ptr as *const f32; + [ + *ptr.add(4 * self.len + i), + *ptr.add(5 * self.len + i), + *ptr.add(6 * self.len + i), + ] + } + } + + fn set_dir(&mut self, i: usize, d: [f32; 3]) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + let ptr = self.ptr as *mut f32; + *ptr.add(4 * self.len + i) = d[0]; + *ptr.add(5 * self.len + i) = d[1]; + *ptr.add(6 * self.len + i) = d[2]; + } + } + + fn tnear(&self, i: usize) -> f32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const f32).add(3 * self.len + i) } + } + + fn set_tnear(&mut self, i: usize, t: f32) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + *(self.ptr as *mut f32).add(3 * self.len + i) = t; + } + } + + fn tfar(&self, i: usize) -> f32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const f32).add(8 * self.len + i) } + } + + fn set_tfar(&mut self, i: usize, t: f32) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + *(self.ptr as *mut f32).add(8 * self.len + i) = t; + } + } + + fn time(&self, i: usize) -> f32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const f32).add(7 * self.len + i) } + } + + fn set_time(&mut self, i: usize, t: f32) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + *(self.ptr as *mut f32).add(7 * self.len + i) = t; + } + } + + fn mask(&self, i: usize) -> u32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const u32).add(9 * self.len + i) } + } + + fn set_mask(&mut self, i: usize, m: u32) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + *(self.ptr as *mut u32).add(9 * self.len + i) = m; + } + } + + fn id(&self, i: usize) -> u32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const u32).add(10 * self.len + i) } + } + + fn set_id(&mut self, i: usize, id: u32) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + *(self.ptr as *mut u32).add(10 * self.len + i) = id; + } + } + + fn flags(&self, i: usize) -> u32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const u32).add(11 * self.len + i) } + } + + fn set_flags(&mut self, i: usize, f: u32) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + *(self.ptr as *mut u32).add(11 * self.len + i) = f; + } + } +} + +/// Hit packet of runtime size. +/// +/// It is used to represent a packet of hits that is not known at compile +/// time, generally used as an argument to callback functions. The size +/// of the packet can only be either 1, 4, 8, or 16. +pub struct HitN<'a> { + pub(crate) ptr: *mut sys::RTCHitN, + pub(crate) len: usize, + pub(crate) marker: PhantomData<&'a mut sys::RTCHitN>, +} + +impl<'a> SoAHit for HitN<'a> { + fn normal(&self, i: usize) -> [f32; 3] { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + [ + *(self.ptr as *const f32).add(i), + *(self.ptr as *const f32).add(self.len + i), + *(self.ptr as *const f32).add(2 * self.len + i), + ] + } + } + + fn unit_normal(&self, i: usize) -> [f32; 3] { normalise_vector3(self.normal(i)) } + + fn set_normal(&mut self, i: usize, n: [f32; 3]) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + let ptr = self.ptr as *mut f32; + *(ptr).add(i) = n[0]; + *(ptr).add(self.len + i) = n[1]; + *(ptr).add(2 * self.len + i) = n[2]; + } + } + + fn uv(&self, i: usize) -> [f32; 2] { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + [ + *(self.ptr as *const f32).add(3 * self.len + i), + *(self.ptr as *const f32).add(4 * self.len + i), + ] + } + } + + fn u(&self, i: usize) -> f32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const f32).add(3 * self.len + i) } + } + + fn v(&self, i: usize) -> f32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const f32).add(4 * self.len + i) } + } + + fn set_uv(&mut self, i: usize, uv: [f32; 2]) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + let ptr = self.ptr as *mut f32; + *(ptr).add(3 * self.len + i) = uv[0]; + *(ptr).add(4 * self.len + i) = uv[1]; + } + } + + fn set_u(&mut self, i: usize, u: f32) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + *(self.ptr as *mut f32).add(3 * self.len + i) = u; + } + } + + fn set_v(&mut self, i: usize, v: f32) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + *(self.ptr as *mut f32).add(4 * self.len + i) = v; + } + } + + fn prim_id(&self, i: usize) -> u32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const u32).add(5 * self.len + i) } + } + + fn set_prim_id(&mut self, i: usize, id: u32) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + *(self.ptr as *mut u32).add(5 * self.len + i) = id; + } + } + + fn geom_id(&self, i: usize) -> u32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const u32).add(6 * self.len + i) } + } + + fn set_geom_id(&mut self, i: usize, id: u32) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + *(self.ptr as *mut u32).add(6 * self.len + i) = id; + } + } + + fn inst_id(&self, i: usize) -> u32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const u32).add(7 * self.len + i) } + } + + fn set_inst_id(&mut self, i: usize, id: u32) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + *(self.ptr as *mut u32).add(7 * self.len + i) = id; + } + } +} + +impl<'a> HitN<'a> { + /// Returns the number of hits in the packet. + pub const fn len(&self) -> usize { self.len } + + /// Returns true if the packet is empty. + pub const fn is_empty(&self) -> bool { self.len == 0 } +} + +/// Combined ray and hit packet of runtime size. +/// +/// The size of the packet can only be either 1, 4, 8, or 16. +pub struct RayHitN<'a> { + pub(crate) ptr: *mut sys::RTCRayHitN, + pub(crate) len: usize, + pub(crate) marker: PhantomData<&'a mut sys::RTCRayHitN>, +} + +impl<'a> RayHitN<'a> { + /// Returns the ray packet. + pub fn ray_n(&'a self) -> RayN<'a> { + RayN { + ptr: self.ptr as *mut sys::RTCRayN, + len: self.len, + marker: PhantomData, + } + } + + /// Returns the hit packet. + pub fn hit_n(&'a self) -> HitN<'a> { + HitN { + ptr: unsafe { (self.ptr as *const u32).add(12 * self.len) as *mut sys::RTCHitN }, + len: self.len, + marker: PhantomData, + } + } +} diff --git a/src/soa_ray.rs b/src/ray/soa.rs similarity index 78% rename from src/soa_ray.rs rename to src/ray/soa.rs index 8a6822d94..212de390a 100644 --- a/src/soa_ray.rs +++ b/src/ray/soa.rs @@ -1,23 +1,30 @@ -use std::iter::{ExactSizeIterator, Iterator}; -use std::marker::PhantomData; -use std::u32; +use crate::{normalise_vector3, INVALID_ID}; +use std::{ + iter::{ExactSizeIterator, Iterator}, + marker::PhantomData, +}; pub trait SoARay { fn org(&self, i: usize) -> [f32; 3]; fn set_org(&mut self, i: usize, o: [f32; 3]); - fn dir(&self, i: usize) -> [f32; 3]; - fn set_dir(&mut self, i: usize, d: [f32; 3]); - fn tnear(&self, i: usize) -> f32; fn set_tnear(&mut self, i: usize, near: f32); - fn tfar(&self, i: usize) -> f32; - fn set_tfar(&mut self, i: usize, far: f32); + fn dir(&self, i: usize) -> [f32; 3]; + fn set_dir(&mut self, i: usize, d: [f32; 3]); + + fn unit_dir(&self, i: usize) -> [f32; 3] { + let dir = self.dir(i); + normalise_vector3(dir) + } fn time(&self, i: usize) -> f32; fn set_time(&mut self, i: usize, time: f32); + fn tfar(&self, i: usize) -> f32; + fn set_tfar(&mut self, i: usize, far: f32); + fn mask(&self, i: usize) -> u32; fn set_mask(&mut self, i: usize, mask: u32); @@ -30,24 +37,36 @@ pub trait SoARay { pub trait SoAHit { fn normal(&self, i: usize) -> [f32; 3]; + + fn unit_normal(&self, i: usize) -> [f32; 3]; + fn set_normal(&mut self, i: usize, n: [f32; 3]); - fn uv(&self, i: usize) -> (f32, f32); + fn u(&self, i: usize) -> f32; + + fn v(&self, i: usize) -> f32; + + fn uv(&self, i: usize) -> [f32; 2]; + fn set_u(&mut self, i: usize, u: f32); + fn set_v(&mut self, i: usize, v: f32); + fn set_uv(&mut self, i: usize, uv: [f32; 2]); + fn prim_id(&self, i: usize) -> u32; + fn set_prim_id(&mut self, i: usize, id: u32); fn geom_id(&self, i: usize) -> u32; + fn set_geom_id(&mut self, i: usize, id: u32); fn inst_id(&self, i: usize) -> u32; + fn set_inst_id(&mut self, i: usize, id: u32); - fn hit(&self, i: usize) -> bool { - self.geom_id(i) != u32::MAX - } + fn is_valid(&self, i: usize) -> bool { self.geom_id(i) != INVALID_ID } } pub struct SoARayRef<'a, T> { @@ -56,27 +75,14 @@ pub struct SoARayRef<'a, T> { } impl<'a, T: SoARay + 'a> SoARayRef<'a, T> { - pub fn origin(&self) -> [f32; 3] { - self.ray.org(self.idx) - } - pub fn dir(&self) -> [f32; 3] { - self.ray.dir(self.idx) - } - pub fn tnear(&self) -> f32 { - self.ray.tnear(self.idx) - } - pub fn tfar(&self) -> f32 { - self.ray.tfar(self.idx) - } - pub fn mask(&self) -> u32 { - self.ray.mask(self.idx) - } - pub fn id(&self) -> u32 { - self.ray.id(self.idx) - } - pub fn flags(&self) -> u32 { - self.ray.flags(self.idx) - } + pub fn origin(&self) -> [f32; 3] { self.ray.org(self.idx) } + pub fn dir(&self) -> [f32; 3] { self.ray.dir(self.idx) } + pub fn unit_dir(&self) -> [f32; 3] { self.ray.unit_dir(self.idx) } + pub fn tnear(&self) -> f32 { self.ray.tnear(self.idx) } + pub fn tfar(&self) -> f32 { self.ray.tfar(self.idx) } + pub fn mask(&self) -> u32 { self.ray.mask(self.idx) } + pub fn id(&self) -> u32 { self.ray.id(self.idx) } + pub fn flags(&self) -> u32 { self.ray.flags(self.idx) } } // TODO: Is this going to work well? @@ -152,13 +158,7 @@ pub struct SoARayIter<'a, T> { } impl<'a, T: SoARay + 'a> SoARayIter<'a, T> { - pub fn new(ray: &'a T, len: usize) -> SoARayIter<'a, T> { - SoARayIter { - ray: ray, - cur: 0, - len: len, - } - } + pub fn new(ray: &'a T, len: usize) -> SoARayIter<'a, T> { SoARayIter { ray, cur: 0, len } } } impl<'a, T: SoARay + 'a> Iterator for SoARayIter<'a, T> { @@ -169,7 +169,7 @@ impl<'a, T: SoARay + 'a> Iterator for SoARayIter<'a, T> { None } else { let i = self.cur; - self.cur = self.cur + 1; + self.cur += 1; Some(SoARayRef { ray: self.ray, idx: i, @@ -179,9 +179,7 @@ impl<'a, T: SoARay + 'a> Iterator for SoARayIter<'a, T> { } impl<'a, T: SoARay + 'a> ExactSizeIterator for SoARayIter<'a, T> { - fn len(&self) -> usize { - self.len - self.cur - } + fn len(&self) -> usize { self.len - self.cur } } pub struct SoARayIterMut<'a, T> { @@ -192,11 +190,7 @@ pub struct SoARayIterMut<'a, T> { impl<'a, T: SoARay + 'a> SoARayIterMut<'a, T> { pub fn new(ray: &'a mut T, len: usize) -> SoARayIterMut<'a, T> { - SoARayIterMut { - ray: ray, - cur: 0, - len: len, - } + SoARayIterMut { ray, cur: 0, len } } } @@ -208,7 +202,7 @@ impl<'a, T: SoARay + 'a> Iterator for SoARayIterMut<'a, T> { None } else { let i = self.cur; - self.cur = self.cur + 1; + self.cur += 1; Some(SoARayRefMut { ray: self.ray as *mut T, idx: i, @@ -219,9 +213,7 @@ impl<'a, T: SoARay + 'a> Iterator for SoARayIterMut<'a, T> { } impl<'a, T: SoARay + 'a> ExactSizeIterator for SoARayIterMut<'a, T> { - fn len(&self) -> usize { - self.len - self.cur - } + fn len(&self) -> usize { self.len - self.cur } } pub struct SoAHitRef<'a, T> { @@ -230,24 +222,13 @@ pub struct SoAHitRef<'a, T> { } impl<'a, T: SoAHit + 'a> SoAHitRef<'a, T> { - pub fn normal(&self) -> [f32; 3] { - self.hit.normal(self.idx) - } - pub fn uv(&self) -> (f32, f32) { - self.hit.uv(self.idx) - } - pub fn prim_id(&self) -> u32 { - self.hit.prim_id(self.idx) - } - pub fn geom_id(&self) -> u32 { - self.hit.geom_id(self.idx) - } - pub fn inst_id(&self) -> u32 { - self.hit.inst_id(self.idx) - } - pub fn hit(&self) -> bool { - self.hit.hit(self.idx) - } + pub fn normal(&self) -> [f32; 3] { self.hit.normal(self.idx) } + pub fn unit_normal(&self) -> [f32; 3] { self.hit.unit_normal(self.idx) } + pub fn uv(&self) -> [f32; 2] { self.hit.uv(self.idx) } + pub fn prim_id(&self) -> u32 { self.hit.prim_id(self.idx) } + pub fn geom_id(&self) -> u32 { self.hit.geom_id(self.idx) } + pub fn inst_id(&self) -> u32 { self.hit.inst_id(self.idx) } + pub fn is_valid(&self) -> bool { self.hit.is_valid(self.idx) } } pub struct SoAHitIter<'a, T> { @@ -257,13 +238,7 @@ pub struct SoAHitIter<'a, T> { } impl<'a, T: SoAHit + 'a> SoAHitIter<'a, T> { - pub fn new(hit: &'a T, len: usize) -> SoAHitIter<'a, T> { - SoAHitIter { - hit: hit, - cur: 0, - len: len, - } - } + pub fn new(hit: &'a T, len: usize) -> SoAHitIter<'a, T> { SoAHitIter { hit, cur: 0, len } } } impl<'a, T: SoAHit + 'a> Iterator for SoAHitIter<'a, T> { @@ -274,7 +249,7 @@ impl<'a, T: SoAHit + 'a> Iterator for SoAHitIter<'a, T> { None } else { let i = self.cur; - self.cur = self.cur + 1; + self.cur += 1; Some(SoAHitRef { hit: self.hit, idx: i, @@ -284,9 +259,7 @@ impl<'a, T: SoAHit + 'a> Iterator for SoAHitIter<'a, T> { } impl<'a, T: SoAHit + 'a> ExactSizeIterator for SoAHitIter<'a, T> { - fn len(&self) -> usize { - self.len - self.cur - } + fn len(&self) -> usize { self.len - self.cur } } pub struct SoAHitRefMut<'a, T> { @@ -300,11 +273,15 @@ impl<'a, T: SoAHit + 'a> SoAHitRefMut<'a, T> { let hit = unsafe { self.hit.as_ref().expect("should never be null!") }; hit.normal(self.idx) } + pub fn unit_normal(&self) -> [f32; 3] { + let hit = unsafe { self.hit.as_ref().expect("should never be null!") }; + hit.unit_normal(self.idx) + } pub fn set_normal(&mut self, n: [f32; 3]) { let hit = unsafe { self.hit.as_mut().expect("should never be null!") }; hit.set_normal(self.idx, n) } - pub fn uv(&self) -> (f32, f32) { + pub fn uv(&self) -> [f32; 2] { let hit = unsafe { self.hit.as_ref().expect("should never be null!") }; hit.uv(self.idx) } @@ -342,7 +319,7 @@ impl<'a, T: SoAHit + 'a> SoAHitRefMut<'a, T> { } pub fn hit(&self) -> bool { let hit = unsafe { self.hit.as_ref().expect("should never be null!") }; - hit.hit(self.idx) + hit.is_valid(self.idx) } } @@ -354,11 +331,7 @@ pub struct SoAHitIterMut<'a, T> { impl<'a, T: SoAHit + 'a> SoAHitIterMut<'a, T> { pub fn new(hit: &'a mut T, len: usize) -> SoAHitIterMut<'a, T> { - SoAHitIterMut { - hit: hit, - cur: 0, - len: len, - } + SoAHitIterMut { hit, cur: 0, len } } } @@ -370,7 +343,7 @@ impl<'a, T: SoAHit + 'a> Iterator for SoAHitIterMut<'a, T> { None } else { let i = self.cur; - self.cur = self.cur + 1; + self.cur += 1; Some(SoAHitRefMut { hit: self.hit as *mut T, idx: i, @@ -381,7 +354,5 @@ impl<'a, T: SoAHit + 'a> Iterator for SoAHitIterMut<'a, T> { } impl<'a, T: SoAHit + 'a> ExactSizeIterator for SoAHitIterMut<'a, T> { - fn len(&self) -> usize { - self.len - self.cur - } + fn len(&self) -> usize { self.len - self.cur } } diff --git a/src/ray/stream.rs b/src/ray/stream.rs new file mode 100644 index 000000000..13fb479ef --- /dev/null +++ b/src/ray/stream.rs @@ -0,0 +1,481 @@ +//! Ray stream types in SOA layout. + +use std::{alloc, iter::Iterator, marker::PhantomData, ptr::NonNull}; + +use crate::{ + normalise_vector3, + sys::{RTCHitNp, RTCRayHitNp, RTCRayNp}, + SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut, INVALID_ID, +}; + +/// A ray stream stored in SoA format. +/// +/// Each ray component is aligned to 16 bytes. +pub struct RayNp { + /// The pointer to the start of the ray stream. + ptr: NonNull, + /// The number of rays in the stream. + len: usize, + /// The size of the allocated memory in bytes for each field of the ray + /// stream. This is always aligned to 16 bytes. + aligned_field_size: usize, + marker: PhantomData, +} + +impl RayNp { + /// Allocate a new Ray stream with room for `n` rays. + /// + /// The rays are uninitialized. + pub fn new(n: usize) -> RayNp { + unsafe { + let aligned_field_size = (n * std::mem::size_of::() + 15) & !15; + let layout = alloc::Layout::from_size_align(aligned_field_size * 12, 16).unwrap(); + let ptr = match NonNull::new(alloc::alloc_zeroed(layout) as *mut u8) { + Some(ptr) => ptr, + None => alloc::handle_alloc_error(layout), + }; + // Set the mask to 0xFFFFFFFF + ptr.as_ptr() + .add(aligned_field_size * 9) + .write_bytes(0xFF, aligned_field_size); + // Set the tfar to INFINITY + let tfar_ptr = ptr.as_ptr().add(aligned_field_size * 8) as *mut f32; + for i in 0..n { + tfar_ptr.add(i).write(f32::INFINITY); + } + RayNp { + ptr, + len: n, + aligned_field_size, + marker: PhantomData, + } + } + } + + pub fn iter(&self) -> SoARayIter { SoARayIter::new(self, self.len()) } + + pub fn iter_mut(&mut self) -> SoARayIterMut { + let n = self.len(); + SoARayIterMut::new(self, n) + } + + /// Returns the number of rays in the stream. + pub fn len(&self) -> usize { self.len } + + /// Returns true if the stream is empty. + pub fn is_empty(&self) -> bool { self.len() == 0 } + + pub fn as_raw_mut(&mut self) -> RTCRayNp { + unsafe { + let base_ptr = self.ptr.as_ptr(); + RTCRayNp { + org_x: base_ptr.add(0) as *mut f32, + org_y: base_ptr.add(self.aligned_field_size) as *mut f32, + org_z: base_ptr.add(2 * self.aligned_field_size) as *mut f32, + tnear: base_ptr.add(3 * self.aligned_field_size) as *mut f32, + dir_x: base_ptr.add(4 * self.aligned_field_size) as *mut f32, + dir_y: base_ptr.add(5 * self.aligned_field_size) as *mut f32, + dir_z: base_ptr.add(6 * self.aligned_field_size) as *mut f32, + time: base_ptr.add(7 * self.aligned_field_size) as *mut f32, + tfar: base_ptr.add(8 * self.aligned_field_size) as *mut f32, + mask: base_ptr.add(9 * self.aligned_field_size) as *mut u32, + id: base_ptr.add(10 * self.aligned_field_size) as *mut u32, + flags: base_ptr.add(11 * self.aligned_field_size) as *mut u32, + } + } + } +} + +impl Drop for RayNp { + fn drop(&mut self) { + unsafe { + let layout = alloc::Layout::from_size_align(self.aligned_field_size * 12, 16).unwrap(); + alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout); + } + } +} + +impl SoARay for RayNp { + fn org(&self, i: usize) -> [f32; 3] { + unsafe { + let base_ptr = self.ptr.as_ptr(); + [ + *(base_ptr.add(0) as *mut f32).add(i), + *(base_ptr.add(self.aligned_field_size) as *mut f32).add(i), + *(base_ptr.add(2 * self.aligned_field_size) as *mut f32).add(i), + ] + } + } + + fn set_org(&mut self, i: usize, o: [f32; 3]) { + unsafe { + let base_ptr = self.ptr.as_ptr(); + *(base_ptr.add(0) as *mut f32).add(i) = o[0]; + *(base_ptr.add(self.aligned_field_size) as *mut f32).add(i) = o[1]; + *(base_ptr.add(2 * self.aligned_field_size) as *mut f32).add(i) = o[2]; + } + } + + fn tnear(&self, i: usize) -> f32 { + unsafe { *(self.ptr.as_ptr().add(3 * self.aligned_field_size) as *mut f32).add(i) } + } + + fn set_tnear(&mut self, i: usize, near: f32) { + unsafe { + *(self.ptr.as_ptr().add(3 * self.aligned_field_size) as *mut f32).add(i) = near; + } + } + + fn dir(&self, i: usize) -> [f32; 3] { + unsafe { + let base_ptr = self.ptr.as_ptr(); + [ + *(base_ptr.add(4 * self.aligned_field_size) as *mut f32).add(i), + *(base_ptr.add(5 * self.aligned_field_size) as *mut f32).add(i), + *(base_ptr.add(6 * self.aligned_field_size) as *mut f32).add(i), + ] + } + } + + fn set_dir(&mut self, i: usize, d: [f32; 3]) { + unsafe { + let base_ptr = self.ptr.as_ptr(); + *(base_ptr.add(4 * self.aligned_field_size) as *mut f32).add(i) = d[0]; + *(base_ptr.add(5 * self.aligned_field_size) as *mut f32).add(i) = d[1]; + *(base_ptr.add(6 * self.aligned_field_size) as *mut f32).add(i) = d[2]; + } + } + + fn time(&self, i: usize) -> f32 { + unsafe { *(self.ptr.as_ptr().add(7 * self.aligned_field_size) as *mut f32).add(i) } + } + + fn set_time(&mut self, i: usize, time: f32) { + unsafe { + *(self.ptr.as_ptr().add(7 * self.aligned_field_size) as *mut f32).add(i) = time; + } + } + + fn tfar(&self, i: usize) -> f32 { + unsafe { *(self.ptr.as_ptr().add(8 * self.aligned_field_size) as *mut f32).add(i) } + } + + fn set_tfar(&mut self, i: usize, far: f32) { + unsafe { + *(self.ptr.as_ptr().add(8 * self.aligned_field_size) as *mut f32).add(i) = far; + } + } + + fn mask(&self, i: usize) -> u32 { + unsafe { *(self.ptr.as_ptr().add(9 * self.aligned_field_size) as *mut u32).add(i) } + } + + fn set_mask(&mut self, i: usize, mask: u32) { + unsafe { + *(self.ptr.as_ptr().add(9 * self.aligned_field_size) as *mut u32).add(i) = mask; + } + } + + fn id(&self, i: usize) -> u32 { + unsafe { *(self.ptr.as_ptr().add(10 * self.aligned_field_size) as *mut u32).add(i) } + } + + fn set_id(&mut self, i: usize, id: u32) { + unsafe { + *(self.ptr.as_ptr().add(10 * self.aligned_field_size) as *mut u32).add(i) = id; + } + } + + fn flags(&self, i: usize) -> u32 { + unsafe { *(self.ptr.as_ptr().add(11 * self.aligned_field_size) as *mut u32).add(i) } + } + + fn set_flags(&mut self, i: usize, flags: u32) { + unsafe { + *(self.ptr.as_ptr().add(11 * self.aligned_field_size) as *mut u32).add(i) = flags; + } + } +} + +#[test] +fn test_stream_layout_raynp() { + let mut ray0 = RayNp::new(11); + assert_eq!(ray0.aligned_field_size, 48); + + let ray1 = RayNp::new(17); + assert_eq!(ray1.aligned_field_size, 80); + + assert_eq!( + std::mem::size_of::(), + 24, + concat!("Size of: ", stringify!(RayNp)) + ); + + assert_eq!(ray0.as_raw_mut().org_x as usize % 16, 0); + assert_eq!(ray0.as_raw_mut().org_y as usize % 16, 0); + assert_eq!(ray0.as_raw_mut().org_z as usize % 16, 0); + assert_eq!(ray0.as_raw_mut().tnear as usize % 16, 0); + assert_eq!(ray0.as_raw_mut().dir_x as usize % 16, 0); + assert_eq!(ray0.as_raw_mut().dir_y as usize % 16, 0); + assert_eq!(ray0.as_raw_mut().dir_z as usize % 16, 0); + assert_eq!(ray0.as_raw_mut().time as usize % 16, 0); + assert_eq!(ray0.as_raw_mut().tfar as usize % 16, 0); + assert_eq!(ray0.as_raw_mut().mask as usize % 16, 0); + assert_eq!(ray0.as_raw_mut().id as usize % 16, 0); + assert_eq!(ray0.as_raw_mut().flags as usize % 16, 0); +} + +#[test] +fn test_stream_new_raynp() { + let ray = RayNp::new(135); + for i in 0..135 { + assert_eq!(ray.org(i), [0.0, 0.0, 0.0]); + assert_eq!(ray.dir(i), [0.0, 0.0, 0.0]); + assert_eq!(ray.tnear(i), 0.0); + assert_eq!(ray.tfar(i), f32::INFINITY); + assert_eq!(ray.mask(i), 0xFFFFFFFF); + assert_eq!(ray.id(i), 0); + assert_eq!(ray.flags(i), 0); + } +} + +/// A hit stream in SoA format. +/// +/// Each hit component is aligned to 16 bytes. +pub struct HitNp { + /// The pointer to the data. + ptr: NonNull, + /// The number of hits. + len: usize, + /// The size of each field, rounded up to the nearest multiple of 16. + aligned_field_size: usize, + marker: PhantomData, +} + +impl HitNp { + pub fn new(n: usize) -> HitNp { + unsafe { + let aligned_field_size = (std::mem::size_of::() * n + 15) & !15; + let layout = alloc::Layout::from_size_align(aligned_field_size * 8, 16).unwrap(); + let ptr = match NonNull::new(alloc::alloc_zeroed(layout) as *mut u8) { + Some(ptr) => ptr, + None => alloc::handle_alloc_error(layout), + }; + // Set the primID, geomID, instID to INVALID_ID. + (ptr.as_ptr() as *mut u8) + .add(5 * aligned_field_size) + .write_bytes(0xFF, aligned_field_size * 3); + HitNp { + ptr, + len: n, + aligned_field_size, + marker: PhantomData, + } + } + } + + pub fn any_hit(&self) -> bool { self.iter_validity().any(|g| g) } + + pub fn iter_validity(&self) -> impl Iterator + '_ { + unsafe { + std::slice::from_raw_parts( + self.ptr.as_ptr().add(6 * self.aligned_field_size) as *const u32, + self.len, + ) + .iter() + .map(|g| *g != INVALID_ID) + } + } + + pub fn iter(&self) -> SoAHitIter { SoAHitIter::new(self, self.len()) } + + pub fn iter_hits(&self) -> impl Iterator> { + SoAHitIter::new(self, self.len()).filter(|h| h.is_valid()) + } + + pub fn len(&self) -> usize { self.len } + + pub fn is_empty(&self) -> bool { self.len == 0 } + + pub fn as_raw_mut(&mut self) -> RTCHitNp { + unsafe { + let base_ptr = self.ptr.as_ptr(); + RTCHitNp { + Ng_x: base_ptr.add(0) as *mut f32, + Ng_y: base_ptr.add(self.aligned_field_size) as *mut f32, + Ng_z: base_ptr.add(2 * self.aligned_field_size) as *mut f32, + u: base_ptr.add(3 * self.aligned_field_size) as *mut f32, + v: base_ptr.add(4 * self.aligned_field_size) as *mut f32, + primID: base_ptr.add(5 * self.aligned_field_size) as *mut u32, + geomID: base_ptr.add(6 * self.aligned_field_size) as *mut u32, + instID: [base_ptr.add(7 * self.aligned_field_size) as *mut u32], + } + } + } +} + +impl Drop for HitNp { + fn drop(&mut self) { + unsafe { + let layout = alloc::Layout::from_size_align(self.aligned_field_size * 8, 16).unwrap(); + alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout); + } + } +} + +impl SoAHit for HitNp { + fn normal(&self, i: usize) -> [f32; 3] { + unsafe { + let base_ptr = self.ptr.as_ptr(); + [ + *(base_ptr.add(0) as *mut f32).add(i), + *(base_ptr.add(self.aligned_field_size) as *mut f32).add(i), + *(base_ptr.add(2 * self.aligned_field_size) as *mut f32).add(i), + ] + } + } + + fn unit_normal(&self, i: usize) -> [f32; 3] { normalise_vector3(self.normal(i)) } + + fn set_normal(&mut self, i: usize, n: [f32; 3]) { + unsafe { + let base_ptr = self.ptr.as_ptr(); + *(base_ptr.add(0) as *mut f32).add(i) = n[0]; + *(base_ptr.add(self.aligned_field_size) as *mut f32).add(i) = n[1]; + *(base_ptr.add(2 * self.aligned_field_size) as *mut f32).add(i) = n[2]; + } + } + + fn u(&self, i: usize) -> f32 { + unsafe { *(self.ptr.as_ptr().add(3 * self.aligned_field_size) as *mut f32).add(i) } + } + + fn v(&self, i: usize) -> f32 { + unsafe { *(self.ptr.as_ptr().add(4 * self.aligned_field_size) as *mut f32).add(i) } + } + + fn uv(&self, i: usize) -> [f32; 2] { + unsafe { + let base_ptr = self.ptr.as_ptr(); + [ + *(base_ptr.add(3 * self.aligned_field_size) as *mut f32).add(i), + *(base_ptr.add(4 * self.aligned_field_size) as *mut f32).add(i), + ] + } + } + + fn set_u(&mut self, i: usize, u: f32) { + unsafe { + *(self.ptr.as_ptr().add(3 * self.aligned_field_size) as *mut f32).add(i) = u; + } + } + + fn set_v(&mut self, i: usize, v: f32) { + unsafe { + *(self.ptr.as_ptr().add(4 * self.aligned_field_size) as *mut f32).add(i) = v; + } + } + + fn set_uv(&mut self, i: usize, uv: [f32; 2]) { + unsafe { + let base_ptr = self.ptr.as_ptr(); + *(base_ptr.add(3 * self.aligned_field_size) as *mut f32).add(i) = uv[0]; + *(base_ptr.add(4 * self.aligned_field_size) as *mut f32).add(i) = uv[1]; + } + } + + fn prim_id(&self, i: usize) -> u32 { + unsafe { *(self.ptr.as_ptr().add(5 * self.aligned_field_size) as *mut u32).add(i) } + } + + fn set_prim_id(&mut self, i: usize, id: u32) { + unsafe { + *(self.ptr.as_ptr().add(5 * self.aligned_field_size) as *mut u32).add(i) = id; + } + } + + fn geom_id(&self, i: usize) -> u32 { + unsafe { *(self.ptr.as_ptr().add(6 * self.aligned_field_size) as *mut u32).add(i) } + } + + fn set_geom_id(&mut self, i: usize, id: u32) { + unsafe { + *(self.ptr.as_ptr().add(6 * self.aligned_field_size) as *mut u32).add(i) = id; + } + } + + fn inst_id(&self, i: usize) -> u32 { + unsafe { *(self.ptr.as_ptr().add(7 * self.aligned_field_size) as *mut u32).add(i) } + } + + fn set_inst_id(&mut self, i: usize, id: u32) { + unsafe { + *(self.ptr.as_ptr().add(7 * self.aligned_field_size) as *mut u32).add(i) = id; + } + } +} + +#[test] +fn test_stream_layout_hitnp() { + let mut hit0 = HitNp::new(9); + assert_eq!(hit0.aligned_field_size, 48); + + let hit1 = HitNp::new(18); + assert_eq!(hit1.aligned_field_size, 80); + + assert_eq!( + std::mem::size_of::(), + 24, + concat!("Size of: ", stringify!(RayNp)) + ); + + assert_eq!(hit0.as_raw_mut().Ng_x as usize % 16, 0); + assert_eq!(hit0.as_raw_mut().Ng_y as usize % 16, 0); + assert_eq!(hit0.as_raw_mut().Ng_z as usize % 16, 0); + assert_eq!(hit0.as_raw_mut().u as usize % 16, 0); + assert_eq!(hit0.as_raw_mut().v as usize % 16, 0); + assert_eq!(hit0.as_raw_mut().primID as usize % 16, 0); + assert_eq!(hit0.as_raw_mut().geomID as usize % 16, 0); + assert_eq!(hit0.as_raw_mut().instID[0] as usize % 16, 0); +} + +#[test] +fn test_stream_new_hitnp() { + let mut hit = HitNp::new(13); + for hit in hit.iter_hits() { + assert_eq!(hit.normal(), [0.0, 0.0, 0.0]); + assert_eq!(hit.uv(), [0.0, 0.0]); + assert_eq!(hit.prim_id(), INVALID_ID); + assert_eq!(hit.geom_id(), INVALID_ID); + assert_eq!(hit.inst_id(), INVALID_ID); + } +} + +pub struct RayHitNp { + pub ray: RayNp, + pub hit: HitNp, +} + +impl RayHitNp { + pub fn new(ray: RayNp) -> RayHitNp { + let n = ray.len(); + RayHitNp { + ray, + hit: HitNp::new(n), + } + } + + pub fn iter(&self) -> std::iter::Zip, SoAHitIter> { + self.ray.iter().zip(self.hit.iter()) + } + pub fn len(&self) -> usize { self.ray.len() } + + pub fn is_empty(&self) -> bool { self.len() == 0 } + + pub fn as_raw(&mut self) -> RTCRayHitNp { + RTCRayHitNp { + ray: self.ray.as_raw_mut(), + hit: self.hit.as_raw_mut(), + } + } +} diff --git a/src/ray_packet.rs b/src/ray_packet.rs deleted file mode 100644 index 48693f63d..000000000 --- a/src/ray_packet.rs +++ /dev/null @@ -1,192 +0,0 @@ -use std::{f32, u32}; - -use crate::soa_ray::{SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut}; -use crate::sys; - -pub type Ray4 = sys::RTCRay4; -pub type Hit4 = sys::RTCHit4; -pub type RayHit4 = sys::RTCRayHit4; - -impl Ray4 { - pub fn empty() -> Ray4 { - Ray4::segment( - [[0.0, 0.0, 0.0]; 4], - [[0.0, 0.0, 0.0]; 4], - [0.0; 4], - [f32::INFINITY; 4], - ) - } - pub fn new(origin: [[f32; 3]; 4], dir: [[f32; 3]; 4]) -> Ray4 { - Ray4::segment(origin, dir, [0.0; 4], [f32::INFINITY; 4]) - } - pub fn segment( - origin: [[f32; 3]; 4], - dir: [[f32; 3]; 4], - tnear: [f32; 4], - tfar: [f32; 4], - ) -> Ray4 { - sys::RTCRay4 { - org_x: [origin[0][0], origin[1][0], origin[2][0], origin[3][0]], - org_y: [origin[0][1], origin[1][1], origin[2][1], origin[3][1]], - org_z: [origin[0][2], origin[1][2], origin[2][2], origin[3][2]], - dir_x: [dir[0][0], dir[1][0], dir[2][0], dir[3][0]], - dir_y: [dir[0][1], dir[1][1], dir[2][1], dir[3][1]], - dir_z: [dir[0][2], dir[1][2], dir[2][2], dir[3][2]], - tnear: tnear, - tfar: tfar, - time: [0.0; 4], - mask: [u32::MAX; 4], - id: [0; 4], - flags: [0; 4], - } - } - pub fn iter(&self) -> SoARayIter { - SoARayIter::new(self, 4) - } - pub fn iter_mut(&mut self) -> SoARayIterMut { - SoARayIterMut::new(self, 4) - } -} - -impl SoARay for Ray4 { - fn org(&self, i: usize) -> [f32; 3] { - [self.org_x[i], self.org_y[i], self.org_z[i]] - } - fn set_org(&mut self, i: usize, o: [f32; 3]) { - self.org_x[i] = o[0]; - self.org_y[i] = o[1]; - self.org_z[i] = o[2]; - } - - fn dir(&self, i: usize) -> [f32; 3] { - [self.dir_x[i], self.dir_y[i], self.dir_z[i]] - } - fn set_dir(&mut self, i: usize, d: [f32; 3]) { - self.dir_x[i] = d[0]; - self.dir_y[i] = d[1]; - self.dir_z[i] = d[2]; - } - - fn tnear(&self, i: usize) -> f32 { - self.tnear[i] - } - fn set_tnear(&mut self, i: usize, near: f32) { - self.tnear[i] = near; - } - - fn tfar(&self, i: usize) -> f32 { - self.tfar[i] - } - fn set_tfar(&mut self, i: usize, far: f32) { - self.tfar[i] = far; - } - - fn time(&self, i: usize) -> f32 { - self.time[i] - } - fn set_time(&mut self, i: usize, time: f32) { - self.time[i] = time; - } - - fn mask(&self, i: usize) -> u32 { - self.mask[i] - } - fn set_mask(&mut self, i: usize, mask: u32) { - self.mask[i] = mask; - } - - fn id(&self, i: usize) -> u32 { - self.id[i] - } - fn set_id(&mut self, i: usize, id: u32) { - self.id[i] = id; - } - - fn flags(&self, i: usize) -> u32 { - self.flags[i] - } - fn set_flags(&mut self, i: usize, flags: u32) { - self.flags[i] = flags; - } -} - -impl Hit4 { - pub fn new() -> Hit4 { - sys::RTCHit4 { - Ng_x: [0.0; 4], - Ng_y: [0.0; 4], - Ng_z: [0.0; 4], - u: [0.0; 4], - v: [0.0; 4], - primID: [u32::MAX; 4], - geomID: [u32::MAX; 4], - instID: [[u32::MAX; 4]], - } - } - pub fn any_hit(&self) -> bool { - self.hits().fold(false, |acc, g| acc || g) - } - pub fn hits<'a>(&'a self) -> impl Iterator + 'a { - self.geomID.iter().map(|g| *g != u32::MAX) - } - pub fn iter(&self) -> SoAHitIter { - SoAHitIter::new(self, 4) - } - pub fn iter_hits<'a>(&'a self) -> impl Iterator> + 'a { - SoAHitIter::new(self, 4).filter(|h| h.hit()) - } -} - -impl SoAHit for Hit4 { - fn normal(&self, i: usize) -> [f32; 3] { - [self.Ng_x[i], self.Ng_y[i], self.Ng_z[i]] - } - fn set_normal(&mut self, i: usize, n: [f32; 3]) { - self.Ng_x[i] = n[0]; - self.Ng_y[i] = n[1]; - self.Ng_z[i] = n[2]; - } - - fn uv(&self, i: usize) -> (f32, f32) { - (self.u[i], self.v[i]) - } - fn set_u(&mut self, i: usize, u: f32) { - self.u[i] = u; - } - fn set_v(&mut self, i: usize, v: f32) { - self.v[i] = v; - } - - fn prim_id(&self, i: usize) -> u32 { - self.primID[i] - } - fn set_prim_id(&mut self, i: usize, id: u32) { - self.primID[i] = id; - } - - fn geom_id(&self, i: usize) -> u32 { - self.geomID[i] - } - fn set_geom_id(&mut self, i: usize, id: u32) { - self.geomID[i] = id; - } - - fn inst_id(&self, i: usize) -> u32 { - self.instID[0][i] - } - fn set_inst_id(&mut self, i: usize, id: u32) { - self.instID[0][i] = id; - } -} - -impl RayHit4 { - pub fn new(ray: Ray4) -> RayHit4 { - sys::RTCRayHit4 { - ray: ray, - hit: Hit4::new(), - } - } - pub fn iter(&self) -> std::iter::Zip, SoAHitIter> { - self.ray.iter().zip(self.hit.iter()) - } -} diff --git a/src/ray_stream.rs b/src/ray_stream.rs deleted file mode 100644 index 670764953..000000000 --- a/src/ray_stream.rs +++ /dev/null @@ -1,252 +0,0 @@ -use std::iter::Iterator; -use std::{f32, u32}; - -use crate::soa_ray::{SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut}; -use crate::sys; -use crate::{aligned_vector, aligned_vector_init}; - -/// A ray stream stored in SoA format -pub struct RayN { - org_x: Vec, - org_y: Vec, - org_z: Vec, - tnear: Vec, - dir_x: Vec, - dir_y: Vec, - dir_z: Vec, - time: Vec, - tfar: Vec, - mask: Vec<::std::os::raw::c_uint>, - id: Vec<::std::os::raw::c_uint>, - flags: Vec<::std::os::raw::c_uint>, -} - -impl RayN { - /// Allocate a new Ray stream with room for `n` rays - pub fn new(n: usize) -> RayN { - RayN { - org_x: aligned_vector::(n, 16), - org_y: aligned_vector::(n, 16), - org_z: aligned_vector::(n, 16), - tnear: aligned_vector_init::(n, 16, 0.0), - dir_x: aligned_vector::(n, 16), - dir_y: aligned_vector::(n, 16), - dir_z: aligned_vector::(n, 16), - time: aligned_vector_init::(n, 16, 0.0), - tfar: aligned_vector_init::(n, 16, f32::INFINITY), - mask: aligned_vector_init::(n, 16, u32::MAX), - id: aligned_vector_init::(n, 16, 0), - flags: aligned_vector_init::(n, 16, 0), - } - } - pub fn iter(&self) -> SoARayIter { - SoARayIter::new(self, self.len()) - } - pub fn iter_mut(&mut self) -> SoARayIterMut { - let n = self.len(); - SoARayIterMut::new(self, n) - } - pub fn len(&self) -> usize { - self.org_x.len() - } - pub unsafe fn as_raynp(&mut self) -> sys::RTCRayNp { - sys::RTCRayNp { - org_x: self.org_x.as_mut_ptr(), - org_y: self.org_y.as_mut_ptr(), - org_z: self.org_z.as_mut_ptr(), - dir_x: self.dir_x.as_mut_ptr(), - dir_y: self.dir_y.as_mut_ptr(), - dir_z: self.dir_z.as_mut_ptr(), - tnear: self.tnear.as_mut_ptr(), - tfar: self.tfar.as_mut_ptr(), - time: self.time.as_mut_ptr(), - mask: self.mask.as_mut_ptr(), - id: self.id.as_mut_ptr(), - flags: self.flags.as_mut_ptr(), - } - } -} - -impl SoARay for RayN { - fn org(&self, i: usize) -> [f32; 3] { - [self.org_x[i], self.org_y[i], self.org_z[i]] - } - fn set_org(&mut self, i: usize, o: [f32; 3]) { - self.org_x[i] = o[0]; - self.org_y[i] = o[1]; - self.org_z[i] = o[2]; - } - - fn dir(&self, i: usize) -> [f32; 3] { - [self.dir_x[i], self.dir_y[i], self.dir_z[i]] - } - fn set_dir(&mut self, i: usize, d: [f32; 3]) { - self.dir_x[i] = d[0]; - self.dir_y[i] = d[1]; - self.dir_z[i] = d[2]; - } - - fn tnear(&self, i: usize) -> f32 { - self.tnear[i] - } - fn set_tnear(&mut self, i: usize, near: f32) { - self.tnear[i] = near; - } - - fn tfar(&self, i: usize) -> f32 { - self.tfar[i] - } - fn set_tfar(&mut self, i: usize, far: f32) { - self.tfar[i] = far; - } - - fn time(&self, i: usize) -> f32 { - self.time[i] - } - fn set_time(&mut self, i: usize, time: f32) { - self.time[i] = time; - } - - fn mask(&self, i: usize) -> u32 { - self.mask[i] - } - fn set_mask(&mut self, i: usize, mask: u32) { - self.mask[i] = mask; - } - - fn id(&self, i: usize) -> u32 { - self.id[i] - } - fn set_id(&mut self, i: usize, id: u32) { - self.id[i] = id; - } - - fn flags(&self, i: usize) -> u32 { - self.flags[i] - } - fn set_flags(&mut self, i: usize, flags: u32) { - self.flags[i] = flags; - } -} - -pub struct HitN { - ng_x: Vec, - ng_y: Vec, - ng_z: Vec, - u: Vec, - v: Vec, - prim_id: Vec<::std::os::raw::c_uint>, - geom_id: Vec<::std::os::raw::c_uint>, - inst_id: Vec<::std::os::raw::c_uint>, -} - -impl HitN { - pub fn new(n: usize) -> HitN { - HitN { - ng_x: aligned_vector::(n, 16), - ng_y: aligned_vector::(n, 16), - ng_z: aligned_vector::(n, 16), - u: aligned_vector::(n, 16), - v: aligned_vector::(n, 16), - prim_id: aligned_vector_init::(n, 16, u32::MAX), - geom_id: aligned_vector_init::(n, 16, u32::MAX), - inst_id: aligned_vector_init::(n, 16, u32::MAX), - } - } - pub fn any_hit(&self) -> bool { - self.hits().fold(false, |acc, g| acc || g) - } - pub fn hits<'a>(&'a self) -> impl Iterator + 'a { - self.geom_id.iter().map(|g| *g != u32::MAX) - } - pub fn iter(&self) -> SoAHitIter { - SoAHitIter::new(self, self.len()) - } - pub fn iter_hits<'a>(&'a self) -> impl Iterator> + 'a { - SoAHitIter::new(self, self.len()).filter(|h| h.hit()) - } - pub fn len(&self) -> usize { - self.ng_x.len() - } - pub unsafe fn as_hitnp(&mut self) -> sys::RTCHitNp { - sys::RTCHitNp { - Ng_x: self.ng_x.as_mut_ptr(), - Ng_y: self.ng_y.as_mut_ptr(), - Ng_z: self.ng_z.as_mut_ptr(), - u: self.u.as_mut_ptr(), - v: self.v.as_mut_ptr(), - primID: self.prim_id.as_mut_ptr(), - geomID: self.geom_id.as_mut_ptr(), - instID: [self.inst_id.as_mut_ptr(); 1usize], - } - } -} - -impl SoAHit for HitN { - fn normal(&self, i: usize) -> [f32; 3] { - [self.ng_x[i], self.ng_y[i], self.ng_z[i]] - } - fn set_normal(&mut self, i: usize, n: [f32; 3]) { - self.ng_x[i] = n[0]; - self.ng_y[i] = n[1]; - self.ng_z[i] = n[2]; - } - - fn uv(&self, i: usize) -> (f32, f32) { - (self.u[i], self.v[i]) - } - fn set_u(&mut self, i: usize, u: f32) { - self.u[i] = u; - } - fn set_v(&mut self, i: usize, v: f32) { - self.v[i] = v; - } - - fn prim_id(&self, i: usize) -> u32 { - self.prim_id[i] - } - fn set_prim_id(&mut self, i: usize, id: u32) { - self.prim_id[i] = id; - } - - fn geom_id(&self, i: usize) -> u32 { - self.geom_id[i] - } - fn set_geom_id(&mut self, i: usize, id: u32) { - self.geom_id[i] = id; - } - - fn inst_id(&self, i: usize) -> u32 { - self.inst_id[i] - } - fn set_inst_id(&mut self, i: usize, id: u32) { - self.inst_id[i] = id; - } -} - -pub struct RayHitN { - pub ray: RayN, - pub hit: HitN, -} - -impl RayHitN { - pub fn new(ray: RayN) -> RayHitN { - let n = ray.len(); - RayHitN { - ray: ray, - hit: HitN::new(n), - } - } - pub fn iter(&self) -> std::iter::Zip, SoAHitIter> { - self.ray.iter().zip(self.hit.iter()) - } - pub fn len(&self) -> usize { - self.ray.len() - } - pub unsafe fn as_rayhitnp(&mut self) -> sys::RTCRayHitNp { - sys::RTCRayHitNp { - ray: self.ray.as_raynp(), - hit: self.hit.as_hitnp(), - } - } -} diff --git a/src/scene.rs b/src/scene.rs index c84dc1f86..4e83be065 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,72 +1,236 @@ -use std::collections::HashMap; -use std::mem; -use std::sync::Arc; - -use crate::callback; -use crate::device::Device; -use crate::geometry::Geometry; -use crate::intersect_context::IntersectContext; -use crate::ray::{Ray, RayHit}; -use crate::ray_packet::{Ray4, RayHit4}; -use crate::ray_stream::{RayHitN, RayN}; -use crate::sys::*; - -/// A scene containing various geometry for rendering. Geometry -/// can be added and removed by attaching and detaching it, after -/// which the scene BVH can be built via `commit` which will -/// return a `CommittedScene` which can be used for ray queries. -pub struct Scene { +use crate::{ + AsIntersectContext, Bounds, BuildQuality, Error, PointQuery, PointQueryContext, Ray, Ray16, + Ray8, RayHit, RayHit16, RayHit8, RayHitNp, RayHitPacket, RayPacket, SceneFlags, +}; +use std::{ + any::TypeId, + collections::HashMap, + mem, ptr, + sync::{Arc, Mutex}, +}; + +use crate::{ + device::Device, + geometry::Geometry, + ray::{Ray4, RayHit4, RayNp}, + sys::*, +}; + +/// A scene containing various geometries. +#[derive(Debug)] +pub struct Scene<'a> { pub(crate) handle: RTCScene, - pub(crate) device: Arc, - geometry: HashMap>, + pub(crate) device: Device, + geometries: Arc>>>, + point_query_user_data: Arc>, +} + +impl<'a> Clone for Scene<'a> { + fn clone(&self) -> Self { + unsafe { rtcRetainScene(self.handle) } + Self { + handle: self.handle, + device: self.device.clone(), + geometries: self.geometries.clone(), + point_query_user_data: self.point_query_user_data.clone(), + } + } +} + +impl<'a> Drop for Scene<'a> { + fn drop(&mut self) { + unsafe { + rtcReleaseScene(self.handle); + } + } } -impl Scene { - pub fn new(device: Arc) -> Arc { - Arc::new(Scene { - handle: unsafe { rtcNewScene(device.handle) }, - device, - geometry: HashMap::new(), - }) - } - - /// Attach a new geometry to the scene. Returns the scene local ID which - /// can than be used to find the hit geometry from the ray ID member. - /// A geometry can only be attached to one Scene at a time, per the Embree - /// documentation. The geometry can be detached from the scene to move - /// it to another one. - pub fn attach_geometry(&mut self, mesh: Arc) -> u32 { - let id = unsafe { rtcAttachGeometry(self.handle, mesh.handle()) }; - self.geometry.insert(id, mesh); +unsafe impl<'a> Sync for Scene<'a> {} +unsafe impl<'a> Send for Scene<'a> {} + +impl<'a> Scene<'a> { + /// Creates a new scene with the given device. + pub(crate) fn new(device: Device) -> Result { + let handle = unsafe { rtcNewScene(device.handle) }; + if handle.is_null() { + Err(device.get_error()) + } else { + Ok(Scene { + handle, + device, + geometries: Default::default(), + point_query_user_data: Arc::new(Mutex::new(PointQueryUserData::default())), + }) + } + } + + /// Creates a new scene with the given device and flags. + pub(crate) fn new_with_flags(device: Device, flags: SceneFlags) -> Result { + let scene = Self::new(device)?; + scene.set_flags(flags); + Ok(scene) + } + + /// Returns the device the scene got created in. + pub fn device(&self) -> &Device { &self.device } + + /// Attaches a new geometry to the scene. + /// + /// A geometry can get attached to multiple scenes. The geometry ID is + /// unique per scene, and is used to identify the geometry when hitting + /// by a ray or ray packet during ray queries. + /// + /// This function is thread-safe, thus multiple threads can attach + /// geometries to a scene at the same time. + /// + /// The geometry IDs are assigned sequentially, starting at 0, as long as + /// no geometries are detached from the scene. If geometries are detached + /// from the scene, the implementation will reuse IDs in an implementation + /// dependent way. + pub fn attach_geometry(&mut self, geometry: &Geometry<'a>) -> u32 { + let id = unsafe { rtcAttachGeometry(self.handle, geometry.handle) }; + self.geometries.lock().unwrap().insert(id, geometry.clone()); id } - /// Detach the geometry from the scene - pub fn detach(&mut self, id: u32) { + /// Attaches a geometry to the scene using a specified geometry ID. + /// + /// A geometry can get attached to multiple scenes. The user-provided + /// geometry ID must be unused in the scene, otherwise the creation of the + /// geometry will fail. Further, the user-provided geometry IDs + /// should be compact, as Embree internally creates a vector which size is + /// equal to the largest geometry ID used. Creating very large geometry + /// IDs for small scenes would thus cause a memory consumption and + /// performance overhead. + /// + /// This function is thread-safe, thus multiple threads can attach + /// geometries to a scene at the same time. + pub fn attach_geometry_by_id(&mut self, geometry: &Geometry<'a>, id: u32) { + unsafe { rtcAttachGeometryByID(self.handle, geometry.handle, id) }; + self.geometries.lock().unwrap().insert(id, geometry.clone()); + } + + /// Detaches the geometry from the scene. + /// + /// This function is thread-safe, thus multiple threads can detach + /// geometries from a scene at the same time. + pub fn detach_geometry(&mut self, id: u32) { unsafe { rtcDetachGeometry(self.handle, id); } - self.geometry.remove(&id); + self.geometries.lock().unwrap().remove(&id); } - /// Get the underlying handle to the scene, e.g. for passing it to - /// native code or ISPC kernels. - pub unsafe fn handle(&self) -> RTCScene { - self.handle + /// Returns the geometry bound to the specified geometry ID. + /// + /// This function is NOT thread-safe, and thus CAN be used during rendering. + /// However, it is recommended to store the geometry handle inside the + /// application's geometry representation and look up the geometry + /// handle from that representation directly. + /// + /// For a thread-safe version of this function, see [`Scene::get_geometry`]. + pub fn get_geometry_unchecked(&self, id: u32) -> Option> { + let raw = unsafe { rtcGetGeometry(self.handle, id) }; + if raw.is_null() { + None + } else { + let geometries = self.geometries.lock().unwrap(); + geometries.get(&id).cloned() + } + } + + /// Returns the geometry bound to the specified geometry ID. + /// + /// This function is thread safe and should **NOT** get used during + /// rendering. If you need a fast non-thread safe version during + /// rendering please use the [`Scene::get_geometry_unchecked`] function. + pub fn get_geometry(&self, id: u32) -> Option> { + let raw = unsafe { rtcGetGeometryThreadSafe(self.handle, id) }; + if raw.is_null() { + None + } else { + let geometries = self.geometries.lock().unwrap(); + geometries.get(&id).cloned() + } } - /// Commit the scene to build the BVH on top of the geometry to allow - /// for ray tracing the scene using the intersect/occluded methods + /// Returns the raw underlying handle to the scene, e.g. for passing it to + /// native code or ISPC kernels. + /// + /// # Safety + /// + /// Use this function only if you know what you are doing. The returned + /// handle is a raw pointer to an Embree reference-counted object. The + /// reference count is not increased by this function, so the caller must + /// ensure that the handle is not used after the scene object is + /// destroyed. + pub unsafe fn handle(&self) -> RTCScene { self.handle } + + /// Commits all changes for the specified scene. + /// + /// This internally triggers building of a spatial acceleration structure + /// for the scene using all available worker threads. After the commit, + /// ray queries can be executed on the scene. + /// + /// If scene geometries get modified or attached or detached, the + /// [`Scene::commit`] call must be invoked before performing any further + /// ray queries for the scene; otherwise the effect of the ray query is + /// undefined. + /// + /// The modification of a geometry, committing the scene, and + /// tracing of rays must always happen sequentially, and never at the + /// same time. + /// + /// Any API call that sets a property of the scene or geometries + /// contained in the scene count as scene modification, e.g. including + /// setting of intersection filter functions. pub fn commit(&self) { unsafe { rtcCommitScene(self.handle); } } - /// Set the scene flags. Multiple flags can be enabled using an OR operation. - /// See [`RTCSceneFlags`] for all possible flags. - /// On failure an error code is set that can be queried using [`rtcGetDeviceError`]. - pub fn set_flags(&self, flags: RTCSceneFlags) { + /// Commits the scene from multiple threads. + /// + /// This function is similar to [`Scene::commit`], but allows multiple + /// threads to commit the scene at the same time. All threads must + /// consistently call [`Scene::join_commit`]. + /// + /// This method allows a flexible way to lazily create hierarchies + /// during rendering. A thread reaching a not-yet-constructed sub-scene of a + /// two-level scene can generate the sub-scene geometry and call this method + /// on that just generated scene. During construction, further threads + /// reaching the not-yet-built scene can join the build operation by + /// also invoking this method. A thread that calls `join_commit` after + /// the build finishes will directly return from the `join_commit` call. + /// + /// Multiple scene commit operations on different scenes can be running at + /// the same time, hence it is possible to commit many small scenes in + /// parallel, distributing the commits to many threads. + pub fn join_commit(&self) { + unsafe { + rtcJoinCommitScene(self.handle); + } + } + + /// Set the scene flags. Multiple flags can be enabled using an OR + /// operation. See [`SceneFlags`] for all possible flags. + /// On failure an error code is set that can be queried using + /// [`rtcGetDeviceError`]. + /// + /// Possible scene flags are: + /// - NONE: No flags set. + /// - DYNAMIC: Provides better build performance for dynamic scenes (but + /// also higher memory consumption). + /// - COMPACT: Uses compact acceleration structures and avoids algorithms + /// that consume much memory. + /// - ROBUST: Uses acceleration structures that allow for robust traversal, + /// and avoids optimizations that reduce arithmetic accuracy. This mode is + /// typically used for avoiding artifacts caused by rays shooting through + /// edges of neighboring primitives. + /// - CONTEXT_FILTER_FUNCTION: Enables support for a filter function inside + /// the intersection context for this scene. + pub fn set_flags(&self, flags: SceneFlags) { unsafe { rtcSetSceneFlags(self.handle, flags); } @@ -74,21 +238,144 @@ impl Scene { /// Query the flags of the scene. /// - /// Useful when setting individual flags, e.g. to just set the robust mode without - /// changing other flags the following way: + /// Useful when setting individual flags, e.g. to just set the robust mode + /// without changing other flags the following way: /// ```no_run /// use embree::{Device, Scene, SceneFlags}; - /// let device = Device::new(); - /// let scene = Scene::new(device.clone()); - /// let flags = scene.flags(); + /// let device = Device::new().unwrap(); + /// let scene = device.create_scene().unwrap(); + /// let flags = scene.get_flags(); /// scene.set_flags(flags | SceneFlags::ROBUST); /// ``` - pub fn flags(&self) -> RTCSceneFlags { - unsafe { rtcGetSceneFlags(self.handle) } + pub fn get_flags(&self) -> SceneFlags { unsafe { rtcGetSceneFlags(self.handle) } } + + /// Traverses the BVH with a point query object. + /// + /// Traverses the BVH using the point query object and calls a user defined + /// callback function for each primitive of the scene that intersects the + /// query domain. + /// + /// The user has to initialize the query location (x, y and z member) and + /// query radius in the range [0, ∞]. If the scene contains motion blur + /// geometries, also the query time (time member) must be initialized to + /// a value in the range [0, 1]. + /// + /// # Arguments + /// + /// * `query` - The point query object. + /// + /// * `context` - The point query context object. It contains ID and + /// transformation information of the instancing hierarchy if + /// (multilevel-)instancing is used. See [`PointQueryContext`]. + /// + /// * `query_fn` - The user defined callback function. For each primitive + /// that intersects the query domain, the callback function is called, in + /// which distance computations to the primitive can be implemented. The + /// user will be provided with the primitive ID and geometry ID of the + /// according primitive, however, the geometry information has to be + /// determined manually. The callback function can be `None`, in which + /// case the callback function is not invoked. + /// + /// * `user_data` - The user defined data that is passed to the callback. + /// + /// A callback function can still get attached to a specific [`Geometry`] + /// object using [`Geometry::set_point_query_function`]. If a callback + /// function is attached to a geometry, and (a potentially different) + /// callback function is passed to this function, both functions will be + /// called for the primitives of the according geometries. + /// + /// The query radius can be decreased inside the callback function, which + /// allows to efficiently cull parts of the scene during BVH traversal. + /// Increasing the query radius and modifying time or location of the query + /// will result in undefined behavior. + /// + /// The callback function will be called for all primitives in a leaf node + /// of the BVH even if the primitive is outside the query domain, + /// since Embree does not gather geometry information of primitives + /// internally. + /// + /// Point queries can be used with (multi-)instancing. However, care has to + /// be taken when the instance transformation contains anisotropic scaling + /// or sheering. In these cases distance computations have to be performed + /// in world space to ensure correctness and the ellipsoidal query domain + /// (in instance space) will be approximated with its axis aligned + /// bounding box internally. Therefore, the callback function might be + /// invoked even for primitives in inner BVH nodes that do not intersect + /// the query domain. + /// + /// The point query structure must be aligned to 16 bytes. + /// + /// Currently, all primitive types are supported by the point query API + /// except of points, curves and subdivision surfaces. + /// + /// See **closet_point** in examples folder for an example of this. + pub fn point_query( + &self, + query: &mut PointQuery, + context: &mut PointQueryContext, + query_fn: Option, + mut user_data: Option, + ) where + D: UserPointQueryData, + F: FnMut(&mut PointQuery, &mut PointQueryContext, Option<&mut D>, u32, u32, f32) -> bool, + { + let mut query_fn = query_fn; + let point_query_user_data = PointQueryUserData { + scene_closure: if query_fn.is_some() { + query_fn.as_mut().unwrap() as *mut F as *mut _ + } else { + ptr::null_mut() + }, + data: if user_data.is_some() { + user_data.as_mut().unwrap() as *mut D as *mut _ + } else { + ptr::null_mut() + }, + type_id: TypeId::of::(), + }; + unsafe { + rtcPointQuery( + self.handle, + query as *mut _, + context as *mut _, + if query_fn.is_some() { + point_query_function(query_fn.as_mut().unwrap()) + } else { + None + }, + if query_fn.is_some() { + point_query_user_data.data as *mut D as *mut _ + } else { + std::ptr::null_mut() + }, + ); + } } - /// Set the build quality of the scene. See [`RTCBuildQuality`] for all possible values. - pub fn set_build_quality(&self, quality: RTCBuildQuality) { + /// Set the build quality of the scene. See [`BuildQuality`] for all + /// possible values. + /// + /// The per-geometry build quality is only a hint and may be ignored. Embree + /// currently uses the per-geometry build quality when the scene build + /// quality is set to [`BuildQuality::LOW`]. In this mode a two-level + /// acceleration structure is build, and geometries build a separate + /// acceleration structure using the geometry build quality. + /// + /// The build quality can be one of the following: + /// + /// - [`BuildQuality::LOW`]: Creates lower quality data structures, e.g. for + /// dynamic scenes. + /// + /// - [`BuildQuality::MEDIUM`]: Default build quality for most usages. Gives + /// a good balance between quality and performance. + /// + /// - [`BuildQuality::HIGH`]: Creates higher quality data structures for + /// final frame rendering. Enables a spatial split builder for certain + /// primitive types. + /// + /// - [`BuildQuality::REFIT`]: Uses a BVH refitting approach when changing + /// only the vertex buffer. + pub fn set_build_quality(&self, quality: BuildQuality) { unsafe { rtcSetSceneBuildQuality(self.handle, quality); } @@ -103,130 +390,489 @@ impl Scene { /// /// # Arguments /// - /// * `progress` - A callback function that takes a number in range [0.0, 1.0] + /// * `progress` - A callback function that takes a number in range [0.0, + /// 1.0] /// indicating the progress of the operation. /// /// # Warning /// /// Must be called after the scene has been committed. - pub fn set_progress_monitor_function(&self, progress: F) + pub fn set_progress_monitor_function(&mut self, progress: F) where F: FnMut(f64) -> bool, { unsafe { let mut closure = progress; - rtcSetSceneProgressMonitorFunction( self.handle, - Some(callback::progress_monitor_function_helper(&mut closure)), + progress_monitor_function(&mut closure), &mut closure as *mut _ as *mut ::std::os::raw::c_void, ); } } /// Unregister the progress monitor callback function. - pub fn unset_progress_monitor_function(&self) { + pub fn unset_progress_monitor_function(&mut self) { unsafe { rtcSetSceneProgressMonitorFunction(self.handle, None, ::std::ptr::null_mut()); } } - pub fn intersect(&self, ctx: &mut IntersectContext, ray: &mut RayHit) { + /// Finds the closest hit of a single ray with the scene. + /// + /// Analogous to [`rtcIntersect1`]. + /// + /// The user has to initialize the ray origin, ray direction, ray segment + /// (`tnear`, `tfar` ray members), and set the ray flags to 0 (`flags` ray + /// member). If the scene contains motion blur geometries, also the ray + /// time (`time` ray member) must be initialized to a value in the range + /// [0, 1]. If ray masks are enabled at compile time, the ray mask + /// (`mask` ray member) must be initialized as well. The geometry ID + /// (`geomID` ray hit member) must be initialized to `INVALID_ID`. + /// + /// When no intersection is found, the ray/hit data is not updated. When an + /// intersection is found, the hit distance is written into the `tfar` + /// member of the ray and all hit data is set, such as unnormalized + /// geometry normal in object space (`Ng` hit member), local hit + /// coordinates (`u, v` hit member), instance ID stack (`instID` + /// hit member), geometry ID (`geomID` hit member), and primitive ID + /// (`primID` hit member). See [`RayHit`] for more information. + /// + /// The intersection context (`ctx` argument) can specify flags to optimize + /// traversal and a filter callback function to be invoked for every + /// intersection. Further, the pointer to the intersection context is + /// propagated to callback functions invoked during traversal and can + /// thus be used to extend the ray with additional data. See + /// [`IntersectContext`] for more information. + /// + /// # Arguments + /// + /// * `ctx` - The intersection context to use for the ray query. It + /// specifies flags to optimize traversal and a filter callback function + /// to be invoked for every intersection. Further, the pointer to the + /// intersection context is propagated to callback functions invoked + /// during traversal and can thus be used to extend the ray with + /// additional data. See [`IntersectContext`] for more information. + /// * `ray` - The ray to intersect with the scene. + pub fn intersect(&self, ctx: &mut C, ray: &mut RayHit) { unsafe { rtcIntersect1( self.handle, - ctx as *mut RTCIntersectContext, - ray as *mut RTCRayHit, + ctx.as_mut_context_ptr(), + ray as *mut _ as *mut _, ); } } - pub fn occluded(&self, ctx: &mut IntersectContext, ray: &mut Ray) { + /// Finds the closest hits for a ray packet of size 4 with the scene. + /// + /// # Arguments + /// + /// * `ctx` - The intersection context to use for the ray query. It + /// specifies flags to optimize traversal and a filter callback function + /// to be invoked for every intersection. Further, the pointer to the + /// intersection context is propagated to callback functions invoked + /// during traversal and can thus be used to extend the ray with + /// additional data. See [`IntersectContext`] for more information. + /// * `ray` - The ray packet of size 4 to intersect with the scene. The ray + /// packet must be aligned to 16 bytes. + /// * `valid` - A mask indicating which rays in the packet are valid. -1 + /// means + /// valid, 0 means invalid. + /// + /// The ray packet pointer passed to callback functions is not guaranteed to + /// be identical to the original ray provided. To extend the ray with + /// additional data to be accessed in callback functions, use the + /// intersection context. + /// + /// Only active rays are processed, and hit data of inactive rays is not + /// changed. + pub fn intersect4( + &self, + ctx: &mut C, + ray: &mut RayHit4, + valid: &[i32; 4], + ) { unsafe { - rtcOccluded1( + rtcIntersect4( + valid.as_ptr(), self.handle, - ctx as *mut RTCIntersectContext, - ray as *mut RTCRay, + ctx.as_mut_context_ptr(), + ray as *mut RTCRayHit4, ); } } - pub fn intersect4(&self, ctx: &mut IntersectContext, ray: &mut RayHit4, valid: &[i32; 4]) { + /// Finds the closest hits for a ray packet of size 8 with the scene. + /// + /// # Arguments + /// + /// * `ctx` - The intersection context to use for the ray query. It + /// specifies flags to optimize traversal and a filter callback function + /// to be invoked for every intersection. Further, the pointer to the + /// intersection context is propagated to callback functions invoked + /// during traversal and can thus be used to extend the ray with + /// additional data. See [`IntersectContext`] for more information. + /// * `ray` - The ray packet of size 8 to intersect with the scene. The ray + /// packet must be aligned to 32 bytes. + /// * `valid` - A mask indicating which rays in the packet are valid. -1 + /// means + /// valid, 0 means invalid. + /// + /// The ray packet pointer passed to callback functions is not guaranteed to + /// be identical to the original ray provided. To extend the ray with + /// additional data to be accessed in callback functions, use the + /// intersection context. + /// + /// Only active rays are processed, and hit data of inactive rays is not + /// changed. + pub fn intersect8( + &self, + ctx: &mut C, + ray: &mut RayHit8, + valid: &[i32; 8], + ) { unsafe { - rtcIntersect4( + rtcIntersect8( valid.as_ptr(), self.handle, - ctx as *mut RTCIntersectContext, - ray as *mut RTCRayHit4, + ctx.as_mut_context_ptr(), + ray as *mut RTCRayHit8, + ); + } + } + + /// Finds the closest hits for a ray packet of size 16 with the scene. + /// + /// # Arguments + /// + /// * `ctx` - The intersection context to use for the ray query. It + /// specifies flags to optimize traversal and a filter callback function + /// to be invoked for every intersection. Further, the pointer to the + /// intersection context is propagated to callback functions invoked + /// during traversal and can thus be used to extend the ray with + /// additional data. See [`IntersectContext`] for more information. + /// * `ray` - The ray packet of size 16 to intersect with the scene. The ray + /// packet must be aligned to 64 bytes. + /// * `valid` - A mask indicating which rays in the packet are valid. -1 + /// means + /// valid, 0 means invalid. + /// + /// The ray packet pointer passed to callback functions is not guaranteed to + /// be identical to the original ray provided. To extend the ray with + /// additional data to be accessed in callback functions, use the + /// intersection context. + /// + /// Only active rays are processed, and hit data of inactive rays is not + /// changed. + pub fn intersect16( + &self, + ctx: &mut C, + ray: &mut RayHit16, + valid: &[i32; 16], + ) { + unsafe { + rtcIntersect16( + valid.as_ptr(), + self.handle, + ctx.as_mut_context_ptr(), + ray as *mut RTCRayHit16, ); } } - pub fn occluded4(&self, ctx: &mut IntersectContext, ray: &mut Ray4, valid: &[i32; 4]) { + /// Checks for a single ray if whether there is any hit with the scene. + /// + /// When no intersection is found, the ray data is not updated. In case + /// a hit was found, the `tfar` component of the ray is set to `-inf`. + /// + /// # Arguments + /// + /// * `ctx` - The intersection context to use for the ray query. It + /// specifies flags to optimize traversal and a filter callback function + /// to be invoked for every intersection. Further, the pointer to the + /// intersection context is propagated to callback functions invoked + /// during traversal and can thus be used to extend the ray with + /// additional data. See [`IntersectContext`] for more information. + /// + /// * `ray` - The ray to intersect with the scene. + pub fn occluded(&self, ctx: &mut C, ray: &mut Ray) -> bool { + unsafe { + rtcOccluded1(self.handle, ctx.as_mut_context_ptr(), ray as *mut RTCRay); + } + ray.tfar == f32::NEG_INFINITY + } + + /// Checks for each active ray of a ray packet of size 4 if whether there is + /// any hit with the scene. + /// + /// # Arguments + /// + /// * `ctx` - The intersection context to use for the ray query. It + /// specifies flags to optimize traversal and a filter callback function + /// to be invoked for every intersection. Further, the pointer to the + /// intersection context is propagated to callback functions invoked + /// during traversal and can thus be used to extend the ray with + /// additional data. See [`IntersectContext`] for more information. + /// * `ray` - The ray packet of size 4 to intersect with the scene. The ray + /// packet must be aligned to 16 bytes. + /// * `valid` - A mask indicating which rays in the packet are valid. -1 + /// means + /// valid, 0 means invalid. + /// + /// The ray packet pointer passed to callback functions is not guaranteed to + /// be identical to the original ray provided. To extend the ray with + /// additional data to be accessed in callback functions, use the + /// intersection context. + /// + /// Only active rays are processed, and hit data of inactive rays is not + /// changed. + pub fn occluded4(&self, ctx: &mut C, ray: &mut Ray4, valid: &[i32; 4]) { unsafe { rtcOccluded4( valid.as_ptr(), self.handle, - ctx as *mut RTCIntersectContext, + ctx.as_mut_context_ptr(), ray as *mut RTCRay4, ); } } - pub fn intersect_stream_aos(&self, ctx: &mut IntersectContext, rays: &mut Vec) { - let m = rays.len(); + /// Checks for each active ray of a ray packet of size 4 if whether there is + /// any hit with the scene. + /// + /// # Arguments + /// + /// * `ctx` - The intersection context to use for the ray query. It + /// specifies flags to optimize traversal and a filter callback function + /// to be invoked for every intersection. Further, the pointer to the + /// intersection context is propagated to callback functions invoked + /// during traversal and can thus be used to extend the ray with + /// additional data. See [`IntersectContext`] for more information. + /// * `ray` - The ray packet of size 8 to intersect with the scene. The ray + /// packet must be aligned to 32 bytes. + /// * `valid` - A mask indicating which rays in the packet are valid. -1 + /// means + /// valid, 0 means invalid. + /// + /// The ray packet pointer passed to callback functions is not guaranteed to + /// be identical to the original ray provided. To extend the ray with + /// additional data to be accessed in callback functions, use the + /// intersection context. + /// + /// Only active rays are processed, and hit data of inactive rays is not + /// changed. + pub fn occluded8(&self, ctx: &mut C, ray: &mut Ray8, valid: &[i32; 8]) { unsafe { - rtcIntersect1M( + rtcOccluded8( + valid.as_ptr(), self.handle, - ctx as *mut RTCIntersectContext, - rays.as_mut_ptr(), - m as u32, - mem::size_of::(), + ctx.as_mut_context_ptr(), + ray as *mut RTCRay8, ); } } - pub fn occluded_stream_aos(&self, ctx: &mut IntersectContext, rays: &mut Vec) { - let m = rays.len(); + /// Checks for each active ray of a ray packet of size 16 if whether there + /// is any hit with the scene. + /// + /// # Arguments + /// + /// * `ctx` - The intersection context to use for the ray query. It + /// specifies flags to optimize traversal and a filter callback function + /// to be invoked for every intersection. Further, the pointer to the + /// intersection context is propagated to callback functions invoked + /// during traversal and can thus be used to extend the ray with + /// additional data. See [`IntersectContext`] for more information. + /// * `ray` - The ray packet of size 16 to intersect with the scene. The ray + /// packet must be aligned to 64 bytes. + /// * `valid` - A mask indicating which rays in the packet are valid. -1 + /// means + /// valid, 0 means invalid. + /// + /// The ray packet pointer passed to callback functions is not guaranteed to + /// be identical to the original ray provided. To extend the ray with + /// additional data to be accessed in callback functions, use the + /// intersection context. + /// + /// Only active rays are processed, and hit data of inactive rays is not + /// changed. + pub fn occluded16( + &self, + ctx: &mut C, + ray: &mut Ray16, + valid: &[i32; 16], + ) { unsafe { - rtcOccluded1M( + rtcOccluded16( + valid.as_ptr(), self.handle, - ctx as *mut RTCIntersectContext, - rays.as_mut_ptr(), - m as u32, - mem::size_of::(), + ctx.as_mut_context_ptr(), + ray as *mut RTCRay16, ); } } - pub fn intersect_stream_soa(&self, ctx: &mut IntersectContext, rays: &mut RayHitN) { - let n = rays.len(); + /// Finds the closest hits for a stream of M ray packets. + /// + /// A ray in the stream is inactive if its `tnear` value is larger than its + /// `tfar` value. The stream can be any size including zero. Each ray + /// must be aligned to 16 bytes. + /// + /// The implementation of the stream ray query functions may re-order rays + /// arbitrarily and re-pack rays into ray packets of different size. For + /// this reason, the callback functions may be invoked with an arbitrary + /// packet size (of size 1, 4, 8, or 16) and different ordering as + /// specified initially in the ray stream. For this reason, you MUST NOT + /// rely on the ordering of the rays in the ray stream to be preserved but + /// instead use the `rayID` component of the ray to identify the original + /// rya, e.g. to access a per-ray payload. + /// + /// Analogous to [`rtcIntersectNM`] and [`rtcIntersect1M`]. + /// + /// # Arguments + /// + /// * `ctx` - The intersection context to use for the ray query. It + /// specifies flags to optimize traversal and a filter callback function + /// to be invoked for every intersection. Further, the pointer to the + /// intersection context is propagated to callback functions invoked + /// during traversal and can thus be used to extend the ray with + /// additional data. See [`IntersectContext`] for more information. + /// + /// * `rays` - The ray stream to intersect with the scene. + pub fn intersect_stream_aos( + &self, + ctx: &mut C, + rays: &mut Vec

, + ) { + let m = rays.len(); + unsafe { + if P::Ray::LEN == 1 { + rtcIntersect1M( + self.handle, + ctx.as_mut_context_ptr(), + rays.as_mut_ptr() as *mut _, + m as u32, + mem::size_of::

(), + ); + } else { + rtcIntersectNM( + self.handle, + ctx.as_mut_context_ptr(), + rays.as_mut_ptr() as *mut _, + P::Ray::LEN as u32, + m as u32, + mem::size_of::

(), + ); + } + } + } + + /// Finds the closest hits for a stream of M ray packets. + /// + /// A ray in the stream is inactive if its `tnear` value is larger than its + /// `tfar` value. The stream can be any size including zero. Each ray + /// must be aligned to 16 bytes. + /// + /// The implementation of the stream ray query functions may re-order rays + /// arbitrarily and re-pack rays into ray packets of different size. For + /// this reason, the callback functions may be invoked with an arbitrary + /// packet size (of size 1, 4, 8, or 16) and different ordering as + /// specified initially in the ray stream. For this reason, you MUST NOT + /// rely on the ordering of the rays in the ray stream to be preserved but + /// instead use the `rayID` component of the ray to identify the original + /// rya, e.g. to access a per-ray payload. + /// + /// Analogous to [`rtcOccluded1M`] and [`rtcOccludedNM`]. + /// + /// # Arguments + /// + /// * `ctx` - The intersection context to use for the ray query. It + /// specifies flags + /// to optimize traversal and a filter callback function to be invoked for + /// every intersection. Further, the pointer to the intersection context + /// is propagated to callback functions invoked during traversal and can + /// thus be used to extend the ray with additional data. See + /// [`IntersectContext`] for more information. + /// + /// * `rays` - The ray stream to intersect with the scene. + pub fn occluded_stream_aos( + &self, + ctx: &mut C, + rays: &mut Vec

, + ) { + let m = rays.len(); + unsafe { + if P::LEN == 1 { + rtcOccluded1M( + self.handle, + ctx.as_mut_context_ptr(), + rays.as_mut_ptr() as *mut RTCRay, + m as u32, + mem::size_of::

(), + ); + } else { + rtcOccludedNM( + self.handle, + ctx.as_mut_context_ptr(), + rays.as_mut_ptr() as *mut RTCRayN, + P::LEN as u32, + m as u32, + mem::size_of::

(), + ); + } + } + } + + /// Finds the closest hit for a SOA ray stream of size `n`. + /// + /// The implementation of the stream ray query functions may re-order rays + /// arbitrarily and re-pack rays into ray packets of different size. For + /// this reason, callback functions may be invoked with an arbitrary + /// packet size (of size 1, 4, 8, or 16) and different ordering as + /// specified initially. For this reason, one may have to use the rayID + /// component of the ray to identify the original ray, e.g. to access + /// a per-ray payload. + /// + /// A ray in a ray stream is considered inactive if its tnear value is + /// larger than its tfar value. + pub fn intersect_stream_soa(&self, ctx: &mut C, rays: &mut RayHitNp) { unsafe { - let mut rayhit = rays.as_rayhitnp(); rtcIntersectNp( self.handle, - ctx as *mut RTCIntersectContext, - &mut rayhit as *mut RTCRayHitNp, - n as u32, + ctx.as_mut_context_ptr(), + &mut rays.as_raw() as *mut _, + rays.len() as u32, ); } } - pub fn occluded_stream_soa(&self, ctx: &mut IntersectContext, rays: &mut RayN) { - let n = rays.len(); + /// Finds any hits for a SOA ray stream of size `n`. + /// + /// The implementation of the stream ray query functions may re-order rays + /// arbitrarily and re-pack rays into ray packets of different size. For + /// this reason, callback functions may be invoked with an arbitrary + /// packet size (of size 1, 4, 8, or 16) and different ordering as + /// specified initially. For this reason, one may have to use the rayID + /// component of the ray to identify the original ray, e.g. to access + /// a per-ray payload. + /// + /// A ray in a ray stream is considered inactive if its tnear value is + /// larger than its tfar value. + pub fn occluded_stream_soa(&self, ctx: &mut C, rays: &mut RayNp) { unsafe { - let mut r = rays.as_raynp(); rtcOccludedNp( self.handle, - ctx as *mut RTCIntersectContext, - &mut r as *mut RTCRayNp, - n as u32, + ctx.as_mut_context_ptr(), + &mut rays.as_raw_mut() as *mut RTCRayNp, + rays.len() as u32, ); } } - pub fn bounds(&self) -> RTCBounds { - let mut bounds = RTCBounds { + /// Returns the axis-aligned bounding box of the scene. + pub fn get_bounds(&self) -> Bounds { + let mut bounds = Bounds { lower_x: 0.0, upper_x: 0.0, lower_y: 0.0, @@ -237,16 +883,89 @@ impl Scene { align1: 0.0, }; unsafe { - rtcGetSceneBounds(self.handle(), &mut bounds as *mut RTCBounds); + rtcGetSceneBounds(self.handle(), &mut bounds as *mut Bounds); } bounds } } -impl Drop for Scene { - fn drop(&mut self) { - unsafe { - rtcReleaseScene(self.handle); +pub trait UserPointQueryData: Sized + Send + Sync + 'static {} + +impl UserPointQueryData for T where T: Sized + Send + Sync + 'static {} + +/// User data for callback of [`Scene::point_query`] and +/// [`Geometry::set_point_query_function`]. +#[derive(Debug)] +pub(crate) struct PointQueryUserData { + pub scene_closure: *mut std::os::raw::c_void, + pub data: *mut std::os::raw::c_void, + pub type_id: TypeId, +} + +impl Default for PointQueryUserData { + fn default() -> Self { + Self { + scene_closure: ptr::null_mut(), + data: ptr::null_mut(), + type_id: TypeId::of::<()>(), + } + } +} + +/// Helper function to convert a Rust closure to `RTCProgressMonitorFunction` +/// callback. +fn progress_monitor_function(_f: &mut F) -> RTCProgressMonitorFunction +where + F: FnMut(f64) -> bool, +{ + unsafe extern "C" fn inner(f: *mut std::os::raw::c_void, n: f64) -> bool + where + F: FnMut(f64) -> bool, + { + let cb = &mut *(f as *mut F); + cb(n) + } + + Some(inner::) +} + +/// Helper function to convert a Rust closure to `RTCPointQueryFunction` +/// callback. +fn point_query_function(_f: &mut F) -> RTCPointQueryFunction +where + D: UserPointQueryData, + F: FnMut(&mut PointQuery, &mut PointQueryContext, Option<&mut D>, u32, u32, f32) -> bool, +{ + unsafe extern "C" fn inner(args: *mut RTCPointQueryFunctionArguments) -> bool + where + D: UserPointQueryData, + F: FnMut(&mut PointQuery, &mut PointQueryContext, Option<&mut D>, u32, u32, f32) -> bool, + { + let user_data = &mut *((*args).userPtr as *mut PointQueryUserData); + let cb_ptr = user_data.scene_closure as *mut F; + if !cb_ptr.is_null() { + let data = { + if user_data.data.is_null() || user_data.type_id != TypeId::of::() { + None + } else { + Some(&mut *(user_data.data as *mut D)) + } + }; + let cb = &mut *cb_ptr; + cb( + &mut *(*args).query, + &mut *(*args).context, + data, + (*args).primID, + (*args).geomID, + (*args).similarityScale, + ) + } else { + false } } + + Some(inner::) } + +// TODO: implement rtcIntersect1Mp diff --git a/src/sys.rs b/src/sys.rs index 37504938f..69e036e83 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -1,18 +1,15 @@ -/* automatically generated by rust-bindgen 0.55.1 */ +/* automatically generated by rust-bindgen 0.64.0 */ pub const RTC_VERSION_MAJOR: u32 = 3; -pub const RTC_VERSION_MINOR: u32 = 12; -pub const RTC_VERSION_PATCH: u32 = 1; -pub const RTC_VERSION: u32 = 31201; -pub const RTC_VERSION_STRING: &'static [u8; 7usize] = b"3.12.1\0"; +pub const RTC_VERSION_MINOR: u32 = 13; +pub const RTC_VERSION_PATCH: u32 = 5; +pub const RTC_VERSION: u32 = 31305; +pub const RTC_VERSION_STRING: &[u8; 7usize] = b"3.13.5\0"; pub const RTC_MAX_INSTANCE_LEVEL_COUNT: u32 = 1; pub const RTC_MIN_WIDTH: u32 = 0; pub const RTC_MAX_TIME_STEP_COUNT: u32 = 129; -pub type size_t = usize; -pub type __ssize_t = isize; -pub type ssize_t = __ssize_t; #[repr(u32)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum RTCFormat { UNDEFINED = 0, UCHAR = 4097, @@ -84,7 +81,7 @@ pub enum RTCFormat { GRID = 40961, } #[repr(u32)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum RTCBuildQuality { LOW = 0, MEDIUM = 1, @@ -106,6 +103,8 @@ pub struct RTCBounds { } #[test] fn bindgen_test_layout_RTCBounds() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 32usize, @@ -117,7 +116,7 @@ fn bindgen_test_layout_RTCBounds() { concat!("Alignment of ", stringify!(RTCBounds)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).lower_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).lower_x) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -127,7 +126,7 @@ fn bindgen_test_layout_RTCBounds() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).lower_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).lower_y) as usize - ptr as usize }, 4usize, concat!( "Offset of field: ", @@ -137,7 +136,7 @@ fn bindgen_test_layout_RTCBounds() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).lower_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).lower_z) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -147,7 +146,7 @@ fn bindgen_test_layout_RTCBounds() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).align0 as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).align0) as usize - ptr as usize }, 12usize, concat!( "Offset of field: ", @@ -157,7 +156,7 @@ fn bindgen_test_layout_RTCBounds() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).upper_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).upper_x) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -167,7 +166,7 @@ fn bindgen_test_layout_RTCBounds() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).upper_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).upper_y) as usize - ptr as usize }, 20usize, concat!( "Offset of field: ", @@ -177,7 +176,7 @@ fn bindgen_test_layout_RTCBounds() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).upper_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).upper_z) as usize - ptr as usize }, 24usize, concat!( "Offset of field: ", @@ -187,7 +186,7 @@ fn bindgen_test_layout_RTCBounds() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).align1 as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).align1) as usize - ptr as usize }, 28usize, concat!( "Offset of field: ", @@ -206,6 +205,8 @@ pub struct RTCLinearBounds { } #[test] fn bindgen_test_layout_RTCLinearBounds() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 64usize, @@ -217,7 +218,7 @@ fn bindgen_test_layout_RTCLinearBounds() { concat!("Alignment of ", stringify!(RTCLinearBounds)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).bounds0 as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).bounds0) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -227,7 +228,7 @@ fn bindgen_test_layout_RTCLinearBounds() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).bounds1 as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).bounds1) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -249,31 +250,23 @@ impl RTCIntersectContextFlags { impl ::std::ops::BitOr for RTCIntersectContextFlags { type Output = Self; #[inline] - fn bitor(self, other: Self) -> Self { - RTCIntersectContextFlags(self.0 | other.0) - } + fn bitor(self, other: Self) -> Self { RTCIntersectContextFlags(self.0 | other.0) } } impl ::std::ops::BitOrAssign for RTCIntersectContextFlags { #[inline] - fn bitor_assign(&mut self, rhs: RTCIntersectContextFlags) { - self.0 |= rhs.0; - } + fn bitor_assign(&mut self, rhs: RTCIntersectContextFlags) { self.0 |= rhs.0; } } impl ::std::ops::BitAnd for RTCIntersectContextFlags { type Output = Self; #[inline] - fn bitand(self, other: Self) -> Self { - RTCIntersectContextFlags(self.0 & other.0) - } + fn bitand(self, other: Self) -> Self { RTCIntersectContextFlags(self.0 & other.0) } } impl ::std::ops::BitAndAssign for RTCIntersectContextFlags { #[inline] - fn bitand_assign(&mut self, rhs: RTCIntersectContextFlags) { - self.0 &= rhs.0; - } + fn bitand_assign(&mut self, rhs: RTCIntersectContextFlags) { self.0 &= rhs.0; } } #[repr(transparent)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct RTCIntersectContextFlags(pub ::std::os::raw::c_uint); #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -287,6 +280,9 @@ pub struct RTCFilterFunctionNArguments { } #[test] fn bindgen_test_layout_RTCFilterFunctionNArguments() { + const UNINIT: ::std::mem::MaybeUninit = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 48usize, @@ -298,9 +294,7 @@ fn bindgen_test_layout_RTCFilterFunctionNArguments() { concat!("Alignment of ", stringify!(RTCFilterFunctionNArguments)) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).valid as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).valid) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -310,10 +304,7 @@ fn bindgen_test_layout_RTCFilterFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).geometryUserPtr as *const _ - as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).geometryUserPtr) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -323,9 +314,7 @@ fn bindgen_test_layout_RTCFilterFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).context as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).context) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -335,7 +324,7 @@ fn bindgen_test_layout_RTCFilterFunctionNArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).ray as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).ray) as usize - ptr as usize }, 24usize, concat!( "Offset of field: ", @@ -345,7 +334,7 @@ fn bindgen_test_layout_RTCFilterFunctionNArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).hit as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).hit) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -355,7 +344,7 @@ fn bindgen_test_layout_RTCFilterFunctionNArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).N as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).N) as usize - ptr as usize }, 40usize, concat!( "Offset of field: ", @@ -376,6 +365,8 @@ pub struct RTCIntersectContext { } #[test] fn bindgen_test_layout_RTCIntersectContext() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 24usize, @@ -387,7 +378,7 @@ fn bindgen_test_layout_RTCIntersectContext() { concat!("Alignment of ", stringify!(RTCIntersectContext)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -397,7 +388,7 @@ fn bindgen_test_layout_RTCIntersectContext() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).filter as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).filter) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -407,7 +398,7 @@ fn bindgen_test_layout_RTCIntersectContext() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).instID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).instID) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -429,6 +420,8 @@ pub struct RTCPointQuery { } #[test] fn bindgen_test_layout_RTCPointQuery() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 32usize, @@ -440,7 +433,7 @@ fn bindgen_test_layout_RTCPointQuery() { concat!("Alignment of ", stringify!(RTCPointQuery)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).x) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -450,7 +443,7 @@ fn bindgen_test_layout_RTCPointQuery() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).y) as usize - ptr as usize }, 4usize, concat!( "Offset of field: ", @@ -460,7 +453,7 @@ fn bindgen_test_layout_RTCPointQuery() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).z) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -470,7 +463,7 @@ fn bindgen_test_layout_RTCPointQuery() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).time as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).time) as usize - ptr as usize }, 12usize, concat!( "Offset of field: ", @@ -480,7 +473,7 @@ fn bindgen_test_layout_RTCPointQuery() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).radius as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).radius) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -502,6 +495,8 @@ pub struct RTCPointQuery4 { } #[test] fn bindgen_test_layout_RTCPointQuery4() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 80usize, @@ -513,7 +508,7 @@ fn bindgen_test_layout_RTCPointQuery4() { concat!("Alignment of ", stringify!(RTCPointQuery4)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).x) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -523,7 +518,7 @@ fn bindgen_test_layout_RTCPointQuery4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).y) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -533,7 +528,7 @@ fn bindgen_test_layout_RTCPointQuery4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).z) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -543,7 +538,7 @@ fn bindgen_test_layout_RTCPointQuery4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).time as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).time) as usize - ptr as usize }, 48usize, concat!( "Offset of field: ", @@ -553,7 +548,7 @@ fn bindgen_test_layout_RTCPointQuery4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).radius as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).radius) as usize - ptr as usize }, 64usize, concat!( "Offset of field: ", @@ -575,6 +570,8 @@ pub struct RTCPointQuery8 { } #[test] fn bindgen_test_layout_RTCPointQuery8() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 160usize, @@ -586,7 +583,7 @@ fn bindgen_test_layout_RTCPointQuery8() { concat!("Alignment of ", stringify!(RTCPointQuery8)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).x) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -596,7 +593,7 @@ fn bindgen_test_layout_RTCPointQuery8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).y) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -606,7 +603,7 @@ fn bindgen_test_layout_RTCPointQuery8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).z) as usize - ptr as usize }, 64usize, concat!( "Offset of field: ", @@ -616,7 +613,7 @@ fn bindgen_test_layout_RTCPointQuery8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).time as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).time) as usize - ptr as usize }, 96usize, concat!( "Offset of field: ", @@ -626,7 +623,7 @@ fn bindgen_test_layout_RTCPointQuery8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).radius as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).radius) as usize - ptr as usize }, 128usize, concat!( "Offset of field: ", @@ -638,7 +635,7 @@ fn bindgen_test_layout_RTCPointQuery8() { } #[repr(C)] #[repr(align(64))] -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub struct RTCPointQuery16 { pub x: [f32; 16usize], pub y: [f32; 16usize], @@ -648,6 +645,8 @@ pub struct RTCPointQuery16 { } #[test] fn bindgen_test_layout_RTCPointQuery16() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 320usize, @@ -659,7 +658,7 @@ fn bindgen_test_layout_RTCPointQuery16() { concat!("Alignment of ", stringify!(RTCPointQuery16)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).x) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -669,7 +668,7 @@ fn bindgen_test_layout_RTCPointQuery16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).y) as usize - ptr as usize }, 64usize, concat!( "Offset of field: ", @@ -679,7 +678,7 @@ fn bindgen_test_layout_RTCPointQuery16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).z) as usize - ptr as usize }, 128usize, concat!( "Offset of field: ", @@ -689,7 +688,7 @@ fn bindgen_test_layout_RTCPointQuery16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).time as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).time) as usize - ptr as usize }, 192usize, concat!( "Offset of field: ", @@ -699,7 +698,7 @@ fn bindgen_test_layout_RTCPointQuery16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).radius as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).radius) as usize - ptr as usize }, 256usize, concat!( "Offset of field: ", @@ -725,6 +724,8 @@ pub struct RTCPointQueryContext { } #[test] fn bindgen_test_layout_RTCPointQueryContext() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 144usize, @@ -736,7 +737,7 @@ fn bindgen_test_layout_RTCPointQueryContext() { concat!("Alignment of ", stringify!(RTCPointQueryContext)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).world2inst as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).world2inst) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -746,7 +747,7 @@ fn bindgen_test_layout_RTCPointQueryContext() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).inst2world as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).inst2world) as usize - ptr as usize }, 64usize, concat!( "Offset of field: ", @@ -756,7 +757,7 @@ fn bindgen_test_layout_RTCPointQueryContext() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).instID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).instID) as usize - ptr as usize }, 128usize, concat!( "Offset of field: ", @@ -766,9 +767,7 @@ fn bindgen_test_layout_RTCPointQueryContext() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).instStackSize as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).instStackSize) as usize - ptr as usize }, 132usize, concat!( "Offset of field: ", @@ -791,6 +790,9 @@ pub struct RTCPointQueryFunctionArguments { } #[test] fn bindgen_test_layout_RTCPointQueryFunctionArguments() { + const UNINIT: ::std::mem::MaybeUninit = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 48usize, @@ -802,9 +804,7 @@ fn bindgen_test_layout_RTCPointQueryFunctionArguments() { concat!("Alignment of ", stringify!(RTCPointQueryFunctionArguments)) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).query as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).query) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -814,9 +814,7 @@ fn bindgen_test_layout_RTCPointQueryFunctionArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).userPtr as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).userPtr) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -826,9 +824,7 @@ fn bindgen_test_layout_RTCPointQueryFunctionArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).primID as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).primID) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -838,9 +834,7 @@ fn bindgen_test_layout_RTCPointQueryFunctionArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).geomID as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).geomID) as usize - ptr as usize }, 20usize, concat!( "Offset of field: ", @@ -850,9 +844,7 @@ fn bindgen_test_layout_RTCPointQueryFunctionArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).context as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).context) as usize - ptr as usize }, 24usize, concat!( "Offset of field: ", @@ -862,10 +854,7 @@ fn bindgen_test_layout_RTCPointQueryFunctionArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).similarityScale as *const _ - as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).similarityScale) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -893,7 +882,7 @@ extern "C" { pub fn rtcReleaseDevice(device: RTCDevice); } #[repr(u32)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum RTCDeviceProperty { VERSION = 0, VERSION_MAJOR = 1, @@ -920,13 +909,13 @@ pub enum RTCDeviceProperty { PARALLEL_COMMIT_SUPPORTED = 130, } extern "C" { - pub fn rtcGetDeviceProperty(device: RTCDevice, prop: RTCDeviceProperty) -> ssize_t; + pub fn rtcGetDeviceProperty(device: RTCDevice, prop: RTCDeviceProperty) -> isize; } extern "C" { - pub fn rtcSetDeviceProperty(device: RTCDevice, prop: RTCDeviceProperty, value: ssize_t); + pub fn rtcSetDeviceProperty(device: RTCDevice, prop: RTCDeviceProperty, value: isize); } #[repr(u32)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum RTCError { NONE = 0, UNKNOWN = 1, @@ -939,29 +928,32 @@ pub enum RTCError { extern "C" { pub fn rtcGetDeviceError(device: RTCDevice) -> RTCError; } -pub type RTCErrorFunction = unsafe extern "C" fn( - userPtr: *mut ::std::os::raw::c_void, - code: RTCError, - str_: *const ::std::os::raw::c_char, -); +pub type RTCErrorFunction = ::std::option::Option< + unsafe extern "C" fn( + userPtr: *mut ::std::os::raw::c_void, + code: RTCError, + str_: *const ::std::os::raw::c_char, + ), +>; extern "C" { pub fn rtcSetDeviceErrorFunction( device: RTCDevice, - error: Option, + error: RTCErrorFunction, userPtr: *mut ::std::os::raw::c_void, ); } -pub type RTCMemoryMonitorFunction = - unsafe extern "C" fn(ptr: *mut ::std::os::raw::c_void, bytes: ssize_t, post: bool) -> bool; +pub type RTCMemoryMonitorFunction = ::std::option::Option< + unsafe extern "C" fn(ptr: *mut ::std::os::raw::c_void, bytes: isize, post: bool) -> bool, +>; extern "C" { pub fn rtcSetDeviceMemoryMonitorFunction( device: RTCDevice, - memoryMonitor: Option, + memoryMonitor: RTCMemoryMonitorFunction, userPtr: *mut ::std::os::raw::c_void, ); } #[repr(u32)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum RTCBufferType { INDEX = 0, VERTEX = 1, @@ -986,13 +978,13 @@ pub struct RTCBufferTy { } pub type RTCBuffer = *mut RTCBufferTy; extern "C" { - pub fn rtcNewBuffer(device: RTCDevice, byteSize: size_t) -> RTCBuffer; + pub fn rtcNewBuffer(device: RTCDevice, byteSize: usize) -> RTCBuffer; } extern "C" { pub fn rtcNewSharedBuffer( device: RTCDevice, ptr: *mut ::std::os::raw::c_void, - byteSize: size_t, + byteSize: usize, ) -> RTCBuffer; } extern "C" { @@ -1023,6 +1015,8 @@ pub struct RTCRay { } #[test] fn bindgen_test_layout_RTCRay() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 48usize, @@ -1034,7 +1028,7 @@ fn bindgen_test_layout_RTCRay() { concat!("Alignment of ", stringify!(RTCRay)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).org_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).org_x) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -1044,7 +1038,7 @@ fn bindgen_test_layout_RTCRay() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).org_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).org_y) as usize - ptr as usize }, 4usize, concat!( "Offset of field: ", @@ -1054,7 +1048,7 @@ fn bindgen_test_layout_RTCRay() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).org_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).org_z) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -1064,7 +1058,7 @@ fn bindgen_test_layout_RTCRay() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).tnear as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).tnear) as usize - ptr as usize }, 12usize, concat!( "Offset of field: ", @@ -1074,7 +1068,7 @@ fn bindgen_test_layout_RTCRay() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dir_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dir_x) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -1084,7 +1078,7 @@ fn bindgen_test_layout_RTCRay() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dir_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dir_y) as usize - ptr as usize }, 20usize, concat!( "Offset of field: ", @@ -1094,7 +1088,7 @@ fn bindgen_test_layout_RTCRay() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dir_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dir_z) as usize - ptr as usize }, 24usize, concat!( "Offset of field: ", @@ -1104,7 +1098,7 @@ fn bindgen_test_layout_RTCRay() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).time as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).time) as usize - ptr as usize }, 28usize, concat!( "Offset of field: ", @@ -1114,7 +1108,7 @@ fn bindgen_test_layout_RTCRay() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).tfar as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).tfar) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -1124,7 +1118,7 @@ fn bindgen_test_layout_RTCRay() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).mask as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).mask) as usize - ptr as usize }, 36usize, concat!( "Offset of field: ", @@ -1134,7 +1128,7 @@ fn bindgen_test_layout_RTCRay() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).id as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).id) as usize - ptr as usize }, 40usize, concat!( "Offset of field: ", @@ -1144,7 +1138,7 @@ fn bindgen_test_layout_RTCRay() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize }, 44usize, concat!( "Offset of field: ", @@ -1169,6 +1163,8 @@ pub struct RTCHit { } #[test] fn bindgen_test_layout_RTCHit() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 32usize, @@ -1180,7 +1176,7 @@ fn bindgen_test_layout_RTCHit() { concat!("Alignment of ", stringify!(RTCHit)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).Ng_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).Ng_x) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -1190,7 +1186,7 @@ fn bindgen_test_layout_RTCHit() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).Ng_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).Ng_y) as usize - ptr as usize }, 4usize, concat!( "Offset of field: ", @@ -1200,7 +1196,7 @@ fn bindgen_test_layout_RTCHit() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).Ng_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).Ng_z) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -1210,17 +1206,17 @@ fn bindgen_test_layout_RTCHit() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).u as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).u) as usize - ptr as usize }, 12usize, concat!("Offset of field: ", stringify!(RTCHit), "::", stringify!(u)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).v as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).v) as usize - ptr as usize }, 16usize, concat!("Offset of field: ", stringify!(RTCHit), "::", stringify!(v)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).primID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).primID) as usize - ptr as usize }, 20usize, concat!( "Offset of field: ", @@ -1230,7 +1226,7 @@ fn bindgen_test_layout_RTCHit() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).geomID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).geomID) as usize - ptr as usize }, 24usize, concat!( "Offset of field: ", @@ -1240,7 +1236,7 @@ fn bindgen_test_layout_RTCHit() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).instID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).instID) as usize - ptr as usize }, 28usize, concat!( "Offset of field: ", @@ -1259,6 +1255,8 @@ pub struct RTCRayHit { } #[test] fn bindgen_test_layout_RTCRayHit() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 80usize, @@ -1270,7 +1268,7 @@ fn bindgen_test_layout_RTCRayHit() { concat!("Alignment of ", stringify!(RTCRayHit)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).ray as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).ray) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -1280,7 +1278,7 @@ fn bindgen_test_layout_RTCRayHit() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).hit as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).hit) as usize - ptr as usize }, 48usize, concat!( "Offset of field: ", @@ -1309,6 +1307,8 @@ pub struct RTCRay4 { } #[test] fn bindgen_test_layout_RTCRay4() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 192usize, @@ -1320,7 +1320,7 @@ fn bindgen_test_layout_RTCRay4() { concat!("Alignment of ", stringify!(RTCRay4)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).org_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).org_x) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -1330,7 +1330,7 @@ fn bindgen_test_layout_RTCRay4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).org_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).org_y) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -1340,7 +1340,7 @@ fn bindgen_test_layout_RTCRay4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).org_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).org_z) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -1350,7 +1350,7 @@ fn bindgen_test_layout_RTCRay4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).tnear as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).tnear) as usize - ptr as usize }, 48usize, concat!( "Offset of field: ", @@ -1360,7 +1360,7 @@ fn bindgen_test_layout_RTCRay4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dir_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dir_x) as usize - ptr as usize }, 64usize, concat!( "Offset of field: ", @@ -1370,7 +1370,7 @@ fn bindgen_test_layout_RTCRay4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dir_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dir_y) as usize - ptr as usize }, 80usize, concat!( "Offset of field: ", @@ -1380,7 +1380,7 @@ fn bindgen_test_layout_RTCRay4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dir_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dir_z) as usize - ptr as usize }, 96usize, concat!( "Offset of field: ", @@ -1390,7 +1390,7 @@ fn bindgen_test_layout_RTCRay4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).time as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).time) as usize - ptr as usize }, 112usize, concat!( "Offset of field: ", @@ -1400,7 +1400,7 @@ fn bindgen_test_layout_RTCRay4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).tfar as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).tfar) as usize - ptr as usize }, 128usize, concat!( "Offset of field: ", @@ -1410,7 +1410,7 @@ fn bindgen_test_layout_RTCRay4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).mask as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).mask) as usize - ptr as usize }, 144usize, concat!( "Offset of field: ", @@ -1420,7 +1420,7 @@ fn bindgen_test_layout_RTCRay4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).id as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).id) as usize - ptr as usize }, 160usize, concat!( "Offset of field: ", @@ -1430,7 +1430,7 @@ fn bindgen_test_layout_RTCRay4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize }, 176usize, concat!( "Offset of field: ", @@ -1455,6 +1455,8 @@ pub struct RTCHit4 { } #[test] fn bindgen_test_layout_RTCHit4() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 128usize, @@ -1466,7 +1468,7 @@ fn bindgen_test_layout_RTCHit4() { concat!("Alignment of ", stringify!(RTCHit4)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).Ng_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).Ng_x) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -1476,7 +1478,7 @@ fn bindgen_test_layout_RTCHit4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).Ng_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).Ng_y) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -1486,7 +1488,7 @@ fn bindgen_test_layout_RTCHit4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).Ng_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).Ng_z) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -1496,7 +1498,7 @@ fn bindgen_test_layout_RTCHit4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).u as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).u) as usize - ptr as usize }, 48usize, concat!( "Offset of field: ", @@ -1506,7 +1508,7 @@ fn bindgen_test_layout_RTCHit4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).v as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).v) as usize - ptr as usize }, 64usize, concat!( "Offset of field: ", @@ -1516,7 +1518,7 @@ fn bindgen_test_layout_RTCHit4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).primID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).primID) as usize - ptr as usize }, 80usize, concat!( "Offset of field: ", @@ -1526,7 +1528,7 @@ fn bindgen_test_layout_RTCHit4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).geomID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).geomID) as usize - ptr as usize }, 96usize, concat!( "Offset of field: ", @@ -1536,7 +1538,7 @@ fn bindgen_test_layout_RTCHit4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).instID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).instID) as usize - ptr as usize }, 112usize, concat!( "Offset of field: ", @@ -1555,6 +1557,8 @@ pub struct RTCRayHit4 { } #[test] fn bindgen_test_layout_RTCRayHit4() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 320usize, @@ -1566,7 +1570,7 @@ fn bindgen_test_layout_RTCRayHit4() { concat!("Alignment of ", stringify!(RTCRayHit4)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).ray as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).ray) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -1576,7 +1580,7 @@ fn bindgen_test_layout_RTCRayHit4() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).hit as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).hit) as usize - ptr as usize }, 192usize, concat!( "Offset of field: ", @@ -1605,6 +1609,8 @@ pub struct RTCRay8 { } #[test] fn bindgen_test_layout_RTCRay8() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 384usize, @@ -1616,7 +1622,7 @@ fn bindgen_test_layout_RTCRay8() { concat!("Alignment of ", stringify!(RTCRay8)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).org_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).org_x) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -1626,7 +1632,7 @@ fn bindgen_test_layout_RTCRay8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).org_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).org_y) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -1636,7 +1642,7 @@ fn bindgen_test_layout_RTCRay8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).org_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).org_z) as usize - ptr as usize }, 64usize, concat!( "Offset of field: ", @@ -1646,7 +1652,7 @@ fn bindgen_test_layout_RTCRay8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).tnear as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).tnear) as usize - ptr as usize }, 96usize, concat!( "Offset of field: ", @@ -1656,7 +1662,7 @@ fn bindgen_test_layout_RTCRay8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dir_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dir_x) as usize - ptr as usize }, 128usize, concat!( "Offset of field: ", @@ -1666,7 +1672,7 @@ fn bindgen_test_layout_RTCRay8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dir_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dir_y) as usize - ptr as usize }, 160usize, concat!( "Offset of field: ", @@ -1676,7 +1682,7 @@ fn bindgen_test_layout_RTCRay8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dir_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dir_z) as usize - ptr as usize }, 192usize, concat!( "Offset of field: ", @@ -1686,7 +1692,7 @@ fn bindgen_test_layout_RTCRay8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).time as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).time) as usize - ptr as usize }, 224usize, concat!( "Offset of field: ", @@ -1696,7 +1702,7 @@ fn bindgen_test_layout_RTCRay8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).tfar as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).tfar) as usize - ptr as usize }, 256usize, concat!( "Offset of field: ", @@ -1706,7 +1712,7 @@ fn bindgen_test_layout_RTCRay8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).mask as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).mask) as usize - ptr as usize }, 288usize, concat!( "Offset of field: ", @@ -1716,7 +1722,7 @@ fn bindgen_test_layout_RTCRay8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).id as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).id) as usize - ptr as usize }, 320usize, concat!( "Offset of field: ", @@ -1726,7 +1732,7 @@ fn bindgen_test_layout_RTCRay8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize }, 352usize, concat!( "Offset of field: ", @@ -1751,6 +1757,8 @@ pub struct RTCHit8 { } #[test] fn bindgen_test_layout_RTCHit8() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 256usize, @@ -1762,7 +1770,7 @@ fn bindgen_test_layout_RTCHit8() { concat!("Alignment of ", stringify!(RTCHit8)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).Ng_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).Ng_x) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -1772,7 +1780,7 @@ fn bindgen_test_layout_RTCHit8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).Ng_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).Ng_y) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -1782,7 +1790,7 @@ fn bindgen_test_layout_RTCHit8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).Ng_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).Ng_z) as usize - ptr as usize }, 64usize, concat!( "Offset of field: ", @@ -1792,7 +1800,7 @@ fn bindgen_test_layout_RTCHit8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).u as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).u) as usize - ptr as usize }, 96usize, concat!( "Offset of field: ", @@ -1802,7 +1810,7 @@ fn bindgen_test_layout_RTCHit8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).v as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).v) as usize - ptr as usize }, 128usize, concat!( "Offset of field: ", @@ -1812,7 +1820,7 @@ fn bindgen_test_layout_RTCHit8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).primID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).primID) as usize - ptr as usize }, 160usize, concat!( "Offset of field: ", @@ -1822,7 +1830,7 @@ fn bindgen_test_layout_RTCHit8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).geomID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).geomID) as usize - ptr as usize }, 192usize, concat!( "Offset of field: ", @@ -1832,7 +1840,7 @@ fn bindgen_test_layout_RTCHit8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).instID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).instID) as usize - ptr as usize }, 224usize, concat!( "Offset of field: ", @@ -1851,6 +1859,8 @@ pub struct RTCRayHit8 { } #[test] fn bindgen_test_layout_RTCRayHit8() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 640usize, @@ -1862,7 +1872,7 @@ fn bindgen_test_layout_RTCRayHit8() { concat!("Alignment of ", stringify!(RTCRayHit8)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).ray as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).ray) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -1872,7 +1882,7 @@ fn bindgen_test_layout_RTCRayHit8() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).hit as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).hit) as usize - ptr as usize }, 384usize, concat!( "Offset of field: ", @@ -1884,7 +1894,7 @@ fn bindgen_test_layout_RTCRayHit8() { } #[repr(C)] #[repr(align(64))] -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub struct RTCRay16 { pub org_x: [f32; 16usize], pub org_y: [f32; 16usize], @@ -1901,6 +1911,8 @@ pub struct RTCRay16 { } #[test] fn bindgen_test_layout_RTCRay16() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 768usize, @@ -1912,7 +1924,7 @@ fn bindgen_test_layout_RTCRay16() { concat!("Alignment of ", stringify!(RTCRay16)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).org_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).org_x) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -1922,7 +1934,7 @@ fn bindgen_test_layout_RTCRay16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).org_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).org_y) as usize - ptr as usize }, 64usize, concat!( "Offset of field: ", @@ -1932,7 +1944,7 @@ fn bindgen_test_layout_RTCRay16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).org_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).org_z) as usize - ptr as usize }, 128usize, concat!( "Offset of field: ", @@ -1942,7 +1954,7 @@ fn bindgen_test_layout_RTCRay16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).tnear as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).tnear) as usize - ptr as usize }, 192usize, concat!( "Offset of field: ", @@ -1952,7 +1964,7 @@ fn bindgen_test_layout_RTCRay16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dir_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dir_x) as usize - ptr as usize }, 256usize, concat!( "Offset of field: ", @@ -1962,7 +1974,7 @@ fn bindgen_test_layout_RTCRay16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dir_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dir_y) as usize - ptr as usize }, 320usize, concat!( "Offset of field: ", @@ -1972,7 +1984,7 @@ fn bindgen_test_layout_RTCRay16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dir_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dir_z) as usize - ptr as usize }, 384usize, concat!( "Offset of field: ", @@ -1982,7 +1994,7 @@ fn bindgen_test_layout_RTCRay16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).time as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).time) as usize - ptr as usize }, 448usize, concat!( "Offset of field: ", @@ -1992,7 +2004,7 @@ fn bindgen_test_layout_RTCRay16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).tfar as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).tfar) as usize - ptr as usize }, 512usize, concat!( "Offset of field: ", @@ -2002,7 +2014,7 @@ fn bindgen_test_layout_RTCRay16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).mask as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).mask) as usize - ptr as usize }, 576usize, concat!( "Offset of field: ", @@ -2012,7 +2024,7 @@ fn bindgen_test_layout_RTCRay16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).id as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).id) as usize - ptr as usize }, 640usize, concat!( "Offset of field: ", @@ -2022,7 +2034,7 @@ fn bindgen_test_layout_RTCRay16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize }, 704usize, concat!( "Offset of field: ", @@ -2034,7 +2046,7 @@ fn bindgen_test_layout_RTCRay16() { } #[repr(C)] #[repr(align(64))] -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub struct RTCHit16 { pub Ng_x: [f32; 16usize], pub Ng_y: [f32; 16usize], @@ -2047,6 +2059,8 @@ pub struct RTCHit16 { } #[test] fn bindgen_test_layout_RTCHit16() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 512usize, @@ -2058,7 +2072,7 @@ fn bindgen_test_layout_RTCHit16() { concat!("Alignment of ", stringify!(RTCHit16)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).Ng_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).Ng_x) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -2068,7 +2082,7 @@ fn bindgen_test_layout_RTCHit16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).Ng_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).Ng_y) as usize - ptr as usize }, 64usize, concat!( "Offset of field: ", @@ -2078,7 +2092,7 @@ fn bindgen_test_layout_RTCHit16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).Ng_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).Ng_z) as usize - ptr as usize }, 128usize, concat!( "Offset of field: ", @@ -2088,7 +2102,7 @@ fn bindgen_test_layout_RTCHit16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).u as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).u) as usize - ptr as usize }, 192usize, concat!( "Offset of field: ", @@ -2098,7 +2112,7 @@ fn bindgen_test_layout_RTCHit16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).v as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).v) as usize - ptr as usize }, 256usize, concat!( "Offset of field: ", @@ -2108,7 +2122,7 @@ fn bindgen_test_layout_RTCHit16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).primID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).primID) as usize - ptr as usize }, 320usize, concat!( "Offset of field: ", @@ -2118,7 +2132,7 @@ fn bindgen_test_layout_RTCHit16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).geomID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).geomID) as usize - ptr as usize }, 384usize, concat!( "Offset of field: ", @@ -2128,7 +2142,7 @@ fn bindgen_test_layout_RTCHit16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).instID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).instID) as usize - ptr as usize }, 448usize, concat!( "Offset of field: ", @@ -2140,13 +2154,15 @@ fn bindgen_test_layout_RTCHit16() { } #[repr(C)] #[repr(align(64))] -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub struct RTCRayHit16 { pub ray: RTCRay16, pub hit: RTCHit16, } #[test] fn bindgen_test_layout_RTCRayHit16() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 1280usize, @@ -2158,7 +2174,7 @@ fn bindgen_test_layout_RTCRayHit16() { concat!("Alignment of ", stringify!(RTCRayHit16)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).ray as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).ray) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -2168,7 +2184,7 @@ fn bindgen_test_layout_RTCRayHit16() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).hit as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).hit) as usize - ptr as usize }, 768usize, concat!( "Offset of field: ", @@ -2196,6 +2212,8 @@ pub struct RTCRayNp { } #[test] fn bindgen_test_layout_RTCRayNp() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 96usize, @@ -2207,7 +2225,7 @@ fn bindgen_test_layout_RTCRayNp() { concat!("Alignment of ", stringify!(RTCRayNp)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).org_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).org_x) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -2217,7 +2235,7 @@ fn bindgen_test_layout_RTCRayNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).org_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).org_y) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -2227,7 +2245,7 @@ fn bindgen_test_layout_RTCRayNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).org_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).org_z) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -2237,7 +2255,7 @@ fn bindgen_test_layout_RTCRayNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).tnear as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).tnear) as usize - ptr as usize }, 24usize, concat!( "Offset of field: ", @@ -2247,7 +2265,7 @@ fn bindgen_test_layout_RTCRayNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dir_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dir_x) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -2257,7 +2275,7 @@ fn bindgen_test_layout_RTCRayNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dir_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dir_y) as usize - ptr as usize }, 40usize, concat!( "Offset of field: ", @@ -2267,7 +2285,7 @@ fn bindgen_test_layout_RTCRayNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dir_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dir_z) as usize - ptr as usize }, 48usize, concat!( "Offset of field: ", @@ -2277,7 +2295,7 @@ fn bindgen_test_layout_RTCRayNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).time as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).time) as usize - ptr as usize }, 56usize, concat!( "Offset of field: ", @@ -2287,7 +2305,7 @@ fn bindgen_test_layout_RTCRayNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).tfar as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).tfar) as usize - ptr as usize }, 64usize, concat!( "Offset of field: ", @@ -2297,7 +2315,7 @@ fn bindgen_test_layout_RTCRayNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).mask as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).mask) as usize - ptr as usize }, 72usize, concat!( "Offset of field: ", @@ -2307,7 +2325,7 @@ fn bindgen_test_layout_RTCRayNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).id as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).id) as usize - ptr as usize }, 80usize, concat!( "Offset of field: ", @@ -2317,7 +2335,7 @@ fn bindgen_test_layout_RTCRayNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize }, 88usize, concat!( "Offset of field: ", @@ -2341,6 +2359,8 @@ pub struct RTCHitNp { } #[test] fn bindgen_test_layout_RTCHitNp() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 64usize, @@ -2352,7 +2372,7 @@ fn bindgen_test_layout_RTCHitNp() { concat!("Alignment of ", stringify!(RTCHitNp)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).Ng_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).Ng_x) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -2362,7 +2382,7 @@ fn bindgen_test_layout_RTCHitNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).Ng_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).Ng_y) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -2372,7 +2392,7 @@ fn bindgen_test_layout_RTCHitNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).Ng_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).Ng_z) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -2382,7 +2402,7 @@ fn bindgen_test_layout_RTCHitNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).u as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).u) as usize - ptr as usize }, 24usize, concat!( "Offset of field: ", @@ -2392,7 +2412,7 @@ fn bindgen_test_layout_RTCHitNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).v as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).v) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -2402,7 +2422,7 @@ fn bindgen_test_layout_RTCHitNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).primID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).primID) as usize - ptr as usize }, 40usize, concat!( "Offset of field: ", @@ -2412,7 +2432,7 @@ fn bindgen_test_layout_RTCHitNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).geomID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).geomID) as usize - ptr as usize }, 48usize, concat!( "Offset of field: ", @@ -2422,7 +2442,7 @@ fn bindgen_test_layout_RTCHitNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).instID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).instID) as usize - ptr as usize }, 56usize, concat!( "Offset of field: ", @@ -2440,6 +2460,8 @@ pub struct RTCRayHitNp { } #[test] fn bindgen_test_layout_RTCRayHitNp() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 160usize, @@ -2451,7 +2473,7 @@ fn bindgen_test_layout_RTCRayHitNp() { concat!("Alignment of ", stringify!(RTCRayHitNp)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).ray as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).ray) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -2461,7 +2483,7 @@ fn bindgen_test_layout_RTCRayHitNp() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).hit as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).hit) as usize - ptr as usize }, 96usize, concat!( "Offset of field: ", @@ -2509,6 +2531,9 @@ pub struct RTCQuaternionDecomposition { } #[test] fn bindgen_test_layout_RTCQuaternionDecomposition() { + const UNINIT: ::std::mem::MaybeUninit = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 64usize, @@ -2520,9 +2545,7 @@ fn bindgen_test_layout_RTCQuaternionDecomposition() { concat!("Alignment of ", stringify!(RTCQuaternionDecomposition)) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).scale_x as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).scale_x) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -2532,9 +2555,7 @@ fn bindgen_test_layout_RTCQuaternionDecomposition() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).scale_y as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).scale_y) as usize - ptr as usize }, 4usize, concat!( "Offset of field: ", @@ -2544,9 +2565,7 @@ fn bindgen_test_layout_RTCQuaternionDecomposition() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).scale_z as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).scale_z) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -2556,9 +2575,7 @@ fn bindgen_test_layout_RTCQuaternionDecomposition() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).skew_xy as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).skew_xy) as usize - ptr as usize }, 12usize, concat!( "Offset of field: ", @@ -2568,9 +2585,7 @@ fn bindgen_test_layout_RTCQuaternionDecomposition() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).skew_xz as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).skew_xz) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -2580,9 +2595,7 @@ fn bindgen_test_layout_RTCQuaternionDecomposition() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).skew_yz as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).skew_yz) as usize - ptr as usize }, 20usize, concat!( "Offset of field: ", @@ -2592,9 +2605,7 @@ fn bindgen_test_layout_RTCQuaternionDecomposition() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).shift_x as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).shift_x) as usize - ptr as usize }, 24usize, concat!( "Offset of field: ", @@ -2604,9 +2615,7 @@ fn bindgen_test_layout_RTCQuaternionDecomposition() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).shift_y as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).shift_y) as usize - ptr as usize }, 28usize, concat!( "Offset of field: ", @@ -2616,9 +2625,7 @@ fn bindgen_test_layout_RTCQuaternionDecomposition() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).shift_z as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).shift_z) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -2628,9 +2635,7 @@ fn bindgen_test_layout_RTCQuaternionDecomposition() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).quaternion_r as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).quaternion_r) as usize - ptr as usize }, 36usize, concat!( "Offset of field: ", @@ -2640,9 +2645,7 @@ fn bindgen_test_layout_RTCQuaternionDecomposition() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).quaternion_i as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).quaternion_i) as usize - ptr as usize }, 40usize, concat!( "Offset of field: ", @@ -2652,9 +2655,7 @@ fn bindgen_test_layout_RTCQuaternionDecomposition() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).quaternion_j as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).quaternion_j) as usize - ptr as usize }, 44usize, concat!( "Offset of field: ", @@ -2664,9 +2665,7 @@ fn bindgen_test_layout_RTCQuaternionDecomposition() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).quaternion_k as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).quaternion_k) as usize - ptr as usize }, 48usize, concat!( "Offset of field: ", @@ -2676,10 +2675,7 @@ fn bindgen_test_layout_RTCQuaternionDecomposition() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).translation_x as *const _ - as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).translation_x) as usize - ptr as usize }, 52usize, concat!( "Offset of field: ", @@ -2689,10 +2685,7 @@ fn bindgen_test_layout_RTCQuaternionDecomposition() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).translation_y as *const _ - as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).translation_y) as usize - ptr as usize }, 56usize, concat!( "Offset of field: ", @@ -2702,10 +2695,7 @@ fn bindgen_test_layout_RTCQuaternionDecomposition() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).translation_z as *const _ - as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).translation_z) as usize - ptr as usize }, 60usize, concat!( "Offset of field: ", @@ -2728,7 +2718,7 @@ pub struct RTCGeometryTy { } pub type RTCGeometry = *mut RTCGeometryTy; #[repr(u32)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum RTCGeometryType { TRIANGLE = 0, QUAD = 1, @@ -2756,7 +2746,7 @@ pub enum RTCGeometryType { INSTANCE = 121, } #[repr(u32)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum RTCSubdivisionMode { NO_BOUNDARY = 0, SMOOTH_BOUNDARY = 1, @@ -2773,31 +2763,23 @@ impl RTCCurveFlags { impl ::std::ops::BitOr for RTCCurveFlags { type Output = Self; #[inline] - fn bitor(self, other: Self) -> Self { - RTCCurveFlags(self.0 | other.0) - } + fn bitor(self, other: Self) -> Self { RTCCurveFlags(self.0 | other.0) } } impl ::std::ops::BitOrAssign for RTCCurveFlags { #[inline] - fn bitor_assign(&mut self, rhs: RTCCurveFlags) { - self.0 |= rhs.0; - } + fn bitor_assign(&mut self, rhs: RTCCurveFlags) { self.0 |= rhs.0; } } impl ::std::ops::BitAnd for RTCCurveFlags { type Output = Self; #[inline] - fn bitand(self, other: Self) -> Self { - RTCCurveFlags(self.0 & other.0) - } + fn bitand(self, other: Self) -> Self { RTCCurveFlags(self.0 & other.0) } } impl ::std::ops::BitAndAssign for RTCCurveFlags { #[inline] - fn bitand_assign(&mut self, rhs: RTCCurveFlags) { - self.0 &= rhs.0; - } + fn bitand_assign(&mut self, rhs: RTCCurveFlags) { self.0 &= rhs.0; } } #[repr(transparent)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct RTCCurveFlags(pub ::std::os::raw::c_uint); #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -2809,6 +2791,9 @@ pub struct RTCBoundsFunctionArguments { } #[test] fn bindgen_test_layout_RTCBoundsFunctionArguments() { + const UNINIT: ::std::mem::MaybeUninit = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 24usize, @@ -2820,10 +2805,7 @@ fn bindgen_test_layout_RTCBoundsFunctionArguments() { concat!("Alignment of ", stringify!(RTCBoundsFunctionArguments)) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).geometryUserPtr as *const _ - as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).geometryUserPtr) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -2833,9 +2815,7 @@ fn bindgen_test_layout_RTCBoundsFunctionArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).primID as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).primID) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -2845,9 +2825,7 @@ fn bindgen_test_layout_RTCBoundsFunctionArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).timeStep as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).timeStep) as usize - ptr as usize }, 12usize, concat!( "Offset of field: ", @@ -2857,9 +2835,7 @@ fn bindgen_test_layout_RTCBoundsFunctionArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).bounds_o as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).bounds_o) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -2884,6 +2860,9 @@ pub struct RTCIntersectFunctionNArguments { } #[test] fn bindgen_test_layout_RTCIntersectFunctionNArguments() { + const UNINIT: ::std::mem::MaybeUninit = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 48usize, @@ -2895,9 +2874,7 @@ fn bindgen_test_layout_RTCIntersectFunctionNArguments() { concat!("Alignment of ", stringify!(RTCIntersectFunctionNArguments)) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).valid as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).valid) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -2907,10 +2884,7 @@ fn bindgen_test_layout_RTCIntersectFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).geometryUserPtr as *const _ - as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).geometryUserPtr) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -2920,9 +2894,7 @@ fn bindgen_test_layout_RTCIntersectFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).primID as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).primID) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -2932,9 +2904,7 @@ fn bindgen_test_layout_RTCIntersectFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).context as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).context) as usize - ptr as usize }, 24usize, concat!( "Offset of field: ", @@ -2944,9 +2914,7 @@ fn bindgen_test_layout_RTCIntersectFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).rayhit as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).rayhit) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -2956,9 +2924,7 @@ fn bindgen_test_layout_RTCIntersectFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).N as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).N) as usize - ptr as usize }, 40usize, concat!( "Offset of field: ", @@ -2968,9 +2934,7 @@ fn bindgen_test_layout_RTCIntersectFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).geomID as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).geomID) as usize - ptr as usize }, 44usize, concat!( "Offset of field: ", @@ -2995,6 +2959,9 @@ pub struct RTCOccludedFunctionNArguments { } #[test] fn bindgen_test_layout_RTCOccludedFunctionNArguments() { + const UNINIT: ::std::mem::MaybeUninit = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 48usize, @@ -3006,9 +2973,7 @@ fn bindgen_test_layout_RTCOccludedFunctionNArguments() { concat!("Alignment of ", stringify!(RTCOccludedFunctionNArguments)) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).valid as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).valid) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -3018,10 +2983,7 @@ fn bindgen_test_layout_RTCOccludedFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).geometryUserPtr as *const _ - as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).geometryUserPtr) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -3031,9 +2993,7 @@ fn bindgen_test_layout_RTCOccludedFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).primID as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).primID) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -3043,9 +3003,7 @@ fn bindgen_test_layout_RTCOccludedFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).context as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).context) as usize - ptr as usize }, 24usize, concat!( "Offset of field: ", @@ -3055,9 +3013,7 @@ fn bindgen_test_layout_RTCOccludedFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).ray as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).ray) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -3067,7 +3023,7 @@ fn bindgen_test_layout_RTCOccludedFunctionNArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).N as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).N) as usize - ptr as usize }, 40usize, concat!( "Offset of field: ", @@ -3077,9 +3033,7 @@ fn bindgen_test_layout_RTCOccludedFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).geomID as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).geomID) as usize - ptr as usize }, 44usize, concat!( "Offset of field: ", @@ -3110,6 +3064,9 @@ pub struct RTCDisplacementFunctionNArguments { } #[test] fn bindgen_test_layout_RTCDisplacementFunctionNArguments() { + const UNINIT: ::std::mem::MaybeUninit = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 96usize, @@ -3124,10 +3081,7 @@ fn bindgen_test_layout_RTCDisplacementFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).geometryUserPtr - as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).geometryUserPtr) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -3137,10 +3091,7 @@ fn bindgen_test_layout_RTCDisplacementFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).geometry as *const _ - as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).geometry) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -3150,10 +3101,7 @@ fn bindgen_test_layout_RTCDisplacementFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).primID as *const _ - as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).primID) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -3163,10 +3111,7 @@ fn bindgen_test_layout_RTCDisplacementFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).timeStep as *const _ - as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).timeStep) as usize - ptr as usize }, 20usize, concat!( "Offset of field: ", @@ -3176,9 +3121,7 @@ fn bindgen_test_layout_RTCDisplacementFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).u as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).u) as usize - ptr as usize }, 24usize, concat!( "Offset of field: ", @@ -3188,9 +3131,7 @@ fn bindgen_test_layout_RTCDisplacementFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).v as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).v) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -3200,9 +3141,7 @@ fn bindgen_test_layout_RTCDisplacementFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).Ng_x as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).Ng_x) as usize - ptr as usize }, 40usize, concat!( "Offset of field: ", @@ -3212,9 +3151,7 @@ fn bindgen_test_layout_RTCDisplacementFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).Ng_y as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).Ng_y) as usize - ptr as usize }, 48usize, concat!( "Offset of field: ", @@ -3224,9 +3161,7 @@ fn bindgen_test_layout_RTCDisplacementFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).Ng_z as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).Ng_z) as usize - ptr as usize }, 56usize, concat!( "Offset of field: ", @@ -3236,9 +3171,7 @@ fn bindgen_test_layout_RTCDisplacementFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).P_x as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).P_x) as usize - ptr as usize }, 64usize, concat!( "Offset of field: ", @@ -3248,9 +3181,7 @@ fn bindgen_test_layout_RTCDisplacementFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).P_y as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).P_y) as usize - ptr as usize }, 72usize, concat!( "Offset of field: ", @@ -3260,9 +3191,7 @@ fn bindgen_test_layout_RTCDisplacementFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).P_z as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).P_z) as usize - ptr as usize }, 80usize, concat!( "Offset of field: ", @@ -3272,9 +3201,7 @@ fn bindgen_test_layout_RTCDisplacementFunctionNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).N as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).N) as usize - ptr as usize }, 88usize, concat!( "Offset of field: ", @@ -3335,9 +3262,9 @@ extern "C" { slot: ::std::os::raw::c_uint, format: RTCFormat, buffer: RTCBuffer, - byteOffset: size_t, - byteStride: size_t, - itemCount: size_t, + byteOffset: usize, + byteStride: usize, + itemCount: usize, ); } extern "C" { @@ -3347,9 +3274,9 @@ extern "C" { slot: ::std::os::raw::c_uint, format: RTCFormat, ptr: *const ::std::os::raw::c_void, - byteOffset: size_t, - byteStride: size_t, - itemCount: size_t, + byteOffset: usize, + byteStride: usize, + itemCount: usize, ); } extern "C" { @@ -3358,8 +3285,8 @@ extern "C" { type_: RTCBufferType, slot: ::std::os::raw::c_uint, format: RTCFormat, - byteStride: size_t, - itemCount: size_t, + byteStride: usize, + itemCount: usize, ) -> *mut ::std::os::raw::c_void; } extern "C" { @@ -3530,6 +3457,9 @@ pub struct RTCInterpolateArguments { } #[test] fn bindgen_test_layout_RTCInterpolateArguments() { + const UNINIT: ::std::mem::MaybeUninit = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 88usize, @@ -3541,9 +3471,7 @@ fn bindgen_test_layout_RTCInterpolateArguments() { concat!("Alignment of ", stringify!(RTCInterpolateArguments)) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).geometry as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).geometry) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -3553,7 +3481,7 @@ fn bindgen_test_layout_RTCInterpolateArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).primID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).primID) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -3563,7 +3491,7 @@ fn bindgen_test_layout_RTCInterpolateArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).u as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).u) as usize - ptr as usize }, 12usize, concat!( "Offset of field: ", @@ -3573,7 +3501,7 @@ fn bindgen_test_layout_RTCInterpolateArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).v as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).v) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -3583,9 +3511,7 @@ fn bindgen_test_layout_RTCInterpolateArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).bufferType as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).bufferType) as usize - ptr as usize }, 20usize, concat!( "Offset of field: ", @@ -3595,9 +3521,7 @@ fn bindgen_test_layout_RTCInterpolateArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).bufferSlot as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).bufferSlot) as usize - ptr as usize }, 24usize, concat!( "Offset of field: ", @@ -3607,7 +3531,7 @@ fn bindgen_test_layout_RTCInterpolateArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).P as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).P) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -3617,7 +3541,7 @@ fn bindgen_test_layout_RTCInterpolateArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dPdu as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dPdu) as usize - ptr as usize }, 40usize, concat!( "Offset of field: ", @@ -3627,7 +3551,7 @@ fn bindgen_test_layout_RTCInterpolateArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dPdv as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dPdv) as usize - ptr as usize }, 48usize, concat!( "Offset of field: ", @@ -3637,7 +3561,7 @@ fn bindgen_test_layout_RTCInterpolateArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).ddPdudu as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).ddPdudu) as usize - ptr as usize }, 56usize, concat!( "Offset of field: ", @@ -3647,7 +3571,7 @@ fn bindgen_test_layout_RTCInterpolateArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).ddPdvdv as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).ddPdvdv) as usize - ptr as usize }, 64usize, concat!( "Offset of field: ", @@ -3657,7 +3581,7 @@ fn bindgen_test_layout_RTCInterpolateArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).ddPdudv as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).ddPdudv) as usize - ptr as usize }, 72usize, concat!( "Offset of field: ", @@ -3667,9 +3591,7 @@ fn bindgen_test_layout_RTCInterpolateArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).valueCount as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).valueCount) as usize - ptr as usize }, 80usize, concat!( "Offset of field: ", @@ -3703,6 +3625,9 @@ pub struct RTCInterpolateNArguments { } #[test] fn bindgen_test_layout_RTCInterpolateNArguments() { + const UNINIT: ::std::mem::MaybeUninit = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 112usize, @@ -3714,9 +3639,7 @@ fn bindgen_test_layout_RTCInterpolateNArguments() { concat!("Alignment of ", stringify!(RTCInterpolateNArguments)) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).geometry as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).geometry) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -3726,7 +3649,7 @@ fn bindgen_test_layout_RTCInterpolateNArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).valid as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).valid) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -3736,9 +3659,7 @@ fn bindgen_test_layout_RTCInterpolateNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).primIDs as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).primIDs) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -3748,7 +3669,7 @@ fn bindgen_test_layout_RTCInterpolateNArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).u as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).u) as usize - ptr as usize }, 24usize, concat!( "Offset of field: ", @@ -3758,7 +3679,7 @@ fn bindgen_test_layout_RTCInterpolateNArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).v as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).v) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -3768,7 +3689,7 @@ fn bindgen_test_layout_RTCInterpolateNArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).N as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).N) as usize - ptr as usize }, 40usize, concat!( "Offset of field: ", @@ -3778,9 +3699,7 @@ fn bindgen_test_layout_RTCInterpolateNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).bufferType as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).bufferType) as usize - ptr as usize }, 44usize, concat!( "Offset of field: ", @@ -3790,9 +3709,7 @@ fn bindgen_test_layout_RTCInterpolateNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).bufferSlot as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).bufferSlot) as usize - ptr as usize }, 48usize, concat!( "Offset of field: ", @@ -3802,7 +3719,7 @@ fn bindgen_test_layout_RTCInterpolateNArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).P as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).P) as usize - ptr as usize }, 56usize, concat!( "Offset of field: ", @@ -3812,7 +3729,7 @@ fn bindgen_test_layout_RTCInterpolateNArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dPdu as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dPdu) as usize - ptr as usize }, 64usize, concat!( "Offset of field: ", @@ -3822,7 +3739,7 @@ fn bindgen_test_layout_RTCInterpolateNArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).dPdv as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).dPdv) as usize - ptr as usize }, 72usize, concat!( "Offset of field: ", @@ -3832,9 +3749,7 @@ fn bindgen_test_layout_RTCInterpolateNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).ddPdudu as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).ddPdudu) as usize - ptr as usize }, 80usize, concat!( "Offset of field: ", @@ -3844,9 +3759,7 @@ fn bindgen_test_layout_RTCInterpolateNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).ddPdvdv as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).ddPdvdv) as usize - ptr as usize }, 88usize, concat!( "Offset of field: ", @@ -3856,9 +3769,7 @@ fn bindgen_test_layout_RTCInterpolateNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).ddPdudv as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).ddPdudv) as usize - ptr as usize }, 96usize, concat!( "Offset of field: ", @@ -3868,9 +3779,7 @@ fn bindgen_test_layout_RTCInterpolateNArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).valueCount as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).valueCount) as usize - ptr as usize }, 104usize, concat!( "Offset of field: ", @@ -3893,6 +3802,8 @@ pub struct RTCGrid { } #[test] fn bindgen_test_layout_RTCGrid() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 12usize, @@ -3904,7 +3815,7 @@ fn bindgen_test_layout_RTCGrid() { concat!("Alignment of ", stringify!(RTCGrid)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).startVertexID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).startVertexID) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -3914,7 +3825,7 @@ fn bindgen_test_layout_RTCGrid() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).stride as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).stride) as usize - ptr as usize }, 4usize, concat!( "Offset of field: ", @@ -3924,7 +3835,7 @@ fn bindgen_test_layout_RTCGrid() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).width as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).width) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -3934,7 +3845,7 @@ fn bindgen_test_layout_RTCGrid() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).height as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).height) as usize - ptr as usize }, 10usize, concat!( "Offset of field: ", @@ -3962,31 +3873,23 @@ impl RTCSceneFlags { impl ::std::ops::BitOr for RTCSceneFlags { type Output = Self; #[inline] - fn bitor(self, other: Self) -> Self { - RTCSceneFlags(self.0 | other.0) - } + fn bitor(self, other: Self) -> Self { RTCSceneFlags(self.0 | other.0) } } impl ::std::ops::BitOrAssign for RTCSceneFlags { #[inline] - fn bitor_assign(&mut self, rhs: RTCSceneFlags) { - self.0 |= rhs.0; - } + fn bitor_assign(&mut self, rhs: RTCSceneFlags) { self.0 |= rhs.0; } } impl ::std::ops::BitAnd for RTCSceneFlags { type Output = Self; #[inline] - fn bitand(self, other: Self) -> Self { - RTCSceneFlags(self.0 & other.0) - } + fn bitand(self, other: Self) -> Self { RTCSceneFlags(self.0 & other.0) } } impl ::std::ops::BitAndAssign for RTCSceneFlags { #[inline] - fn bitand_assign(&mut self, rhs: RTCSceneFlags) { - self.0 &= rhs.0; - } + fn bitand_assign(&mut self, rhs: RTCSceneFlags) { self.0 &= rhs.0; } } #[repr(transparent)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct RTCSceneFlags(pub ::std::os::raw::c_uint); extern "C" { pub fn rtcNewScene(device: RTCDevice) -> RTCScene; @@ -4016,6 +3919,10 @@ extern "C" { extern "C" { pub fn rtcGetGeometry(scene: RTCScene, geomID: ::std::os::raw::c_uint) -> RTCGeometry; } +extern "C" { + pub fn rtcGetGeometryThreadSafe(scene: RTCScene, geomID: ::std::os::raw::c_uint) + -> RTCGeometry; +} extern "C" { pub fn rtcCommitScene(scene: RTCScene); } @@ -4023,11 +3930,11 @@ extern "C" { pub fn rtcJoinCommitScene(scene: RTCScene); } pub type RTCProgressMonitorFunction = - unsafe extern "C" fn(ptr: *mut ::std::os::raw::c_void, n: f64) -> bool; + ::std::option::Option bool>; extern "C" { pub fn rtcSetSceneProgressMonitorFunction( scene: RTCScene, - progress: Option, + progress: RTCProgressMonitorFunction, ptr: *mut ::std::os::raw::c_void, ); } @@ -4122,7 +4029,7 @@ extern "C" { context: *mut RTCIntersectContext, rayhit: *mut RTCRayHit, M: ::std::os::raw::c_uint, - byteStride: size_t, + byteStride: usize, ); } extern "C" { @@ -4140,7 +4047,7 @@ extern "C" { rayhit: *mut RTCRayHitN, N: ::std::os::raw::c_uint, M: ::std::os::raw::c_uint, - byteStride: size_t, + byteStride: usize, ); } extern "C" { @@ -4184,7 +4091,7 @@ extern "C" { context: *mut RTCIntersectContext, ray: *mut RTCRay, M: ::std::os::raw::c_uint, - byteStride: size_t, + byteStride: usize, ); } extern "C" { @@ -4202,7 +4109,7 @@ extern "C" { ray: *mut RTCRayN, N: ::std::os::raw::c_uint, M: ::std::os::raw::c_uint, - byteStride: size_t, + byteStride: usize, ); } extern "C" { @@ -4223,6 +4130,8 @@ pub struct RTCCollision { } #[test] fn bindgen_test_layout_RTCCollision() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 16usize, @@ -4234,7 +4143,7 @@ fn bindgen_test_layout_RTCCollision() { concat!("Alignment of ", stringify!(RTCCollision)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).geomID0 as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).geomID0) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -4244,7 +4153,7 @@ fn bindgen_test_layout_RTCCollision() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).primID0 as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).primID0) as usize - ptr as usize }, 4usize, concat!( "Offset of field: ", @@ -4254,7 +4163,7 @@ fn bindgen_test_layout_RTCCollision() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).geomID1 as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).geomID1) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -4264,7 +4173,7 @@ fn bindgen_test_layout_RTCCollision() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).primID1 as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).primID1) as usize - ptr as usize }, 12usize, concat!( "Offset of field: ", @@ -4310,6 +4219,8 @@ pub struct RTCBuildPrimitive { } #[test] fn bindgen_test_layout_RTCBuildPrimitive() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 32usize, @@ -4321,7 +4232,7 @@ fn bindgen_test_layout_RTCBuildPrimitive() { concat!("Alignment of ", stringify!(RTCBuildPrimitive)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).lower_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).lower_x) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -4331,7 +4242,7 @@ fn bindgen_test_layout_RTCBuildPrimitive() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).lower_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).lower_y) as usize - ptr as usize }, 4usize, concat!( "Offset of field: ", @@ -4341,7 +4252,7 @@ fn bindgen_test_layout_RTCBuildPrimitive() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).lower_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).lower_z) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -4351,7 +4262,7 @@ fn bindgen_test_layout_RTCBuildPrimitive() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).geomID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).geomID) as usize - ptr as usize }, 12usize, concat!( "Offset of field: ", @@ -4361,7 +4272,7 @@ fn bindgen_test_layout_RTCBuildPrimitive() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).upper_x as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).upper_x) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -4371,7 +4282,7 @@ fn bindgen_test_layout_RTCBuildPrimitive() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).upper_y as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).upper_y) as usize - ptr as usize }, 20usize, concat!( "Offset of field: ", @@ -4381,7 +4292,7 @@ fn bindgen_test_layout_RTCBuildPrimitive() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).upper_z as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).upper_z) as usize - ptr as usize }, 24usize, concat!( "Offset of field: ", @@ -4391,7 +4302,7 @@ fn bindgen_test_layout_RTCBuildPrimitive() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).primID as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).primID) as usize - ptr as usize }, 28usize, concat!( "Offset of field: ", @@ -4434,7 +4345,7 @@ pub type RTCCreateLeafFunction = ::std::option::Option< unsafe extern "C" fn( allocator: RTCThreadLocalAllocator, primitives: *const RTCBuildPrimitive, - primitiveCount: size_t, + primitiveCount: usize, userPtr: *mut ::std::os::raw::c_void, ) -> *mut ::std::os::raw::c_void, >; @@ -4457,38 +4368,30 @@ impl RTCBuildFlags { impl ::std::ops::BitOr for RTCBuildFlags { type Output = Self; #[inline] - fn bitor(self, other: Self) -> Self { - RTCBuildFlags(self.0 | other.0) - } + fn bitor(self, other: Self) -> Self { RTCBuildFlags(self.0 | other.0) } } impl ::std::ops::BitOrAssign for RTCBuildFlags { #[inline] - fn bitor_assign(&mut self, rhs: RTCBuildFlags) { - self.0 |= rhs.0; - } + fn bitor_assign(&mut self, rhs: RTCBuildFlags) { self.0 |= rhs.0; } } impl ::std::ops::BitAnd for RTCBuildFlags { type Output = Self; #[inline] - fn bitand(self, other: Self) -> Self { - RTCBuildFlags(self.0 & other.0) - } + fn bitand(self, other: Self) -> Self { RTCBuildFlags(self.0 & other.0) } } impl ::std::ops::BitAndAssign for RTCBuildFlags { #[inline] - fn bitand_assign(&mut self, rhs: RTCBuildFlags) { - self.0 &= rhs.0; - } + fn bitand_assign(&mut self, rhs: RTCBuildFlags) { self.0 &= rhs.0; } } #[repr(transparent)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct RTCBuildFlags(pub ::std::os::raw::c_uint); pub const RTCBuildConstants_RTC_BUILD_MAX_PRIMITIVES_PER_LEAF: RTCBuildConstants = 32; pub type RTCBuildConstants = ::std::os::raw::c_uint; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct RTCBuildArguments { - pub byteSize: size_t, + pub byteSize: usize, pub buildQuality: RTCBuildQuality, pub buildFlags: RTCBuildFlags, pub maxBranchingFactor: ::std::os::raw::c_uint, @@ -4500,8 +4403,8 @@ pub struct RTCBuildArguments { pub intersectionCost: f32, pub bvh: RTCBVH, pub primitives: *mut RTCBuildPrimitive, - pub primitiveCount: size_t, - pub primitiveArrayCapacity: size_t, + pub primitiveCount: usize, + pub primitiveArrayCapacity: usize, pub createNode: RTCCreateNodeFunction, pub setNodeChildren: RTCSetNodeChildrenFunction, pub setNodeBounds: RTCSetNodeBoundsFunction, @@ -4512,6 +4415,8 @@ pub struct RTCBuildArguments { } #[test] fn bindgen_test_layout_RTCBuildArguments() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 136usize, @@ -4523,7 +4428,7 @@ fn bindgen_test_layout_RTCBuildArguments() { concat!("Alignment of ", stringify!(RTCBuildArguments)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).byteSize as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).byteSize) as usize - ptr as usize }, 0usize, concat!( "Offset of field: ", @@ -4533,7 +4438,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).buildQuality as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).buildQuality) as usize - ptr as usize }, 8usize, concat!( "Offset of field: ", @@ -4543,7 +4448,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).buildFlags as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).buildFlags) as usize - ptr as usize }, 12usize, concat!( "Offset of field: ", @@ -4553,9 +4458,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).maxBranchingFactor as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).maxBranchingFactor) as usize - ptr as usize }, 16usize, concat!( "Offset of field: ", @@ -4565,7 +4468,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).maxDepth as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).maxDepth) as usize - ptr as usize }, 20usize, concat!( "Offset of field: ", @@ -4575,7 +4478,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).sahBlockSize as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).sahBlockSize) as usize - ptr as usize }, 24usize, concat!( "Offset of field: ", @@ -4585,7 +4488,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).minLeafSize as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).minLeafSize) as usize - ptr as usize }, 28usize, concat!( "Offset of field: ", @@ -4595,7 +4498,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).maxLeafSize as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).maxLeafSize) as usize - ptr as usize }, 32usize, concat!( "Offset of field: ", @@ -4605,7 +4508,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).traversalCost as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).traversalCost) as usize - ptr as usize }, 36usize, concat!( "Offset of field: ", @@ -4615,9 +4518,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).intersectionCost as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).intersectionCost) as usize - ptr as usize }, 40usize, concat!( "Offset of field: ", @@ -4627,7 +4528,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).bvh as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).bvh) as usize - ptr as usize }, 48usize, concat!( "Offset of field: ", @@ -4637,7 +4538,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).primitives as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).primitives) as usize - ptr as usize }, 56usize, concat!( "Offset of field: ", @@ -4647,9 +4548,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).primitiveCount as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).primitiveCount) as usize - ptr as usize }, 64usize, concat!( "Offset of field: ", @@ -4659,10 +4558,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).primitiveArrayCapacity as *const _ - as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).primitiveArrayCapacity) as usize - ptr as usize }, 72usize, concat!( "Offset of field: ", @@ -4672,7 +4568,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).createNode as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).createNode) as usize - ptr as usize }, 80usize, concat!( "Offset of field: ", @@ -4682,9 +4578,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).setNodeChildren as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).setNodeChildren) as usize - ptr as usize }, 88usize, concat!( "Offset of field: ", @@ -4694,7 +4588,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).setNodeBounds as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).setNodeBounds) as usize - ptr as usize }, 96usize, concat!( "Offset of field: ", @@ -4704,7 +4598,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).createLeaf as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).createLeaf) as usize - ptr as usize }, 104usize, concat!( "Offset of field: ", @@ -4714,9 +4608,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { - &(*(::std::ptr::null::())).splitPrimitive as *const _ as usize - }, + unsafe { ::std::ptr::addr_of!((*ptr).splitPrimitive) as usize - ptr as usize }, 112usize, concat!( "Offset of field: ", @@ -4726,7 +4618,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).buildProgress as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).buildProgress) as usize - ptr as usize }, 120usize, concat!( "Offset of field: ", @@ -4736,7 +4628,7 @@ fn bindgen_test_layout_RTCBuildArguments() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).userPtr as *const _ as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).userPtr) as usize - ptr as usize }, 128usize, concat!( "Offset of field: ", @@ -4755,8 +4647,8 @@ extern "C" { extern "C" { pub fn rtcThreadLocalAlloc( allocator: RTCThreadLocalAllocator, - bytes: size_t, - align: size_t, + bytes: usize, + align: usize, ) -> *mut ::std::os::raw::c_void; } extern "C" { diff --git a/src/triangle_mesh.rs b/src/triangle_mesh.rs deleted file mode 100644 index 68316c1bd..000000000 --- a/src/triangle_mesh.rs +++ /dev/null @@ -1,67 +0,0 @@ -use std::sync::Arc; - -use crate::buffer::Buffer; -use crate::device::Device; -use crate::geometry::Geometry; -use crate::sys::*; -use crate::{BufferType, Format, GeometryType}; - -pub struct TriangleMesh { - device: Arc, - pub(crate) handle: RTCGeometry, - pub vertex_buffer: Buffer<[f32; 4]>, - pub index_buffer: Buffer<[u32; 3]>, -} - -impl TriangleMesh { - pub fn unanimated(device: Arc, num_tris: usize, num_verts: usize) -> Arc { - let h = unsafe { rtcNewGeometry(device.handle, GeometryType::TRIANGLE) }; - let mut vertex_buffer = Buffer::new(device.clone(), num_verts); - let mut index_buffer = Buffer::new(device.clone(), num_tris); - unsafe { - rtcSetGeometryBuffer( - h, - BufferType::VERTEX, - 0, - Format::FLOAT3, - vertex_buffer.handle, - 0, - 16, - num_verts, - ); - vertex_buffer.set_attachment(h, BufferType::VERTEX, 0); - - rtcSetGeometryBuffer( - h, - BufferType::INDEX, - 0, - Format::UINT3, - index_buffer.handle, - 0, - 12, - num_tris, - ); - index_buffer.set_attachment(h, BufferType::INDEX, 0); - } - Arc::new(TriangleMesh { - device: device, - handle: h, - vertex_buffer: vertex_buffer, - index_buffer: index_buffer, - }) - } -} - -impl Geometry for TriangleMesh { - fn handle(&self) -> RTCGeometry { - self.handle - } -} - -impl Drop for TriangleMesh { - fn drop(&mut self) { - unsafe { - rtcReleaseGeometry(self.handle); - } - } -}