From 08f6c9db507710afafaa657400c96206375b8c56 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Wed, 8 Feb 2023 12:18:47 +0100 Subject: [PATCH 01/65] update sys binding generation script --- scripts/generate-sys-bindings.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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" \ From 30693bec27a51b93761ecb0af92f56e477e8e700 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Wed, 8 Feb 2023 13:21:47 +0100 Subject: [PATCH 02/65] update sys bindings generated against v3.13.5 --- src/callback.rs | 6 +- src/device.rs | 6 +- src/geometry.rs | 1 - src/scene.rs | 2 +- src/sys.rs | 909 ++++++++++++++++++++++-------------------------- 5 files changed, 424 insertions(+), 500 deletions(-) diff --git a/src/callback.rs b/src/callback.rs index 0c8a8364b..36c580f4d 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -15,7 +15,7 @@ where cb(n) } - inner:: + Some(inner::) } /// Helper function to convert a Rust closure to `RTCErrorFunction` callback. @@ -34,7 +34,7 @@ where cb(error, std::ffi::CStr::from_ptr(msg).to_str().unwrap()) } - inner:: + Some(inner::) } /// Helper function to convert a Rust closure to `RTCMemoryMonitorFunction` callback. @@ -50,5 +50,5 @@ where cb(bytes, post) } - inner:: + Some(inner::) } diff --git a/src/device.rs b/src/device.rs index afc34726f..a1f91431a 100644 --- a/src/device.rs +++ b/src/device.rs @@ -66,7 +66,7 @@ impl Device { unsafe { rtcSetDeviceErrorFunction( self.handle, - Some(crate::callback::error_function_helper(&mut closure)), + crate::callback::error_function_helper(&mut closure), &mut closure as *mut _ as *mut ::std::os::raw::c_void, ); } @@ -129,9 +129,7 @@ impl Device { unsafe { rtcSetDeviceMemoryMonitorFunction( self.handle, - Some(crate::callback::memory_monitor_function_helper( - &mut closure, - )), + crate::callback::memory_monitor_function_helper(&mut closure), &mut closure as *mut _ as *mut ::std::os::raw::c_void, ); } diff --git a/src/geometry.rs b/src/geometry.rs index 48f90ca01..c2f77dcde 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,4 +1,3 @@ -use crate::callback; use crate::sys::*; /// Geometry trait implemented by all Embree Geometry types diff --git a/src/scene.rs b/src/scene.rs index c84dc1f86..84847fc7f 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -118,7 +118,7 @@ impl Scene { rtcSetSceneProgressMonitorFunction( self.handle, - Some(callback::progress_monitor_function_helper(&mut closure)), + callback::progress_monitor_function_helper(&mut closure), &mut closure as *mut _ as *mut ::std::os::raw::c_void, ); } diff --git a/src/sys.rs b/src/sys.rs index 37504938f..7871b488f 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: ", @@ -238,13 +239,16 @@ fn bindgen_test_layout_RTCLinearBounds() { ); } impl RTCIntersectContextFlags { - pub const NONE: RTCIntersectContextFlags = RTCIntersectContextFlags(0); + pub const NONE: RTCIntersectContextFlags = + RTCIntersectContextFlags(0); } impl RTCIntersectContextFlags { - pub const INCOHERENT: RTCIntersectContextFlags = RTCIntersectContextFlags(0); + pub const INCOHERENT: RTCIntersectContextFlags = + RTCIntersectContextFlags(0); } impl RTCIntersectContextFlags { - pub const COHERENT: RTCIntersectContextFlags = RTCIntersectContextFlags(1); + pub const COHERENT: RTCIntersectContextFlags = + RTCIntersectContextFlags(1); } impl ::std::ops::BitOr for RTCIntersectContextFlags { type Output = Self; @@ -273,7 +277,7 @@ impl ::std::ops::BitAndAssign for RTCIntersectContextFlags { } } #[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 +291,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 +305,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 +315,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 +325,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 +335,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 +345,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 +355,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 +376,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 +389,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 +399,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 +409,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 +431,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 +444,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 +454,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 +464,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 +474,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 +484,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 +506,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 +519,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 +529,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 +539,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 +549,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 +559,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 +581,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 +594,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 +604,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 +614,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 +624,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 +634,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 +646,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 +656,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 +669,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 +679,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 +689,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 +699,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 +709,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 +735,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 +748,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 +758,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 +768,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 +778,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 +801,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 +815,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 +825,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 +835,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 +845,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 +855,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 +865,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 +893,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 +920,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 +939,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 +989,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 +1026,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 +1039,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 +1049,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 +1059,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 +1069,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 +1079,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 +1089,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 +1099,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 +1109,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 +1119,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 +1129,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 +1139,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 +1149,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 +1174,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 +1187,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 +1197,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 +1207,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 +1217,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 +1237,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 +1247,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 +1266,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 +1279,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 +1289,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 +1318,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 +1331,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 +1341,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 +1351,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 +1361,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 +1371,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 +1381,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 +1391,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 +1401,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 +1411,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 +1421,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 +1431,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 +1441,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 +1466,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 +1479,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 +1489,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 +1499,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 +1509,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 +1519,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 +1529,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 +1539,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 +1549,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 +1568,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 +1581,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 +1591,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 +1620,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 +1633,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 +1643,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 +1653,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 +1663,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 +1673,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 +1683,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 +1693,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 +1703,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 +1713,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 +1723,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 +1733,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 +1743,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 +1768,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 +1781,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 +1791,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 +1801,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 +1811,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 +1821,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 +1831,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 +1841,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 +1851,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 +1870,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 +1883,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 +1893,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 +1905,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 +1922,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 +1935,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 +1945,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 +1955,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 +1965,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 +1975,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 +1985,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 +1995,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 +2005,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 +2015,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 +2025,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 +2035,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 +2045,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 +2057,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 +2070,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 +2083,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 +2093,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 +2103,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 +2113,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 +2123,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 +2133,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 +2143,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 +2153,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 +2165,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 +2185,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 +2195,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 +2223,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 +2236,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 +2246,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 +2256,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 +2266,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 +2276,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 +2286,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 +2296,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 +2306,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 +2316,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 +2326,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 +2336,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 +2346,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 +2370,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 +2383,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 +2393,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 +2403,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 +2413,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 +2423,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 +2433,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 +2443,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 +2453,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 +2471,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 +2484,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 +2494,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 +2542,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 +2556,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 +2566,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 +2576,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 +2586,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 +2596,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 +2606,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 +2616,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 +2626,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 +2636,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 +2646,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 +2656,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 +2666,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 +2676,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 +2686,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 +2696,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 +2706,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 +2729,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 +2757,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, @@ -2797,7 +2798,7 @@ impl ::std::ops::BitAndAssign for RTCCurveFlags { } } #[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 +2810,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 +2824,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 +2834,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 +2844,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 +2854,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 +2879,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 +2893,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 +2903,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 +2913,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 +2923,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 +2933,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 +2943,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 +2953,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 +2978,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 +2992,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 +3002,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 +3012,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 +3022,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 +3032,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 +3042,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 +3052,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 +3083,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 +3100,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 +3110,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 +3120,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 +3130,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 +3140,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 +3150,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 +3160,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 +3170,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 +3180,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 +3190,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 +3200,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 +3210,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 +3220,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 +3281,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 +3293,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 +3304,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 +3476,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 +3490,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 +3500,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 +3510,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 +3520,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 +3530,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 +3540,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 +3550,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 +3560,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 +3570,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 +3580,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 +3590,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 +3600,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 +3610,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 +3644,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 +3658,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 +3668,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 +3678,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 +3688,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 +3698,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 +3708,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 +3718,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 +3728,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 +3738,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 +3748,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 +3758,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 +3768,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 +3778,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 +3788,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 +3798,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 +3821,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 +3834,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 +3844,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 +3854,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 +3864,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: ", @@ -3986,7 +3916,7 @@ impl ::std::ops::BitAndAssign for RTCSceneFlags { } } #[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 +3946,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 +3957,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 +4056,7 @@ extern "C" { context: *mut RTCIntersectContext, rayhit: *mut RTCRayHit, M: ::std::os::raw::c_uint, - byteStride: size_t, + byteStride: usize, ); } extern "C" { @@ -4140,7 +4074,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 +4118,7 @@ extern "C" { context: *mut RTCIntersectContext, ray: *mut RTCRay, M: ::std::os::raw::c_uint, - byteStride: size_t, + byteStride: usize, ); } extern "C" { @@ -4202,7 +4136,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 +4157,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 +4170,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 +4180,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 +4190,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 +4200,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 +4246,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 +4259,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 +4269,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 +4279,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 +4289,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 +4299,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 +4309,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 +4319,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 +4329,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 +4372,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, >; @@ -4481,14 +4419,14 @@ impl ::std::ops::BitAndAssign for RTCBuildFlags { } } #[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 +4438,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 +4450,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 +4463,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 +4473,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 +4483,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 +4493,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 +4503,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 +4513,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 +4523,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 +4533,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 +4543,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 +4553,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 +4563,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 +4573,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 +4583,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 +4593,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 +4603,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 +4613,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 +4623,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 +4633,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 +4643,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 +4653,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 +4663,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 +4682,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" { From 0943a72d70931cfd463f4b25490abba806259fed Mon Sep 17 00:00:00 2001 From: matthiascy Date: Wed, 8 Feb 2023 17:25:53 +0100 Subject: [PATCH 03/65] thin RTCError wrapper & allow device creation to fail --- examples/triangle/src/main.rs | 2 +- src/device.rs | 40 ++++++++++++++++++++++------------- src/error.rs | 32 ++++++++++++++++++++++++++++ src/lib.rs | 1 + 4 files changed, 59 insertions(+), 16 deletions(-) create mode 100644 src/error.rs diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index 3e24f7889..7539d29c1 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -11,7 +11,7 @@ use std::sync::Arc; fn main() { let display = support::Display::new(512, 512, "triangle"); - let device = Device::new(); + let device = Device::new().unwrap(); // Make a triangle let mut triangle = TriangleMesh::unanimated(device.clone(), 1, 3); diff --git a/src/device.rs b/src/device.rs index a1f91431a..4c1545840 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,8 +1,9 @@ use std::ffi::CString; -use std::fmt::{Display, Error, Formatter}; +use std::fmt::{self, Display, Formatter}; use std::ptr; use std::sync::Arc; +use crate::error::Error; use crate::sys::*; pub struct Device { @@ -10,27 +11,36 @@ pub struct Device { } impl Device { - pub fn new() -> Arc { + pub fn new() -> Result, Error> { enable_ftz_and_daz(); - Arc::new(Device { - handle: unsafe { rtcNewDevice(ptr::null()) }, - }) + let handle = unsafe { rtcNewDevice(ptr::null()) }; + if handle.is_null() { + Err(unsafe { rtcGetDeviceError(ptr::null_mut()) }.into()) + } else { + Ok(Arc::new(Device { handle })) + } } - pub fn debug() -> Arc { + pub fn debug() -> Result, Error> { let cfg = CString::new("verbose=4").unwrap(); enable_ftz_and_daz(); - Arc::new(Device { - handle: unsafe { rtcNewDevice(cfg.as_ptr()) }, - }) + let handle = unsafe { rtcNewDevice(cfg.as_ptr()) }; + if handle.is_null() { + Err(unsafe { rtcGetDeviceError(ptr::null_mut()) }.into()) + } else { + Ok(Arc::new(Device { handle })) + } } - pub fn with_config(config: Config) -> Arc { + pub fn with_config(config: Config) -> Result, Error> { enable_ftz_and_daz(); let cfg = config.to_c_string(); - Arc::new(Device { - handle: unsafe { rtcNewDevice(cfg.as_ptr()) }, - }) + let handle = unsafe { rtcNewDevice(cfg.as_ptr()) }; + if handle.is_null() { + Err(unsafe { rtcGetDeviceError(ptr::null_mut()) }.into()) + } else { + Ok(Arc::new(Device { handle })) + } } /// Register a callback function to be called when an error occurs. @@ -192,7 +202,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"), @@ -217,7 +227,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"), diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 000000000..ec0dbb9d6 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,32 @@ +use std::fmt::Display; + +use crate::sys; + +/// Thin wrapper around Embree's `RTCError`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Error(sys::RTCError); + +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.0 { + sys::RTCError::NONE => write!(f, "No error"), + sys::RTCError::UNKNOWN => write!(f, "Unknown error"), + sys::RTCError::INVALID_ARGUMENT => write!(f, "Invalid argument"), + sys::RTCError::INVALID_OPERATION => { + write!(f, "Invalid operation.") + } + sys::RTCError::OUT_OF_MEMORY => write!(f, "Out of memory"), + sys::RTCError::UNSUPPORTED_CPU => write!(f, "Unsupported CPU"), + sys::RTCError::CANCELLED => write!( + f, + "Cancelled by a memory monitor callback or progress monitor callback function" + ), + } + } +} + +impl From for Error { + fn from(err: sys::RTCError) -> Self { + Self(err) + } +} diff --git a/src/lib.rs b/src/lib.rs index c5f866997..ae42e358b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,7 @@ mod callback; pub mod catmull_rom_curve; pub mod curve; pub mod device; +pub mod error; pub mod geometry; pub mod hermite_curve; pub mod instance; From 4198c6c9aa8209e9e8ab65c0a852f587ab07bee7 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 9 Feb 2023 11:13:26 +0100 Subject: [PATCH 04/65] fix doc tests --- src/device.rs | 4 ++-- src/scene.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/device.rs b/src/device.rs index 4c1545840..1dc2cdd1e 100644 --- a/src/device.rs +++ b/src/device.rs @@ -63,7 +63,7 @@ impl Device { /// /// ```no_run /// use embree::Device; - /// let device = Device::new(); + /// let device = Device::new().unwrap(); /// device.set_error_function(|error, msg| { /// println!("Error: {:?} {}", error, msg); /// }); @@ -121,7 +121,7 @@ impl Device { /// # 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); diff --git a/src/scene.rs b/src/scene.rs index 84847fc7f..f00805bde 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -78,7 +78,7 @@ impl Scene { /// changing other flags the following way: /// ```no_run /// use embree::{Device, Scene, SceneFlags}; - /// let device = Device::new(); + /// let device = Device::new().unwrap(); /// let scene = Scene::new(device.clone()); /// let flags = scene.flags(); /// scene.set_flags(flags | SceneFlags::ROBUST); From 65c8397870b01d7647d1b2ba91e9dfa6ade54ee8 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 9 Feb 2023 11:20:09 +0100 Subject: [PATCH 05/65] format sys.rs --- src/sys.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/sys.rs b/src/sys.rs index 7871b488f..cb3682191 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -239,16 +239,13 @@ fn bindgen_test_layout_RTCLinearBounds() { ); } impl RTCIntersectContextFlags { - pub const NONE: RTCIntersectContextFlags = - RTCIntersectContextFlags(0); + pub const NONE: RTCIntersectContextFlags = RTCIntersectContextFlags(0); } impl RTCIntersectContextFlags { - pub const INCOHERENT: RTCIntersectContextFlags = - RTCIntersectContextFlags(0); + pub const INCOHERENT: RTCIntersectContextFlags = RTCIntersectContextFlags(0); } impl RTCIntersectContextFlags { - pub const COHERENT: RTCIntersectContextFlags = - RTCIntersectContextFlags(1); + pub const COHERENT: RTCIntersectContextFlags = RTCIntersectContextFlags(1); } impl ::std::ops::BitOr for RTCIntersectContextFlags { type Output = Self; From 2b7b18e2725006bab92d41afacbced2280dfed27 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 9 Feb 2023 11:28:53 +0100 Subject: [PATCH 06/65] remove RTCError wrapper --- src/error.rs | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/error.rs b/src/error.rs index ec0dbb9d6..16045d2d2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,32 +1,22 @@ use std::fmt::Display; -use crate::sys; +use crate::sys::RTCEqrror; -/// Thin wrapper around Embree's `RTCError`. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct Error(sys::RTCError); - -impl Display for Error { +impl Display for RTCError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self.0 { - sys::RTCError::NONE => write!(f, "No error"), - sys::RTCError::UNKNOWN => write!(f, "Unknown error"), - sys::RTCError::INVALID_ARGUMENT => write!(f, "Invalid argument"), - sys::RTCError::INVALID_OPERATION => { + match self { + RTCError::NONE => write!(f, "No error"), + RTCError::UNKNOWN => write!(f, "Unknown error"), + RTCError::INVALID_ARGUMENT => write!(f, "Invalid argument"), + RTCError::INVALID_OPERATION => { write!(f, "Invalid operation.") } - sys::RTCError::OUT_OF_MEMORY => write!(f, "Out of memory"), - sys::RTCError::UNSUPPORTED_CPU => write!(f, "Unsupported CPU"), - sys::RTCError::CANCELLED => write!( + RTCError::OUT_OF_MEMORY => write!(f, "Out of memory"), + RTCError::UNSUPPORTED_CPU => write!(f, "Unsupported CPU"), + RTCError::CANCELLED => write!( f, "Cancelled by a memory monitor callback or progress monitor callback function" ), } } } - -impl From for Error { - fn from(err: sys::RTCError) -> Self { - Self(err) - } -} From b7f73fd0ddd2fae6c6e04b331faa012e92d13ecf Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 9 Feb 2023 11:31:27 +0100 Subject: [PATCH 07/65] fix typos --- src/device.rs | 5 ++--- src/error.rs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/device.rs b/src/device.rs index 1dc2cdd1e..f7bcea8a3 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,11 +1,10 @@ +use crate::sys::*; +use crate::Error; use std::ffi::CString; use std::fmt::{self, Display, Formatter}; use std::ptr; use std::sync::Arc; -use crate::error::Error; -use crate::sys::*; - pub struct Device { pub(crate) handle: RTCDevice, } diff --git a/src/error.rs b/src/error.rs index 16045d2d2..2874a05b9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use crate::sys::RTCEqrror; +use crate::sys::RTCError; impl Display for RTCError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { From a545367a956a445f15fc73f24cae6c50694429e0 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 9 Feb 2023 13:46:44 +0100 Subject: [PATCH 08/65] resolve Twinklebear/embree-rs#23 --- src/buffer.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/buffer.rs b/src/buffer.rs index 05d953c6b..70031f669 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -30,6 +30,7 @@ impl BufferAttachment { // 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. +/// Handle to a buffer managed by Embree. pub struct Buffer { device: Arc, pub(crate) handle: RTCBuffer, @@ -65,7 +66,7 @@ impl Buffer { bytes = if bytes % 16 == 0 { bytes } else { - bytes + bytes / 16 + (bytes + 15) & !15 }; let h = unsafe { rtcNewBuffer(device.handle, bytes) }; Buffer { From bf678b0a4b81d4d73500bab23e3ecc25f589b23d Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 9 Feb 2023 14:35:00 +0100 Subject: [PATCH 09/65] remove Arc from Scene and Device creation --- examples/triangle/src/main.rs | 9 +++------ src/bezier_curve.rs | 10 +++++----- src/bspline_curve.rs | 10 +++++----- src/buffer.rs | 27 +++++++++++++-------------- src/catmull_rom_curve.rs | 10 +++++----- src/device.rs | 23 ++++++++++++++++------- src/hermite_curve.rs | 10 +++++----- src/linear_curve.rs | 10 +++++----- src/quad_mesh.rs | 4 ++-- src/scene.rs | 31 ++++++++++++++++++++++++------- src/triangle_mesh.rs | 4 ++-- 11 files changed, 85 insertions(+), 63 deletions(-) diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index 7539d29c1..aff4730ad 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -31,12 +31,9 @@ fn main() { tri_mut.commit(); } - let mut scene = Scene::new(device.clone()); - { - let scene_mut = Arc::get_mut(&mut scene).unwrap(); - scene_mut.attach_geometry(triangle); - scene_mut.commit(); - } + let mut scene = Scene::new(device.clone()).unwrap(); + scene.attach_geometry(triangle); + scene.commit(); support::display::run(display, move |image, _, _| { let mut intersection_ctx = IntersectContext::coherent(); diff --git a/src/bezier_curve.rs b/src/bezier_curve.rs index b594c51d4..985432ba6 100644 --- a/src/bezier_curve.rs +++ b/src/bezier_curve.rs @@ -7,7 +7,7 @@ use crate::sys::*; use crate::{BufferType, CurveType, Format, GeometryType}; pub struct BezierCurve { - device: Arc, + device: Device, pub(crate) handle: RTCGeometry, pub vertex_buffer: Buffer<[f32; 4]>, pub index_buffer: Buffer, @@ -16,7 +16,7 @@ pub struct BezierCurve { impl BezierCurve { pub fn flat( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, use_normals: bool, @@ -30,7 +30,7 @@ impl BezierCurve { ) } pub fn round( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, use_normals: bool, @@ -44,7 +44,7 @@ impl BezierCurve { ) } pub fn normal_oriented( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, ) -> Arc { @@ -58,7 +58,7 @@ impl BezierCurve { } fn unanimated( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, curve_type: CurveType, diff --git a/src/bspline_curve.rs b/src/bspline_curve.rs index ce1bfc6ce..aae196cd0 100644 --- a/src/bspline_curve.rs +++ b/src/bspline_curve.rs @@ -7,7 +7,7 @@ use crate::sys::*; use crate::{BufferType, CurveType, Format, GeometryType}; pub struct BsplineCurve { - device: Arc, + device: Device, pub(crate) handle: RTCGeometry, pub vertex_buffer: Buffer<[f32; 4]>, pub index_buffer: Buffer, @@ -16,7 +16,7 @@ pub struct BsplineCurve { impl BsplineCurve { pub fn flat( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, use_normals: bool, @@ -30,7 +30,7 @@ impl BsplineCurve { ) } pub fn round( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, use_normals: bool, @@ -44,7 +44,7 @@ impl BsplineCurve { ) } pub fn normal_oriented( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, ) -> Arc { @@ -58,7 +58,7 @@ impl BsplineCurve { } fn unanimated( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, curve_type: CurveType, diff --git a/src/buffer.rs b/src/buffer.rs index 70031f669..5ed718d94 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,6 +1,5 @@ use std::marker::PhantomData; use std::ops::{Index, IndexMut}; -use std::sync::Arc; use std::{mem, ptr}; use crate::device::Device; @@ -8,14 +7,14 @@ use crate::sys::*; use crate::BufferType; #[derive(Copy, Clone)] -struct BufferAttachment { +pub(crate) struct BufferAttachment { geom: RTCGeometry, buf_type: BufferType, slot: u32, } impl BufferAttachment { - fn none() -> BufferAttachment { + pub(crate) fn none() -> BufferAttachment { BufferAttachment { geom: ptr::null_mut(), buf_type: BufferType::VERTEX, @@ -32,35 +31,35 @@ impl BufferAttachment { // larger buffers. /// Handle to a buffer managed by Embree. pub struct Buffer { - device: Arc, + 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) bytes: usize, + pub(crate) attachment: BufferAttachment, + pub(crate) marker: PhantomData, } impl Buffer { /// Allocate a buffer with some raw capacity in bytes - pub fn raw(device: Arc, bytes: usize) -> Buffer { + pub fn raw(device: Device, bytes: usize) -> Buffer { // Pad to a multiple of 16 bytes let bytes = if bytes % 16 == 0 { bytes } else { - bytes + bytes / 16 + (bytes + 15) & !15 }; let h = unsafe { rtcNewBuffer(device.handle, bytes) }; Buffer { - device: device, + device: device.clone(), handle: h, - bytes: bytes, + bytes, attachment: BufferAttachment::none(), marker: PhantomData, } } - pub fn new(device: Arc, len: usize) -> Buffer { + pub fn new(device: Device, len: usize) -> Buffer { let mut bytes = len * mem::size_of::(); // Pad to a multiple of 16 bytes bytes = if bytes % 16 == 0 { @@ -70,9 +69,9 @@ impl Buffer { }; let h = unsafe { rtcNewBuffer(device.handle, bytes) }; Buffer { - device: device, + device: device.clone(), handle: h, - bytes: bytes, + bytes, attachment: BufferAttachment::none(), marker: PhantomData, } diff --git a/src/catmull_rom_curve.rs b/src/catmull_rom_curve.rs index ff1cfd510..881913678 100644 --- a/src/catmull_rom_curve.rs +++ b/src/catmull_rom_curve.rs @@ -7,7 +7,7 @@ use crate::sys::*; use crate::{BufferType, CurveType, Format, GeometryType}; pub struct CatmullRomCurve { - device: Arc, + device: Device, pub(crate) handle: RTCGeometry, pub vertex_buffer: Buffer<[f32; 4]>, pub index_buffer: Buffer, @@ -16,7 +16,7 @@ pub struct CatmullRomCurve { impl CatmullRomCurve { pub fn flat( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, use_normals: bool, @@ -30,7 +30,7 @@ impl CatmullRomCurve { ) } pub fn round( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, use_normals: bool, @@ -44,7 +44,7 @@ impl CatmullRomCurve { ) } pub fn normal_oriented( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, ) -> Arc { @@ -58,7 +58,7 @@ impl CatmullRomCurve { } fn unanimated( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, curve_type: CurveType, diff --git a/src/device.rs b/src/device.rs index f7bcea8a3..6654ca9c8 100644 --- a/src/device.rs +++ b/src/device.rs @@ -3,42 +3,51 @@ use crate::Error; use std::ffi::CString; use std::fmt::{self, Display, Formatter}; use std::ptr; -use std::sync::Arc; +/// Handle to an Embree device. pub struct Device { pub(crate) handle: RTCDevice, } +impl Clone for Device { + fn clone(&self) -> Self { + unsafe { rtcRetainDevice(self.handle) } + Self { + handle: self.handle, + } + } +} + impl Device { - pub fn new() -> Result, Error> { + pub fn new() -> Result { enable_ftz_and_daz(); let handle = unsafe { rtcNewDevice(ptr::null()) }; if handle.is_null() { Err(unsafe { rtcGetDeviceError(ptr::null_mut()) }.into()) } else { - Ok(Arc::new(Device { handle })) + Ok(Device { handle }) } } - pub fn debug() -> Result, Error> { + pub fn debug() -> Result { let cfg = CString::new("verbose=4").unwrap(); enable_ftz_and_daz(); let handle = unsafe { rtcNewDevice(cfg.as_ptr()) }; if handle.is_null() { Err(unsafe { rtcGetDeviceError(ptr::null_mut()) }.into()) } else { - Ok(Arc::new(Device { handle })) + Ok(Device { handle }) } } - pub fn with_config(config: Config) -> Result, Error> { + pub fn with_config(config: Config) -> Result { enable_ftz_and_daz(); let cfg = config.to_c_string(); let handle = unsafe { rtcNewDevice(cfg.as_ptr()) }; if handle.is_null() { Err(unsafe { rtcGetDeviceError(ptr::null_mut()) }.into()) } else { - Ok(Arc::new(Device { handle })) + Ok(Device { handle }) } } diff --git a/src/hermite_curve.rs b/src/hermite_curve.rs index 86207beb1..ba4366977 100644 --- a/src/hermite_curve.rs +++ b/src/hermite_curve.rs @@ -7,7 +7,7 @@ use crate::sys::*; use crate::{BufferType, CurveType, Format, GeometryType}; pub struct HermiteCurve { - device: Arc, + device: Device, pub(crate) handle: RTCGeometry, pub vertex_buffer: Buffer<[f32; 4]>, pub index_buffer: Buffer, @@ -18,7 +18,7 @@ pub struct HermiteCurve { impl HermiteCurve { pub fn flat( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, use_normals: bool, @@ -32,7 +32,7 @@ impl HermiteCurve { ) } pub fn round( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, use_normals: bool, @@ -46,7 +46,7 @@ impl HermiteCurve { ) } pub fn normal_oriented( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, ) -> Arc { @@ -60,7 +60,7 @@ impl HermiteCurve { } fn unanimated( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, curve_type: CurveType, diff --git a/src/linear_curve.rs b/src/linear_curve.rs index c50c1ab55..c51e44771 100644 --- a/src/linear_curve.rs +++ b/src/linear_curve.rs @@ -7,7 +7,7 @@ use crate::sys::*; use crate::{BufferType, CurveType, Format, GeometryType}; pub struct LinearCurve { - device: Arc, + device: Device, pub(crate) handle: RTCGeometry, pub vertex_buffer: Buffer<[f32; 4]>, pub index_buffer: Buffer, @@ -17,7 +17,7 @@ pub struct LinearCurve { impl LinearCurve { pub fn flat( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, use_normals: bool, @@ -31,7 +31,7 @@ impl LinearCurve { ) } pub fn round( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, use_normals: bool, @@ -45,7 +45,7 @@ impl LinearCurve { ) } pub fn cone( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, use_normals: bool, @@ -59,7 +59,7 @@ impl LinearCurve { ) } fn unanimated( - device: Arc, + device: Device, num_segments: usize, num_verts: usize, curve_type: CurveType, diff --git a/src/quad_mesh.rs b/src/quad_mesh.rs index 8fb3854a4..c27f747ab 100644 --- a/src/quad_mesh.rs +++ b/src/quad_mesh.rs @@ -7,14 +7,14 @@ use crate::sys::*; use crate::{BufferType, Format, GeometryType}; pub struct QuadMesh { - device: Arc, + device: Device, 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 { + pub fn unanimated(device: Device, 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); diff --git a/src/scene.rs b/src/scene.rs index f00805bde..2232f9e69 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,3 +1,4 @@ +use crate::Error; use std::collections::HashMap; use std::mem; use std::sync::Arc; @@ -17,17 +18,33 @@ use crate::sys::*; /// return a `CommittedScene` which can be used for ray queries. pub struct Scene { pub(crate) handle: RTCScene, - pub(crate) device: Arc, + pub(crate) device: Device, geometry: HashMap>, } +impl Clone for Scene { + fn clone(&self) -> Self { + unsafe { rtcRetainScene(self.handle) } + Self { + handle: self.handle, + device: self.device.clone(), + geometry: self.geometry.clone(), + } + } +} + impl Scene { - pub fn new(device: Arc) -> Arc { - Arc::new(Scene { - handle: unsafe { rtcNewScene(device.handle) }, - device, - geometry: HashMap::new(), - }) + pub fn new(device: Device) -> Result { + let handle = unsafe { rtcNewScene(device.handle) }; + if handle.is_null() { + Err(device.error_code()) + } else { + Ok(Scene { + handle, + device, + geometry: HashMap::new(), + }) + } } /// Attach a new geometry to the scene. Returns the scene local ID which diff --git a/src/triangle_mesh.rs b/src/triangle_mesh.rs index 68316c1bd..c67be65ef 100644 --- a/src/triangle_mesh.rs +++ b/src/triangle_mesh.rs @@ -7,14 +7,14 @@ use crate::sys::*; use crate::{BufferType, Format, GeometryType}; pub struct TriangleMesh { - device: Arc, + device: Device, 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 { + pub fn unanimated(device: Device, 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); From 0288aad7b858d6c707df93c90f6f6c66a4419e3a Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 9 Feb 2023 14:42:07 +0100 Subject: [PATCH 10/65] update doc test --- src/scene.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scene.rs b/src/scene.rs index 2232f9e69..4a405e5b1 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -96,7 +96,7 @@ impl Scene { /// ```no_run /// use embree::{Device, Scene, SceneFlags}; /// let device = Device::new().unwrap(); - /// let scene = Scene::new(device.clone()); + /// let scene = Scene::new(device.clone()).unwrap(); /// let flags = scene.flags(); /// scene.set_flags(flags | SceneFlags::ROBUST); /// ``` From 54b4de2083da37486aa381a329409686fd68a5c0 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 9 Feb 2023 15:16:55 +0100 Subject: [PATCH 11/65] refactor Scene creation as device method To underline that a scene is bound to a specific device, the scene creation function is refactored as a member function of device. --- examples/triangle/src/main.rs | 2 +- src/device.rs | 14 ++++++++++++++ src/scene.rs | 11 ++++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index aff4730ad..79315ca51 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -31,7 +31,7 @@ fn main() { tri_mut.commit(); } - let mut scene = Scene::new(device.clone()).unwrap(); + let mut scene = device.create_scene().unwrap(); scene.attach_geometry(triangle); scene.commit(); diff --git a/src/device.rs b/src/device.rs index 6654ca9c8..d96a13e66 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,5 +1,6 @@ use crate::sys::*; use crate::Error; +use crate::Scene; use std::ffi::CString; use std::fmt::{self, Display, Formatter}; use std::ptr; @@ -187,6 +188,19 @@ impl Device { pub fn error_code(&self) -> RTCError { unsafe { rtcGetDeviceError(self.handle) } } + + /// Creates a new scene bound to the device. + pub fn create_scene(&self) -> Result { + 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 [`SceneConfig`] for possible values. + pub fn create_scene_with_flags(&self, flags: RTCSceneFlags) -> Result { + Scene::new_with_flags(self.clone(), flags) + } } impl Drop for Device { diff --git a/src/scene.rs b/src/scene.rs index 4a405e5b1..f0f50d714 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,4 +1,5 @@ use crate::Error; +use crate::SceneFlags; use std::collections::HashMap; use std::mem; use std::sync::Arc; @@ -34,7 +35,8 @@ impl Clone for Scene { } impl Scene { - pub fn new(device: Device) -> Result { + /// 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.error_code()) @@ -47,6 +49,13 @@ impl Scene { } } + /// 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) + } + /// 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 From 56b8af96e94cd3e887bc83ca725dd77f784b6ed1 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 9 Feb 2023 15:24:27 +0100 Subject: [PATCH 12/65] update doc test --- src/scene.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scene.rs b/src/scene.rs index f0f50d714..4b764c029 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -105,7 +105,7 @@ impl Scene { /// ```no_run /// use embree::{Device, Scene, SceneFlags}; /// let device = Device::new().unwrap(); - /// let scene = Scene::new(device.clone()).unwrap(); + /// let scene = device.create_scene().unwrap(); /// let flags = scene.flags(); /// scene.set_flags(flags | SceneFlags::ROBUST); /// ``` From baaa561a52068abccce0fa52827e8f54dea446e2 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 9 Feb 2023 18:10:22 +0100 Subject: [PATCH 13/65] start refactoring buffer --- Cargo.toml | 2 + src/buffer.rs | 216 ++++++++++++++++++++++++------------------------ src/device.rs | 14 ++++ src/geometry.rs | 43 ++++++++-- 4 files changed, 160 insertions(+), 115 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d3d4874c9..09caf9f64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,5 @@ exclude = [ "examples/*" ] +[dependencies] +static_assertions = "1.1.0" diff --git a/src/buffer.rs b/src/buffer.rs index 5ed718d94..1a9816b0a 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,99 +1,64 @@ +use crate::Error; use std::marker::PhantomData; -use std::ops::{Index, IndexMut}; +use std::ops::{Bound, Index, IndexMut, RangeBounds}; use std::{mem, ptr}; use crate::device::Device; use crate::sys::*; use crate::BufferType; -#[derive(Copy, Clone)] -pub(crate) struct BufferAttachment { - geom: RTCGeometry, - buf_type: BufferType, - slot: u32, -} - -impl BufferAttachment { - pub(crate) 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() - } -} - -// 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. /// Handle to a buffer managed by Embree. -pub struct Buffer { +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. - pub(crate) bytes: usize, - pub(crate) attachment: BufferAttachment, - pub(crate) marker: PhantomData, + pub(crate) size: usize, } -impl Buffer { - /// Allocate a buffer with some raw capacity in bytes - pub fn raw(device: Device, bytes: usize) -> Buffer { +impl Buffer { + /// Creates a new data buffer of the given size. + pub(crate) fn new(device: Device, size: usize) -> Result { // Pad to a multiple of 16 bytes - let bytes = if bytes % 16 == 0 { - bytes + let size = if size % 16 == 0 { + size } else { - (bytes + 15) & !15 + (size + 15) & !15 }; - let h = unsafe { rtcNewBuffer(device.handle, bytes) }; - Buffer { - device: device.clone(), - handle: h, - bytes, - attachment: BufferAttachment::none(), - marker: PhantomData, - } - } - pub fn new(device: Device, len: usize) -> Buffer { - let mut bytes = len * mem::size_of::(); - // Pad to a multiple of 16 bytes - bytes = if bytes % 16 == 0 { - bytes + let handle = unsafe { rtcNewBuffer(device.handle, size) }; + if handle.is_null() { + Err(device.get_error()) } else { - (bytes + 15) & !15 - }; - let h = unsafe { rtcNewBuffer(device.handle, bytes) }; - Buffer { - device: device.clone(), - handle: h, - bytes, - attachment: BufferAttachment::none(), - marker: PhantomData, + Ok(Buffer { + device, + handle, + size, + }) } } - 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, + + /// Slices into the buffer for the given range. + /// + /// Choosing a range with no end will slice to the end of the buffer: + /// + /// ``` + /// buffer.slice(16..) + /// ``` + /// + /// Choosing a totally unbounded range will use the entire buffer: + /// + /// ``` + /// buffer.slice(..) + /// ``` + pub fn slice>(&self, bounds: S) -> BufferSlice<'_> { + let (offset, size) = range_bounds_to_offset_and_size(bounds); + BufferSlice { + buffer: self, + offset, + size, } } - 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; - } } -impl Drop for Buffer { +impl Drop for Buffer { fn drop(&mut self) { unsafe { rtcReleaseBuffer(self.handle); @@ -101,51 +66,86 @@ impl Drop for Buffer { } } -pub struct MappedBuffer<'a, T: 'a> { - buffer: PhantomData<&'a mut Buffer>, - attachment: BufferAttachment, - slice: *mut T, - len: usize, +static_assertions::assert_impl_all!(Buffer: Send, Sync); + +/// Slice into a [`Buffer`]. +/// +/// Created with [`Buffer::slice`]. +#[derive(Debug, Clone, Copy)] +pub struct BufferSlice<'a> { + buffer: &'a Buffer, + offset: usize, + size: Option, } -impl<'a, T: 'a> MappedBuffer<'a, T> { - pub fn len(&self) -> usize { - self.len +static_assertions::assert_impl_all!(BufferSlice: Send, Sync); + +impl<'a> BufferSlice<'a> { + /// Returns a immutable slice into the buffer. + pub fn view(&self) -> BufferView<'a> { + BufferView { + buffer: self.buffer, + offset: self.offset, + size: self.size.unwrap_or(self.buffer.size - self.offset), + _marker: PhantomData, + } } -} -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, - ); - } + /// Returns a mutable slice into the buffer. + pub fn view_mut(&mut self) -> BufferViewMut<'a> { + BufferViewMut { + buffer: self.buffer, + offset: self.offset, + size: self.size.unwrap_or(self.buffer.size - self.offset), + _marker: PhantomData, } } } -impl<'a, T: 'a> Index for MappedBuffer<'a, T> { - type Output = T; +/// A read-only view of a buffer. +pub struct BufferView<'a, T: 'a> { + slice: BufferSlice<'a>, + data: MappedBuffer<'a, T>, +} - 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) } +/// A mutable view of a buffer. +pub struct BufferViewMut<'a, T: 'a> { + slice: BufferSlice<'a>, + data: MappedBuffer<'a, T>, +} + +#[derive(Debug)] +pub struct MappedBuffer<'a, T: 'a> { + ptr: *mut T, + len: usize, + _marker: PhantomData<&'a mut T>, +} + +impl AsRef<[T]> for BufferView<'_, T> { + fn as_ref(&self) -> &[T] { + unsafe { std::slice::from_raw_parts(self.data.ptr, self.len) } } } -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"); - } - unsafe { &mut *self.slice.offset(index as isize) } +impl AsMut<[T]> for BufferViewMut<'_, T> { + fn as_mut(&mut self) -> &mut [T] { + unsafe { std::slice::from_raw_parts_mut(self.data.ptr, self.len) } } } + +fn range_bounds_to_offset_and_size( + bounds: RangeBounds, +) -> (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/device.rs b/src/device.rs index d96a13e66..56f30a1e9 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,5 +1,8 @@ +use crate::geometry::Geometry; use crate::sys::*; +use crate::Buffer; use crate::Error; +use crate::GeometryType; use crate::Scene; use std::ffi::CString; use std::fmt::{self, Display, Formatter}; @@ -201,6 +204,17 @@ impl Device { pub fn create_scene_with_flags(&self, flags: RTCSceneFlags) -> Result { Scene::new_with_flags(self.clone(), flags) } + + /// 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.clone(), size) + } + + /// Creates a [`Geometry`] object bound to the device. + pub fn create_geometry(&self, kind: GeometryType) -> Result { + Geometry::new(self.clone(), kind) + } } impl Drop for Device { diff --git a/src/geometry.rs b/src/geometry.rs index c2f77dcde..35d37b8c1 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,11 +1,40 @@ -use crate::sys::*; +use std::collections::HashMap; -/// Geometry trait implemented by all Embree Geometry types -pub trait Geometry { - fn handle(&self) -> RTCGeometry; - fn commit(&mut self) { +use crate::{sys::*, Device, Error}; +use crate::{Buffer, BufferType, GeometryType}; + +/// Handle to an Embree geometry object. +pub struct Geometry { + pub(crate) device: Device, + pub(crate) handle: RTCGeometry, + attachments: HashMap>, +} + +impl Drop for Geometry { + fn drop(&mut self) { + unsafe { + rtcReleaseGeometry(self.handle); + } + } +} + +impl Geometry { + pub(crate) fn new(device: Device, kind: GeometryType) -> Result { + let handle = unsafe { rtcNewGeometry(device.handle, kind) }; + if handle.is_null() { + Err(device.get_error()) + } else { + Ok(Geometry { + device, + handle, + attachments: HashMap::new(), + }) + } + } + + pub fn commit(&mut self) { unsafe { - rtcCommitGeometry(self.handle()); + rtcCommitGeometry(self.handle); } } @@ -35,7 +64,7 @@ pub trait Geometry { /// interpolated. fn set_subdivision_mode(&self, topology_id: u32, mode: RTCSubdivisionMode) { unsafe { - rtcSetGeometrySubdivisionMode(self.handle(), topology_id, mode); + rtcSetGeometrySubdivisionMode(self.handle, topology_id, mode); } } From 10be85853cd2d52f94a0c95689123167a90bfb32 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 9 Feb 2023 18:12:54 +0100 Subject: [PATCH 14/65] tmp commit --- src/bezier_curve.rs | 4 ++-- src/bspline_curve.rs | 4 ++-- src/catmull_rom_curve.rs | 4 ++-- src/hermite_curve.rs | 4 ++-- src/instance.rs | 4 ++-- src/lib.rs | 4 ++-- src/linear_curve.rs | 4 ++-- src/quad_mesh.rs | 4 ++-- src/scene.rs | 6 +++--- src/triangle_mesh.rs | 4 ++-- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/bezier_curve.rs b/src/bezier_curve.rs index 985432ba6..5abe1b166 100644 --- a/src/bezier_curve.rs +++ b/src/bezier_curve.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::buffer::Buffer; use crate::device::Device; -use crate::geometry::Geometry; +use crate::geometry::GeometryTrait; use crate::sys::*; use crate::{BufferType, CurveType, Format, GeometryType}; @@ -131,7 +131,7 @@ impl BezierCurve { } } -impl Geometry for BezierCurve { +impl GeometryTrait for BezierCurve { fn handle(&self) -> RTCGeometry { self.handle } diff --git a/src/bspline_curve.rs b/src/bspline_curve.rs index aae196cd0..0c5127c42 100644 --- a/src/bspline_curve.rs +++ b/src/bspline_curve.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::buffer::Buffer; use crate::device::Device; -use crate::geometry::Geometry; +use crate::geometry::GeometryTrait; use crate::sys::*; use crate::{BufferType, CurveType, Format, GeometryType}; @@ -131,7 +131,7 @@ impl BsplineCurve { } } -impl Geometry for BsplineCurve { +impl GeometryTrait for BsplineCurve { fn handle(&self) -> RTCGeometry { self.handle } diff --git a/src/catmull_rom_curve.rs b/src/catmull_rom_curve.rs index 881913678..9d136a095 100644 --- a/src/catmull_rom_curve.rs +++ b/src/catmull_rom_curve.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::buffer::Buffer; use crate::device::Device; -use crate::geometry::Geometry; +use crate::geometry::GeometryTrait; use crate::sys::*; use crate::{BufferType, CurveType, Format, GeometryType}; @@ -134,7 +134,7 @@ impl CatmullRomCurve { } } -impl Geometry for CatmullRomCurve { +impl GeometryTrait for CatmullRomCurve { fn handle(&self) -> RTCGeometry { self.handle } diff --git a/src/hermite_curve.rs b/src/hermite_curve.rs index ba4366977..e335554e5 100644 --- a/src/hermite_curve.rs +++ b/src/hermite_curve.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::buffer::Buffer; use crate::device::Device; -use crate::geometry::Geometry; +use crate::geometry::GeometryTrait; use crate::sys::*; use crate::{BufferType, CurveType, Format, GeometryType}; @@ -163,7 +163,7 @@ impl HermiteCurve { } } -impl Geometry for HermiteCurve { +impl GeometryTrait for HermiteCurve { fn handle(&self) -> RTCGeometry { self.handle } diff --git a/src/instance.rs b/src/instance.rs index be90a4aa2..3d33e123c 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -1,7 +1,7 @@ use std::os::raw; use std::sync::Arc; -use crate::geometry::Geometry; +use crate::geometry::GeometryTrait; use crate::scene::Scene; use crate::sys::*; use crate::{Format, GeometryType}; @@ -37,7 +37,7 @@ impl Instance { } } -impl Geometry for Instance { +impl GeometryTrait for Instance { fn handle(&self) -> RTCGeometry { self.handle } diff --git a/src/lib.rs b/src/lib.rs index ae42e358b..66c9654fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,11 +40,11 @@ pub mod triangle_mesh; pub use bezier_curve::BezierCurve; pub use bspline_curve::BsplineCurve; -pub use buffer::{Buffer, MappedBuffer}; +pub use buffer::{Buffer, BufferView}; pub use catmull_rom_curve::CatmullRomCurve; pub use curve::CurveType; pub use device::{Config, Device, FrequencyLevel, Isa}; -pub use geometry::Geometry; +pub use geometry::GeometryTrait; pub use hermite_curve::HermiteCurve; pub use instance::Instance; pub use intersect_context::IntersectContext; diff --git a/src/linear_curve.rs b/src/linear_curve.rs index c51e44771..5e60d2b21 100644 --- a/src/linear_curve.rs +++ b/src/linear_curve.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::buffer::Buffer; use crate::device::Device; -use crate::geometry::Geometry; +use crate::geometry::GeometryTrait; use crate::sys::*; use crate::{BufferType, CurveType, Format, GeometryType}; @@ -145,7 +145,7 @@ impl LinearCurve { } } -impl Geometry for LinearCurve { +impl GeometryTrait for LinearCurve { fn handle(&self) -> RTCGeometry { self.handle } diff --git a/src/quad_mesh.rs b/src/quad_mesh.rs index c27f747ab..1e3291deb 100644 --- a/src/quad_mesh.rs +++ b/src/quad_mesh.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::buffer::Buffer; use crate::device::Device; -use crate::geometry::Geometry; +use crate::geometry::GeometryTrait; use crate::sys::*; use crate::{BufferType, Format, GeometryType}; @@ -52,7 +52,7 @@ impl QuadMesh { } } -impl Geometry for QuadMesh { +impl GeometryTrait for QuadMesh { fn handle(&self) -> RTCGeometry { self.handle } diff --git a/src/scene.rs b/src/scene.rs index 4b764c029..35a864158 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use crate::callback; use crate::device::Device; -use crate::geometry::Geometry; +use crate::geometry::GeometryTrait; use crate::intersect_context::IntersectContext; use crate::ray::{Ray, RayHit}; use crate::ray_packet::{Ray4, RayHit4}; @@ -20,7 +20,7 @@ use crate::sys::*; pub struct Scene { pub(crate) handle: RTCScene, pub(crate) device: Device, - geometry: HashMap>, + geometry: HashMap>, } impl Clone for Scene { @@ -61,7 +61,7 @@ impl Scene { /// 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 { + pub fn attach_geometry(&mut self, mesh: Arc) -> u32 { let id = unsafe { rtcAttachGeometry(self.handle, mesh.handle()) }; self.geometry.insert(id, mesh); id diff --git a/src/triangle_mesh.rs b/src/triangle_mesh.rs index c67be65ef..4e3a6e541 100644 --- a/src/triangle_mesh.rs +++ b/src/triangle_mesh.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::buffer::Buffer; use crate::device::Device; -use crate::geometry::Geometry; +use crate::geometry::GeometryTrait; use crate::sys::*; use crate::{BufferType, Format, GeometryType}; @@ -52,7 +52,7 @@ impl TriangleMesh { } } -impl Geometry for TriangleMesh { +impl GeometryTrait for TriangleMesh { fn handle(&self) -> RTCGeometry { self.handle } From 6b73de27671809490d8f38c65c3d12411451e983 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Fri, 10 Feb 2023 17:38:33 +0100 Subject: [PATCH 15/65] tmp commit --- src/buffer.rs | 195 ++++++++++++++++++++-------- src/device.rs | 3 +- src/geometry.rs | 58 +++++++-- src/{ => geometry}/bezier_curve.rs | 1 - src/{ => geometry}/bspline_curve.rs | 1 - src/{ => geometry}/curve.rs | 0 src/{ => geometry}/hermite_curve.rs | 0 src/{ => geometry}/linear_curve.rs | 0 src/{ => geometry}/quad_mesh.rs | 0 src/{ => geometry}/triangle_mesh.rs | 23 +++- src/lib.rs | 30 ++--- 11 files changed, 227 insertions(+), 84 deletions(-) rename src/{ => geometry}/bezier_curve.rs (99%) rename src/{ => geometry}/bspline_curve.rs (99%) rename src/{ => geometry}/curve.rs (100%) rename src/{ => geometry}/hermite_curve.rs (100%) rename src/{ => geometry}/linear_curve.rs (100%) rename src/{ => geometry}/quad_mesh.rs (100%) rename src/{ => geometry}/triangle_mesh.rs (82%) diff --git a/src/buffer.rs b/src/buffer.rs index 1a9816b0a..223e38ad8 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,22 +1,36 @@ use crate::Error; use std::marker::PhantomData; -use std::ops::{Bound, Index, IndexMut, RangeBounds}; -use std::{mem, ptr}; +use std::mem; +use std::num::NonZeroUsize; +use std::ops::{Bound, RangeBounds}; use crate::device::Device; use crate::sys::*; -use crate::BufferType; + +/// Non-zero integer type used to describe the size of a buffer. +pub type BufferSize = NonZeroUsize; /// Handle to a buffer managed by Embree. pub struct Buffer { pub(crate) device: Device, pub(crate) handle: RTCBuffer, - pub(crate) size: usize, + pub(crate) size: BufferSize, +} + +impl Clone for Buffer { + fn clone(&self) -> Self { + unsafe { rtcRetainBuffer(self.handle) }; + Buffer { + device: self.device.clone(), + handle: self.handle, + size: self.size, + } + } } impl Buffer { /// Creates a new data buffer of the given size. - pub(crate) fn new(device: Device, size: usize) -> Result { + pub(crate) fn new(device: Device, size: BufferSize) -> Result { // Pad to a multiple of 16 bytes let size = if size % 16 == 0 { size @@ -35,26 +49,55 @@ impl Buffer { } } + /// Returns the a slice of the buffer. + pub fn slice>(&self, bounds: S) -> BufferSlice { + let start = match bounds.start_bound() { + Bound::Included(&n) => n, + Bound::Excluded(&n) => n + 1, + Bound::Unbounded => 0, + }; + let end = match bounds.end_bound() { + Bound::Included(&n) => n + 1, + Bound::Excluded(&n) => n, + Bound::Unbounded => self.size.get(), + }; + BufferSlice { + buffer: self.clone(), + offset: start, + size: NonZeroUsize::new(end - start).unwrap(), + } + } + /// Slices into the buffer for the given range. /// - /// Choosing a range with no end will slice to the end of the buffer: - /// - /// ``` - /// buffer.slice(16..) - /// ``` + /// # Arguments /// - /// Choosing a totally unbounded range will use the entire buffer: - /// - /// ``` - /// buffer.slice(..) - /// ``` - pub fn slice>(&self, bounds: S) -> BufferSlice<'_> { + /// * `range` - 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); - BufferSlice { + let size = size.unwrap_or_else(|| self.size.get() - offset); + debug_assert!(offset + size <= self.size.get() && offset < self.size.get()); + let range = BufferSlice { buffer: self, offset, - size, - } + size: BufferSize::new(size).unwrap(), + }; + BufferView::new(self, range) + } + + /// Mutable slice into the buffer for the given range. + pub fn mapped_range_mut>(&mut self, bounds: S) -> BufferViewMut<'_> { + 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()); + let range = BufferSlice { + buffer: self, + offset, + size: BufferSize::new(size).unwrap(), + }; + BufferViewMut::new(self, range) } } @@ -68,71 +111,119 @@ impl Drop for Buffer { static_assertions::assert_impl_all!(Buffer: Send, Sync); +/// A read-only view into mapped buffer. +#[derive(Debug)] +pub struct BufferView<'a, T: 'a> { + range: BufferSlice, + slice: BufferMappedRange<'a, T>, + marker: PhantomData<&'a Buffer>, +} + +/// A write-only view into mapped buffer. +#[derive(Debug)] +pub struct BufferViewMut<'a, T: 'a> { + range: BufferSlice, + slice: BufferMappedRange<'a, T>, + marker: PhantomData<&'a mut Buffer>, +} + /// Slice into a [`Buffer`]. /// /// Created with [`Buffer::slice`]. -#[derive(Debug, Clone, Copy)] -pub struct BufferSlice<'a> { - buffer: &'a Buffer, - offset: usize, - size: Option, +#[derive(Debug)] +pub struct BufferSlice { + /// The buffer this slice is a part of. + pub(crate) buffer: Buffer, + /// The offset into the buffer in bytes. + pub(crate) offset: usize, + /// The size of the slice in bytes. + pub(crate) size: BufferSize, } static_assertions::assert_impl_all!(BufferSlice: Send, Sync); impl<'a> BufferSlice<'a> { - /// Returns a immutable slice into the buffer. - pub fn view(&self) -> BufferView<'a> { + fn view(&self) -> BufferView<'a, T> { + let slice = BufferMappedRange::new(&self.buffer, self.offset, self.size)?; BufferView { - buffer: self.buffer, - offset: self.offset, - size: self.size.unwrap_or(self.buffer.size - self.offset), - _marker: PhantomData, + range: *self, + slice, + marker: PhantomData, } } - /// Returns a mutable slice into the buffer. - pub fn view_mut(&mut self) -> BufferViewMut<'a> { + fn view_mut(&self) -> BufferViewMut<'a, T> { + let slice = BufferMappedRange::new(&self.buffer, self.offset, self.size)?; BufferViewMut { - buffer: self.buffer, - offset: self.offset, - size: self.size.unwrap_or(self.buffer.size - self.offset), - _marker: PhantomData, + range: *self, + slice, + marker: PhantomData, } } } -/// A read-only view of a buffer. -pub struct BufferView<'a, T: 'a> { - slice: BufferSlice<'a>, - data: MappedBuffer<'a, T>, -} - -/// A mutable view of a buffer. -pub struct BufferViewMut<'a, T: 'a> { - slice: BufferSlice<'a>, - data: MappedBuffer<'a, T>, -} - #[derive(Debug)] -pub struct MappedBuffer<'a, T: 'a> { +struct BufferMappedRange<'a, T: 'a> { ptr: *mut T, len: usize, - _marker: PhantomData<&'a mut T>, +} + +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 new( + 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.offset(offset as isize) + } as *mut T; + Ok(BufferMappedRange { + ptr, + len: size / mem::size_of::(), + }) + } + + 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 AsRef<[T]> for BufferView<'_, T> { fn as_ref(&self) -> &[T] { - unsafe { std::slice::from_raw_parts(self.data.ptr, self.len) } + self.slice.as_slice() } } impl AsMut<[T]> for BufferViewMut<'_, T> { fn as_mut(&mut self) -> &mut [T] { - unsafe { std::slice::from_raw_parts_mut(self.data.ptr, self.len) } + self.slice.as_mut_slice() } } +/// Converts a range bounds into an offset and size. fn range_bounds_to_offset_and_size( bounds: RangeBounds, ) -> (usize, Option) { diff --git a/src/device.rs b/src/device.rs index 56f30a1e9..fa9b3ce09 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,3 +1,4 @@ +use crate::buffer::BufferSize; use crate::geometry::Geometry; use crate::sys::*; use crate::Buffer; @@ -207,7 +208,7 @@ impl Device { /// Creates a new data buffer. /// The created buffer is always aligned to 16 bytes. - pub fn create_buffer(&self, size: usize) -> Result { + pub fn create_buffer(&self, size: BufferSize) -> Result { Buffer::new(self.clone(), size) } diff --git a/src/geometry.rs b/src/geometry.rs index 35d37b8c1..468a3e491 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,16 +1,24 @@ use std::collections::HashMap; +use crate::buffer::BufferSlice; use crate::{sys::*, Device, Error}; -use crate::{Buffer, BufferType, GeometryType}; +use crate::{BufferUsage, Format, GeometryType}; + +mod triangle_mesh; + +pub trait Geometry { + fn geometry_type(&self) -> GeometryType; +} /// Handle to an Embree geometry object. -pub struct Geometry { +pub struct GeometryData { pub(crate) device: Device, pub(crate) handle: RTCGeometry, - attachments: HashMap>, + pub(crate) kind: GeometryType, + pub(crate) bindings: HashMap>, } -impl Drop for Geometry { +impl Drop for GeometryData { fn drop(&mut self) { unsafe { rtcReleaseGeometry(self.handle); @@ -18,20 +26,54 @@ impl Drop for Geometry { } } -impl Geometry { - pub(crate) fn new(device: Device, kind: GeometryType) -> Result { +impl GeometryData { + pub(crate) fn new(device: Device, kind: GeometryType) -> Result { let handle = unsafe { rtcNewGeometry(device.handle, kind) }; if handle.is_null() { Err(device.get_error()) } else { - Ok(Geometry { + Ok(GeometryData { device, handle, - attachments: HashMap::new(), + kind, + bindings: HashMap::new(), }) } } + // pub fn bind_new_buffer( + // &mut self, + // usage: BufferUsage, + // format: Format, + // data: &[T], + // ) -> Result { + // let buffer = self.device.create_buffer(usage, format, data)?; + // self.bind_buffer(usage, buffer) + // } + + /// Binds a view of a buffer to a geometry. + pub fn bind_buffer( + &mut self, + buffer: BufferSlice, + slot: u32, + usage: BufferUsage, + format: Format, + ) -> Result<(), Error> { + self.bindings.entry(usage).or_insert(vec![]) + unsafe { + rtcSetGeometryBuffer( + self.handle, + usage, + slot, + format, + buffer, + buffer.offset, + std::mem::size_of::(), + buffer.size / std::mem::size_of::(), + ) + } + } + pub fn commit(&mut self) { unsafe { rtcCommitGeometry(self.handle); diff --git a/src/bezier_curve.rs b/src/geometry/bezier_curve.rs similarity index 99% rename from src/bezier_curve.rs rename to src/geometry/bezier_curve.rs index 5abe1b166..df7e4931d 100644 --- a/src/bezier_curve.rs +++ b/src/geometry/bezier_curve.rs @@ -2,7 +2,6 @@ use std::sync::Arc; use crate::buffer::Buffer; use crate::device::Device; -use crate::geometry::GeometryTrait; use crate::sys::*; use crate::{BufferType, CurveType, Format, GeometryType}; diff --git a/src/bspline_curve.rs b/src/geometry/bspline_curve.rs similarity index 99% rename from src/bspline_curve.rs rename to src/geometry/bspline_curve.rs index 0c5127c42..b048d97c2 100644 --- a/src/bspline_curve.rs +++ b/src/geometry/bspline_curve.rs @@ -2,7 +2,6 @@ use std::sync::Arc; use crate::buffer::Buffer; use crate::device::Device; -use crate::geometry::GeometryTrait; use crate::sys::*; use crate::{BufferType, CurveType, Format, GeometryType}; diff --git a/src/curve.rs b/src/geometry/curve.rs similarity index 100% rename from src/curve.rs rename to src/geometry/curve.rs diff --git a/src/hermite_curve.rs b/src/geometry/hermite_curve.rs similarity index 100% rename from src/hermite_curve.rs rename to src/geometry/hermite_curve.rs diff --git a/src/linear_curve.rs b/src/geometry/linear_curve.rs similarity index 100% rename from src/linear_curve.rs rename to src/geometry/linear_curve.rs diff --git a/src/quad_mesh.rs b/src/geometry/quad_mesh.rs similarity index 100% rename from src/quad_mesh.rs rename to src/geometry/quad_mesh.rs diff --git a/src/triangle_mesh.rs b/src/geometry/triangle_mesh.rs similarity index 82% rename from src/triangle_mesh.rs rename to src/geometry/triangle_mesh.rs index 4e3a6e541..e2058e721 100644 --- a/src/triangle_mesh.rs +++ b/src/geometry/triangle_mesh.rs @@ -1,16 +1,27 @@ +use std::ops::{Deref, DerefMut}; use std::sync::Arc; use crate::buffer::Buffer; use crate::device::Device; -use crate::geometry::GeometryTrait; use crate::sys::*; use crate::{BufferType, Format, GeometryType}; -pub struct TriangleMesh { - device: Device, - pub(crate) handle: RTCGeometry, - pub vertex_buffer: Buffer<[f32; 4]>, - pub index_buffer: Buffer<[u32; 3]>, +use super::GeometryData; + +pub struct TriangleMesh(GeometryData); + +impl Deref for TriangleMesh { + type Target = GeometryData; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for TriangleMesh { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } } impl TriangleMesh { diff --git a/src/lib.rs b/src/lib.rs index 66c9654fd..59a03453a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,20 +13,20 @@ use std::{alloc, mem}; -pub mod bezier_curve; -pub mod bspline_curve; +// pub mod bezier_curve; +// pub mod bspline_curve; pub mod buffer; mod callback; pub mod catmull_rom_curve; -pub mod curve; +// pub mod curve; pub mod device; pub mod error; pub mod geometry; -pub mod hermite_curve; +// pub mod hermite_curve; pub mod instance; pub mod intersect_context; -pub mod linear_curve; -pub mod quad_mesh; +// pub mod linear_curve; +// pub mod quad_mesh; pub mod ray; pub mod ray_packet; pub mod ray_stream; @@ -36,20 +36,20 @@ pub mod soa_ray; #[allow(non_camel_case_types)] #[allow(non_snake_case)] pub mod sys; -pub mod triangle_mesh; +// pub mod triangle_mesh; -pub use bezier_curve::BezierCurve; -pub use bspline_curve::BsplineCurve; +// pub use bezier_curve::BezierCurve; +// pub use bspline_curve::BsplineCurve; pub use buffer::{Buffer, BufferView}; pub use catmull_rom_curve::CatmullRomCurve; -pub use curve::CurveType; +// pub use curve::CurveType; pub use device::{Config, Device, FrequencyLevel, Isa}; -pub use geometry::GeometryTrait; -pub use hermite_curve::HermiteCurve; +// pub use geometry::GeometryTrait; +// 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 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}; @@ -62,7 +62,7 @@ pub use triangle_mesh::TriangleMesh; // Pull in some cleaned up enum and bitfield types directly, // with prettier aliases -pub use sys::RTCBufferType as BufferType; +pub use sys::RTCBufferType as BufferUsage; pub use sys::RTCBuildQuality as BuildQuality; pub use sys::RTCDeviceProperty as DeviceProperty; pub use sys::RTCError as Error; From 315db83120f3813d90f141dac6ed734e0e7e6d40 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Mon, 13 Feb 2023 23:03:38 +0100 Subject: [PATCH 16/65] refactor Buffer/Geometry - Buffer can be referenced by creating BufferView or BufferViewMut - Different part of Buffer can be referenced by creating BufferSlice - BufferView and BufferViewMut can be created from BufferSlice - Shared data between different geometry objects now resides in BufferGeometry --- examples/triangle/src/main.rs | 33 ++-- src/buffer.rs | 225 ++++++++++++++++++------ src/device.rs | 18 +- src/geometry.rs | 198 +++++++++++++++++---- src/{ => geometry}/catmull_rom_curve.rs | 0 src/geometry/triangle_mesh.rs | 77 +++----- src/instance.rs | 19 +- src/lib.rs | 24 +-- src/scene.rs | 8 +- 9 files changed, 394 insertions(+), 208 deletions(-) rename src/{ => geometry}/catmull_rom_curve.rs (100%) diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index 79315ca51..691994321 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -4,8 +4,7 @@ extern crate cgmath; extern crate embree; extern crate support; -use cgmath::{Vector3, Vector4}; -use embree::{Device, Geometry, IntersectContext, RayHitN, RayN, Scene, TriangleMesh}; +use embree::{BufferUsage, Device, Geometry, IntersectContext, RayHitN, RayN, TriangleMesh}; use std::sync::Arc; fn main() { @@ -13,24 +12,20 @@ fn main() { let device = Device::new().unwrap(); - // 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]; - - tris[0] = [0, 1, 2]; - } - - tri_mut.commit(); - } + device.set_error_function(|error, message| { + println!("Embree error {}: {}", error, message); + }); + // Make a triangle + let mut triangle = TriangleMesh::unanimated(&device, 1, 3); + triangle.get_buffer(BufferUsage::VERTEX, 0).unwrap() + .view_mut::<[f32; 4]>().unwrap() + .copy_from_slice(&[[-1.0, 0.0, 0.0, 1.0], [0.0, 1.0, 0.0, 1.0], [1.0, 0.0, 0.0, 1.0]]); + triangle.get_buffer(BufferUsage::INDEX, 0).unwrap().view_mut::<[u32; 3]>().unwrap() + .copy_from_slice(&[[0, 1, 2]]); + triangle.commit(); + + let triangle = Arc::new(triangle); let mut scene = device.create_scene().unwrap(); scene.attach_geometry(triangle); scene.commit(); diff --git a/src/buffer.rs b/src/buffer.rs index 223e38ad8..dd4d21572 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -2,7 +2,7 @@ use crate::Error; use std::marker::PhantomData; use std::mem; use std::num::NonZeroUsize; -use std::ops::{Bound, RangeBounds}; +use std::ops::{Bound, Deref, DerefMut, RangeBounds}; use crate::device::Device; use crate::sys::*; @@ -11,6 +11,7 @@ use crate::sys::*; pub type BufferSize = NonZeroUsize; /// Handle to a buffer managed by Embree. +#[derive(Debug)] pub struct Buffer { pub(crate) device: Device, pub(crate) handle: RTCBuffer, @@ -21,34 +22,38 @@ impl Clone for Buffer { fn clone(&self) -> Self { unsafe { rtcRetainBuffer(self.handle) }; Buffer { - device: self.device.clone(), handle: self.handle, size: self.size, + device: self.device.clone(), } } } impl Buffer { /// Creates a new data buffer of the given size. - pub(crate) fn new(device: Device, size: BufferSize) -> Result { + pub(crate) fn new(device: &Device, size: BufferSize) -> Result { // Pad to a multiple of 16 bytes - let size = if size % 16 == 0 { - size + let size = if size.get() % 16 == 0 { + size.get() } else { - (size + 15) & !15 + (size.get() + 15) & !15 }; let handle = unsafe { rtcNewBuffer(device.handle, size) }; if handle.is_null() { Err(device.get_error()) } else { Ok(Buffer { - device, handle, - size, + size: NonZeroUsize::new(size).unwrap(), + device: device.clone(), }) } } + pub fn handle(&self) -> RTCBuffer { + self.handle + } + /// Returns the a slice of the buffer. pub fn slice>(&self, bounds: S) -> BufferSlice { let start = match bounds.start_bound() { @@ -61,7 +66,7 @@ impl Buffer { Bound::Excluded(&n) => n, Bound::Unbounded => self.size.get(), }; - BufferSlice { + BufferSlice::Created { buffer: self.clone(), offset: start, size: NonZeroUsize::new(end - start).unwrap(), @@ -75,29 +80,22 @@ impl Buffer { /// * `range` - 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> { + pub fn mapped_range<'a, S: RangeBounds, T>(&'a self, bounds: S) -> BufferView<'a, 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()); - let range = BufferSlice { - buffer: self, - offset, - size: BufferSize::new(size).unwrap(), - }; - BufferView::new(self, range) + BufferView::new(self, offset, BufferSize::new(size).unwrap()).unwrap() } /// Mutable slice into the buffer for the given range. - pub fn mapped_range_mut>(&mut self, bounds: S) -> BufferViewMut<'_> { + 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()); - let range = BufferSlice { - buffer: self, - offset, - size: BufferSize::new(size).unwrap(), - }; - BufferViewMut::new(self, range) + BufferViewMut::new(self, offset, BufferSize::new(size).unwrap()).unwrap() } } @@ -109,56 +107,138 @@ impl Drop for Buffer { } } -static_assertions::assert_impl_all!(Buffer: Send, Sync); - /// A read-only view into mapped buffer. #[derive(Debug)] pub struct BufferView<'a, T: 'a> { range: BufferSlice, - slice: BufferMappedRange<'a, T>, - marker: PhantomData<&'a Buffer>, + mapped: BufferMappedRange<'a, T>, + marker: PhantomData<&'a T>, } /// A write-only view into mapped buffer. #[derive(Debug)] pub struct BufferViewMut<'a, T: 'a> { range: BufferSlice, - slice: BufferMappedRange<'a, T>, - marker: PhantomData<&'a mut Buffer>, + mapped: BufferMappedRange<'a, T>, + marker: PhantomData<&'a mut T>, } /// Slice into a [`Buffer`]. /// /// Created with [`Buffer::slice`]. -#[derive(Debug)] -pub struct BufferSlice { - /// The buffer this slice is a part of. - pub(crate) buffer: Buffer, - /// The offset into the buffer in bytes. - pub(crate) offset: usize, - /// The size of the slice in bytes. - pub(crate) size: BufferSize, +#[derive(Debug, Clone)] +pub enum BufferSlice { + /// Slice created from a [`Buffer`]. + Created { + /// The buffer this slice is a part of. + buffer: Buffer, + /// The offset into the buffer in bytes. + offset: usize, + /// The size of the slice in bytes. + size: BufferSize, + }, + /// Slice managed by Embree internally. + Managed { + ptr: *mut ::std::os::raw::c_void, + size: BufferSize, + marker: PhantomData<*mut ::std::os::raw::c_void>, + }, } -static_assertions::assert_impl_all!(BufferSlice: Send, Sync); +impl BufferSlice { + pub fn view(&self) -> Result, Error> { + match self { + BufferSlice::Created { + buffer, + offset, + size, + } => { + let slice = BufferMappedRange::from_buffer(buffer, *offset, size.get())?; + Ok(BufferView { + range: self.clone(), + mapped: slice, + marker: PhantomData, + }) + } + BufferSlice::Managed { 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 slice = unsafe { BufferMappedRange::from_raw_parts(*ptr as *mut T, len) }; + Ok(BufferView { + range: self.clone(), + mapped: slice, + marker: PhantomData, + }) + } + } + } -impl<'a> BufferSlice<'a> { - fn view(&self) -> BufferView<'a, T> { - let slice = BufferMappedRange::new(&self.buffer, self.offset, self.size)?; - BufferView { - range: *self, - slice, - marker: PhantomData, + pub fn view_mut(&self) -> Result, Error> { + match self { + BufferSlice::Created { + buffer, + offset, + size, + } => { + Ok(BufferViewMut { + range: self.clone(), + mapped: BufferMappedRange::from_buffer(buffer, *offset, size.get())?, + marker: PhantomData, + }) + } + BufferSlice::Managed { 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 slice = unsafe { BufferMappedRange::from_raw_parts(*ptr as *mut T, len) }; + Ok(BufferViewMut { + range: self.clone(), + mapped: slice, + marker: PhantomData, + }) + } } } +} - fn view_mut(&self) -> BufferViewMut<'a, T> { - let slice = BufferMappedRange::new(&self.buffer, self.offset, self.size)?; - BufferViewMut { - range: *self, - slice, +impl<'a, T> BufferView<'a, 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: &'a Buffer, offset: usize, size: BufferSize) -> Result, Error> { + Ok(BufferView { + range: BufferSlice::Created { + buffer: buffer.clone(), + offset, + size, + }, + mapped: BufferMappedRange::from_buffer(buffer, offset, size.into())?, marker: PhantomData, - } + }) + } +} + +impl<'a, T> BufferViewMut<'a, 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: &'a Buffer, + offset: usize, + size: BufferSize, + ) -> Result, Error> { + Ok(BufferViewMut { + range: BufferSlice::Created { + buffer: buffer.clone(), + offset, + size, + }, + mapped: BufferMappedRange::from_buffer(buffer, offset, size.into())?, + marker: PhantomData, + }) } } @@ -166,6 +246,7 @@ impl<'a> BufferSlice<'a> { 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> { @@ -176,7 +257,7 @@ impl<'a, T: 'a> BufferMappedRange<'a, T> { /// # Safety /// /// The caller must ensure that the given offset and size are valid. - fn new( + fn from_buffer( buffer: &'a Buffer, offset: usize, size: usize, @@ -199,9 +280,19 @@ impl<'a, T: 'a> BufferMappedRange<'a, 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, + } + } + fn as_slice(&self) -> &[T] { unsafe { std::slice::from_raw_parts(self.ptr, self.len) } } @@ -213,20 +304,40 @@ impl<'a, T: 'a> BufferMappedRange<'a, T> { impl AsRef<[T]> for BufferView<'_, T> { fn as_ref(&self) -> &[T] { - self.slice.as_slice() + self.mapped.as_slice() } } impl AsMut<[T]> for BufferViewMut<'_, T> { fn as_mut(&mut self) -> &mut [T] { - self.slice.as_mut_slice() + self.mapped.as_mut_slice() + } +} + +impl Deref for BufferView<'_, T> { + type Target = [T]; + + fn deref(&self) -> &Self::Target { + self.mapped.as_slice() + } +} + +impl Deref for BufferViewMut<'_, T> { + type Target = [T]; + + fn deref(&self) -> &Self::Target { + self.mapped.as_slice() + } +} + +impl DerefMut for BufferViewMut<'_, 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: RangeBounds, -) -> (usize, Option) { +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, diff --git a/src/device.rs b/src/device.rs index fa9b3ce09..d363a68e7 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,15 +1,11 @@ -use crate::buffer::BufferSize; -use crate::geometry::Geometry; use crate::sys::*; -use crate::Buffer; -use crate::Error; -use crate::GeometryType; -use crate::Scene; +use crate::{Error, Scene, BufferSize, Buffer, Geometry}; use std::ffi::CString; use std::fmt::{self, Display, Formatter}; use std::ptr; /// Handle to an Embree device. +#[derive(Debug)] pub struct Device { pub(crate) handle: RTCDevice, } @@ -189,7 +185,7 @@ impl Device { /// # Returns /// /// Error code encoded as `RTCError`. - pub fn error_code(&self) -> RTCError { + pub fn get_error(&self) -> RTCError { unsafe { rtcGetDeviceError(self.handle) } } @@ -208,13 +204,13 @@ impl Device { /// Creates a new data buffer. /// The created buffer is always aligned to 16 bytes. - pub fn create_buffer(&self, size: BufferSize) -> Result { - Buffer::new(self.clone(), size) + pub fn create_buffer(&self, size: usize) -> Result { + Buffer::new(self, BufferSize::new(size).unwrap()) } /// Creates a [`Geometry`] object bound to the device. - pub fn create_geometry(&self, kind: GeometryType) -> Result { - Geometry::new(self.clone(), kind) + pub fn create_geometry(&self) -> Result { + G::new(self) } } diff --git a/src/geometry.rs b/src/geometry.rs index 468a3e491..58d79be4f 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,24 +1,45 @@ use std::collections::HashMap; +use std::num::NonZeroUsize; -use crate::buffer::BufferSlice; use crate::{sys::*, Device, Error}; -use crate::{BufferUsage, Format, GeometryType}; +use crate::{BufferUsage, Format, GeometryType, BufferSlice}; mod triangle_mesh; +pub use triangle_mesh::TriangleMesh; + pub trait Geometry { - fn geometry_type(&self) -> GeometryType; + fn new(device: &Device) -> Result where Self: Sized; + fn kind(&self) -> GeometryType; + fn handle(&self) -> RTCGeometry; + fn commit(&mut self) { + unsafe { + rtcCommitGeometry(self.handle()); + } + } +} + +#[derive(Debug)] +pub(crate) struct AttachedBuffer { + pub slot: u32, + pub buffer: BufferSlice, + pub format: Format, + pub stride: usize, } /// Handle to an Embree geometry object. -pub struct GeometryData { +/// +/// BufferGeometry is a wrapper around an Embree geometry object. It does not own the +/// buffers that are bound to it, but it does own the geometry object itself. +#[derive(Debug)] +pub struct BufferGeometry { pub(crate) device: Device, pub(crate) handle: RTCGeometry, pub(crate) kind: GeometryType, - pub(crate) bindings: HashMap>, + pub(crate) attachments: HashMap>, } -impl Drop for GeometryData { +impl<'a> Drop for BufferGeometry { fn drop(&mut self) { unsafe { rtcReleaseGeometry(self.handle); @@ -26,54 +47,157 @@ impl Drop for GeometryData { } } -impl GeometryData { - pub(crate) fn new(device: Device, kind: GeometryType) -> Result { +impl BufferGeometry { + pub(crate) fn new(device: &Device, kind: GeometryType) -> Result { let handle = unsafe { rtcNewGeometry(device.handle, kind) }; if handle.is_null() { Err(device.get_error()) } else { - Ok(GeometryData { - device, + Ok(BufferGeometry { + device: device.clone(), handle, kind, - bindings: HashMap::new(), + attachments: HashMap::new(), }) } } - // pub fn bind_new_buffer( - // &mut self, - // usage: BufferUsage, - // format: Format, - // data: &[T], - // ) -> Result { - // let buffer = self.device.create_buffer(usage, format, data)?; - // self.bind_buffer(usage, buffer) - // } - - /// Binds a view of a buffer to a geometry. - pub fn bind_buffer( + /// Binds a view of a buffer to the geometry. + /// + /// Analogous to [`rtcSetGeometryBuffer`](https://spec.oneapi.io/oneart/0.5-rev-1/embree-spec.html#rtcsetgeometrybuffer). + /// + /// # Arguments + /// + /// * `usage` - The usage of the buffer. + /// + /// * `slot` - The slot to bind the buffer to. + /// + /// * `format` - The format of the buffer. + /// + /// * `slice` - The buffer slice to bind. + /// + /// * `stride` - The stride of the elements in the buffer. + /// + /// * `count` - The number of elements in the buffer. + pub fn set_buffer( &mut self, - buffer: BufferSlice, - slot: u32, usage: BufferUsage, + slot: u32, format: Format, + slice: BufferSlice, + stride: usize, + count: usize, ) -> Result<(), Error> { - self.bindings.entry(usage).or_insert(vec![]) - unsafe { - rtcSetGeometryBuffer( - self.handle, - usage, - slot, - format, + match slice { + BufferSlice::Created { buffer, - buffer.offset, - std::mem::size_of::(), - buffer.size / std::mem::size_of::(), - ) + offset, + size, + } => { + let mut bindings = self.attachments.entry(usage).or_insert_with(Vec::new); + if bindings.iter().find(|a| a.slot == slot).is_none() { + println!("Binding buffer to slot {}, offset {}, stride {}, count {}", slot, + offset, stride, count); + unsafe { + rtcSetGeometryBuffer( + self.handle, + usage, + slot, + format, + buffer.handle, + offset, + stride as usize, + count as usize, + ) + }; + bindings.push(AttachedBuffer { + slot, + buffer: BufferSlice::Created { + buffer, + offset, + size, + }, + format, + stride, + }); + Ok(()) + } else { + eprint!("Buffer already attached to slot {}", slot); + Err(Error::INVALID_ARGUMENT) + } + } + BufferSlice::Managed { .. } => { + eprint!("Internal buffer cannot be shared!"); + Err(Error::INVALID_ARGUMENT) + } + } + } + + /// Creates a new [`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. + /// + /// 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 aligned to 4 bytes. + pub fn set_new_buffer( + &mut self, + usage: BufferUsage, + slot: u32, + format: Format, + stride: usize, + count: usize, + ) -> Result<(), Error> { + let bindings = self.attachments.entry(usage).or_insert_with(Vec::new); + if bindings.iter().find(|a| a.slot == slot).is_none() { + let raw_ptr = unsafe { + rtcSetNewGeometryBuffer(self.handle, usage, slot, format, stride as usize, count as usize) + }; + if raw_ptr.is_null() { + Err(self.device.get_error()) + } else { + let buffer_slice = BufferSlice::Managed { + ptr: raw_ptr, + size: NonZeroUsize::new(count * stride as usize).unwrap(), + marker: std::marker::PhantomData, + }; + bindings.push(AttachedBuffer { + slot, + buffer: buffer_slice, + format, + stride, + }); + Ok(()) + } + } 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<&BufferSlice> { + self.attachments + .get(&usage) + .and_then(|v| v.iter().find(|a| a.slot == slot)) + .map(|a| &a.buffer) + } + pub fn commit(&mut self) { unsafe { rtcCommitGeometry(self.handle); @@ -126,7 +250,7 @@ impl GeometryData { /// 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); + rtcSetGeometryVertexAttributeTopology(self.handle, vertex_attribute_id, topology_id); } } } diff --git a/src/catmull_rom_curve.rs b/src/geometry/catmull_rom_curve.rs similarity index 100% rename from src/catmull_rom_curve.rs rename to src/geometry/catmull_rom_curve.rs diff --git a/src/geometry/triangle_mesh.rs b/src/geometry/triangle_mesh.rs index e2058e721..103313c2a 100644 --- a/src/geometry/triangle_mesh.rs +++ b/src/geometry/triangle_mesh.rs @@ -1,17 +1,12 @@ -use std::ops::{Deref, DerefMut}; -use std::sync::Arc; - -use crate::buffer::Buffer; -use crate::device::Device; use crate::sys::*; -use crate::{BufferType, Format, GeometryType}; - -use super::GeometryData; +use crate::{BufferUsage, Device, Format, GeometryType, BufferGeometry, Geometry}; +use std::ops::{Deref, DerefMut}; -pub struct TriangleMesh(GeometryData); +#[derive(Debug)] +pub struct TriangleMesh(BufferGeometry); impl Deref for TriangleMesh { - type Target = GeometryData; + type Target = BufferGeometry; fn deref(&self) -> &Self::Target { &self.0 @@ -25,54 +20,30 @@ impl DerefMut for TriangleMesh { } impl TriangleMesh { - pub fn unanimated(device: Device, 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); + pub fn unanimated(device: &Device, num_tris: usize, num_verts: usize) -> TriangleMesh { + let mut geometry = BufferGeometry::new(device, GeometryType::TRIANGLE).unwrap(); + + geometry + .set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, num_verts) + .unwrap(); + geometry + .set_new_buffer(BufferUsage::INDEX, 0, Format::UINT3, 12, num_tris) + .unwrap(); - 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, - }) + Self(geometry) } } -impl GeometryTrait for TriangleMesh { - fn handle(&self) -> RTCGeometry { - self.handle +impl Geometry for TriangleMesh { + fn new(device: &Device) -> Result where Self: Sized { + Ok(Self(BufferGeometry::new(device, GeometryType::TRIANGLE)?)) + } + + fn kind(&self) -> GeometryType { + GeometryType::TRIANGLE } -} -impl Drop for TriangleMesh { - fn drop(&mut self) { - unsafe { - rtcReleaseGeometry(self.handle); - } + fn handle(&self) -> RTCGeometry { + self.handle } } diff --git a/src/instance.rs b/src/instance.rs index 3d33e123c..e4c43baa2 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -1,10 +1,10 @@ use std::os::raw; use std::sync::Arc; -use crate::geometry::GeometryTrait; +use crate::geometry::Geometry; use crate::scene::Scene; use crate::sys::*; -use crate::{Format, GeometryType}; +use crate::{Format, GeometryType, Device}; pub struct Instance { /// The scene being instanced @@ -18,10 +18,7 @@ impl Instance { unsafe { rtcSetGeometryInstancedScene(h, scene.handle); } - Arc::new(Instance { - handle: h, - scene: scene, - }) + Arc::new(Instance { handle: h, scene }) } pub fn set_transform>(&mut self, transform: Transform) { let mat: &[f32; 16] = transform.as_ref(); @@ -37,10 +34,18 @@ impl Instance { } } -impl GeometryTrait for Instance { +impl Geometry for Instance { + fn new(device: &Device) -> Result where Self: Sized { + unimplemented!() + } + fn handle(&self) -> RTCGeometry { self.handle } + + fn kind(&self) -> GeometryType { + GeometryType::INSTANCE + } } impl Drop for Instance { diff --git a/src/lib.rs b/src/lib.rs index 59a03453a..5c36d0ee4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,20 +13,13 @@ 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 error; -pub mod geometry; -// pub mod hermite_curve; +mod geometry; 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; @@ -36,20 +29,10 @@ pub mod soa_ray; #[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, BufferView}; -pub use catmull_rom_curve::CatmullRomCurve; -// pub use curve::CurveType; +pub use buffer::{Buffer, BufferSlice, BufferView, BufferViewMut, BufferSize}; pub use device::{Config, Device, FrequencyLevel, Isa}; -// pub use geometry::GeometryTrait; -// 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}; @@ -58,7 +41,8 @@ pub use soa_ray::{ SoAHit, SoAHitIter, SoAHitIterMut, SoAHitRef, SoARay, SoARayIter, SoARayIterMut, SoARayRef, SoARayRefMut, }; -pub use triangle_mesh::TriangleMesh; + +pub use geometry::*; // Pull in some cleaned up enum and bitfield types directly, // with prettier aliases diff --git a/src/scene.rs b/src/scene.rs index 35a864158..32c3645f5 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use crate::callback; use crate::device::Device; -use crate::geometry::GeometryTrait; +use crate::geometry::Geometry; use crate::intersect_context::IntersectContext; use crate::ray::{Ray, RayHit}; use crate::ray_packet::{Ray4, RayHit4}; @@ -20,7 +20,7 @@ use crate::sys::*; pub struct Scene { pub(crate) handle: RTCScene, pub(crate) device: Device, - geometry: HashMap>, + geometry: HashMap>, } impl Clone for Scene { @@ -39,7 +39,7 @@ impl Scene { pub(crate) fn new(device: Device) -> Result { let handle = unsafe { rtcNewScene(device.handle) }; if handle.is_null() { - Err(device.error_code()) + Err(device.get_error()) } else { Ok(Scene { handle, @@ -61,7 +61,7 @@ impl Scene { /// 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 { + pub fn attach_geometry(&mut self, mesh: Arc) -> u32 { let id = unsafe { rtcAttachGeometry(self.handle, mesh.handle()) }; self.geometry.insert(id, mesh); id From 8fcbd59ba5ffa23fd175c1792b87c2a33251edd0 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Mon, 13 Feb 2023 23:23:53 +0100 Subject: [PATCH 17/65] add rustfmt.toml configuration file --- build.rs | 3 +- rustfmt.toml | 7 ++ src/buffer.rs | 73 +++++++++---------- src/callback.rs | 6 +- src/device.rs | 123 ++++++++++++++++--------------- src/geometry.rs | 73 +++++++++++-------- src/geometry/triangle_mesh.rs | 24 +++---- src/instance.rs | 24 +++---- src/intersect_context.rs | 32 +++++---- src/lib.rs | 22 +++--- src/ray.rs | 4 +- src/ray_packet.rs | 118 +++++++++--------------------- src/ray_stream.rs | 131 ++++++++++------------------------ src/scene.rs | 50 +++++++------ src/soa_ray.rs | 80 ++++++--------------- src/sys.rs | 64 +++++------------ 16 files changed, 332 insertions(+), 502 deletions(-) create mode 100644 rustfmt.toml 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/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/src/buffer.rs b/src/buffer.rs index dd4d21572..3d0ea8b50 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,11 +1,12 @@ use crate::Error; -use std::marker::PhantomData; -use std::mem; -use std::num::NonZeroUsize; -use std::ops::{Bound, Deref, DerefMut, RangeBounds}; +use std::{ + marker::PhantomData, + mem, + num::NonZeroUsize, + ops::{Bound, Deref, DerefMut, RangeBounds}, +}; -use crate::device::Device; -use crate::sys::*; +use crate::{device::Device, sys::*}; /// Non-zero integer type used to describe the size of a buffer. pub type BufferSize = NonZeroUsize; @@ -50,9 +51,7 @@ impl Buffer { } } - pub fn handle(&self) -> RTCBuffer { - self.handle - } + pub fn handle(&self) -> RTCBuffer { self.handle } /// Returns the a slice of the buffer. pub fn slice>(&self, bounds: S) -> BufferSlice { @@ -182,13 +181,11 @@ impl BufferSlice { buffer, offset, size, - } => { - Ok(BufferViewMut { - range: self.clone(), - mapped: BufferMappedRange::from_buffer(buffer, *offset, size.get())?, - marker: PhantomData, - }) - } + } => Ok(BufferViewMut { + range: self.clone(), + mapped: BufferMappedRange::from_buffer(buffer, *offset, size.get())?, + marker: PhantomData, + }), BufferSlice::Managed { ptr, size, .. } => { debug_assert!( size.get() % mem::size_of::() == 0, @@ -207,9 +204,13 @@ impl BufferSlice { } impl<'a, T> BufferView<'a, 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: &'a Buffer, offset: usize, size: BufferSize) -> Result, Error> { + /// Creates a new slice from the given Buffer with the given offset and + /// size. Only used internally by [`Buffer::mapped_range`]. + fn new( + buffer: &'a Buffer, + offset: usize, + size: BufferSize, + ) -> Result, Error> { Ok(BufferView { range: BufferSlice::Created { buffer: buffer.clone(), @@ -223,8 +224,8 @@ impl<'a, T> BufferView<'a, T> { } impl<'a, T> BufferViewMut<'a, T> { - /// Creates a new slice from the given Buffer with the given offset and size. - /// Only used internally by [`Buffer::mapped_range_mut`]. + /// 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: &'a Buffer, offset: usize, @@ -250,9 +251,11 @@ struct BufferMappedRange<'a, T: 'a> { } impl<'a, T: 'a> BufferMappedRange<'a, T> { - /// Creates a new slice from the given Buffer with the given offset and size. + /// 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`. + /// The offset and size must be in bytes and must be a multiple of the size + /// of `T`. /// /// # Safety /// @@ -293,9 +296,7 @@ impl<'a, T: 'a> BufferMappedRange<'a, T> { } } - fn as_slice(&self) -> &[T] { - unsafe { std::slice::from_raw_parts(self.ptr, self.len) } - } + 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) } @@ -303,37 +304,27 @@ impl<'a, T: 'a> BufferMappedRange<'a, T> { } impl AsRef<[T]> for BufferView<'_, T> { - fn as_ref(&self) -> &[T] { - self.mapped.as_slice() - } + fn as_ref(&self) -> &[T] { self.mapped.as_slice() } } impl AsMut<[T]> for BufferViewMut<'_, T> { - fn as_mut(&mut self) -> &mut [T] { - self.mapped.as_mut_slice() - } + fn as_mut(&mut self) -> &mut [T] { self.mapped.as_mut_slice() } } impl Deref for BufferView<'_, T> { type Target = [T]; - fn deref(&self) -> &Self::Target { - self.mapped.as_slice() - } + fn deref(&self) -> &Self::Target { self.mapped.as_slice() } } impl Deref for BufferViewMut<'_, T> { type Target = [T]; - fn deref(&self) -> &Self::Target { - self.mapped.as_slice() - } + fn deref(&self) -> &Self::Target { self.mapped.as_slice() } } impl DerefMut for BufferViewMut<'_, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.mapped.as_mut_slice() - } + fn deref_mut(&mut self) -> &mut Self::Target { self.mapped.as_mut_slice() } } /// Converts a range bounds into an offset and size. diff --git a/src/callback.rs b/src/callback.rs index 36c580f4d..07c19b70e 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -2,7 +2,8 @@ use std::os::raw::c_void; use crate::sys::*; -/// Helper function to convert a Rust closure to `RTCProgressMonitorFunction` callback. +/// 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, @@ -37,7 +38,8 @@ where Some(inner::) } -/// Helper function to convert a Rust closure to `RTCMemoryMonitorFunction` callback. +/// 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, diff --git a/src/device.rs b/src/device.rs index d363a68e7..90753ab0f 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,8 +1,9 @@ -use crate::sys::*; -use crate::{Error, Scene, BufferSize, Buffer, Geometry}; -use std::ffi::CString; -use std::fmt::{self, Display, Formatter}; -use std::ptr; +use crate::{sys::*, Buffer, BufferSize, Error, Geometry, Scene}; +use std::{ + ffi::CString, + fmt::{self, Display, Formatter}, + ptr, +}; /// Handle to an Embree device. #[derive(Debug)] @@ -63,10 +64,12 @@ 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 /// @@ -74,7 +77,7 @@ impl Device { /// use embree::Device; /// 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) @@ -100,12 +103,13 @@ 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`]. /// @@ -114,18 +118,22 @@ impl Device { /// * `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. + /// * `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. + /// 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 @@ -133,9 +141,9 @@ impl Device { /// 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 /// }); @@ -165,7 +173,8 @@ impl Device { /// /// # Arguments /// - /// * `prop` - The property to query. See `RTCDeviceProp` for possible values. + /// * `prop` - The property to query. See `RTCDeviceProp` for possible + /// values. /// /// # Returns /// @@ -176,23 +185,20 @@ impl Device { /// 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 `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`. /// /// # Returns /// /// Error code encoded as `RTCError`. - pub fn get_error(&self) -> RTCError { - unsafe { rtcGetDeviceError(self.handle) } - } + pub fn get_error(&self) -> RTCError { unsafe { rtcGetDeviceError(self.handle) } } /// Creates a new scene bound to the device. - pub fn create_scene(&self) -> Result { - Scene::new(self.clone()) - } + pub fn create_scene(&self) -> Result { 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 @@ -209,9 +215,7 @@ impl Device { } /// Creates a [`Geometry`] object bound to the device. - pub fn create_geometry(&self) -> Result { - G::new(self) - } + pub fn create_geometry(&self) -> Result { G::new(self) } } impl Drop for Device { @@ -279,36 +283,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, } @@ -324,8 +333,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, diff --git a/src/geometry.rs b/src/geometry.rs index 58d79be4f..486c65e12 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,15 +1,15 @@ -use std::collections::HashMap; -use std::num::NonZeroUsize; +use std::{collections::HashMap, num::NonZeroUsize}; -use crate::{sys::*, Device, Error}; -use crate::{BufferUsage, Format, GeometryType, BufferSlice}; +use crate::{sys::*, BufferSlice, BufferUsage, Device, Error, Format, GeometryType}; mod triangle_mesh; pub use triangle_mesh::TriangleMesh; pub trait Geometry { - fn new(device: &Device) -> Result where Self: Sized; + fn new(device: &Device) -> Result + where + Self: Sized; fn kind(&self) -> GeometryType; fn handle(&self) -> RTCGeometry; fn commit(&mut self) { @@ -29,8 +29,9 @@ pub(crate) struct AttachedBuffer { /// Handle to an Embree geometry object. /// -/// BufferGeometry is a wrapper around an Embree geometry object. It does not own the -/// buffers that are bound to it, but it does own the geometry object itself. +/// BufferGeometry is a wrapper around an Embree geometry object. It does not +/// own the buffers that are bound to it, but it does own the geometry object +/// itself. #[derive(Debug)] pub struct BufferGeometry { pub(crate) device: Device, @@ -96,8 +97,10 @@ impl BufferGeometry { } => { let mut bindings = self.attachments.entry(usage).or_insert_with(Vec::new); if bindings.iter().find(|a| a.slot == slot).is_none() { - println!("Binding buffer to slot {}, offset {}, stride {}, count {}", slot, - offset, stride, count); + println!( + "Binding buffer to slot {}, offset {}, stride {}, count {}", + slot, offset, stride, count + ); unsafe { rtcSetGeometryBuffer( self.handle, @@ -133,16 +136,18 @@ impl BufferGeometry { } } - /// Creates a new [`Buffer`] and binds it as a specific attribute for this geometry. + /// Creates a new [`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. + /// 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. /// - /// The allocated buffer is managed internally and automatically released when the geometry - /// is destroyed by Embree. + /// The allocated buffer is managed internally and automatically released + /// when the geometry is destroyed by Embree. /// /// # Arguments /// @@ -150,7 +155,8 @@ impl BufferGeometry { /// /// * `slot` - The slot to bind the buffer to. /// - /// * `format` - The format of the buffer items. See [`Format`] for more information. + /// * `format` - The format of the buffer items. See [`Format`] for more + /// information. /// /// * `count` - The number of items in the buffer. /// @@ -166,7 +172,14 @@ impl BufferGeometry { let bindings = self.attachments.entry(usage).or_insert_with(Vec::new); if bindings.iter().find(|a| a.slot == slot).is_none() { let raw_ptr = unsafe { - rtcSetNewGeometryBuffer(self.handle, usage, slot, format, stride as usize, count as usize) + rtcSetNewGeometryBuffer( + self.handle, + usage, + slot, + format, + stride as usize, + count as usize, + ) }; if raw_ptr.is_null() { Err(self.device.get_error()) @@ -207,8 +220,8 @@ impl BufferGeometry { /// 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. @@ -222,8 +235,8 @@ impl BufferGeometry { /// /// * [`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 @@ -237,17 +250,19 @@ impl BufferGeometry { /// 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. + /// 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. + /// 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); diff --git a/src/geometry/triangle_mesh.rs b/src/geometry/triangle_mesh.rs index 103313c2a..12f9c949c 100644 --- a/src/geometry/triangle_mesh.rs +++ b/src/geometry/triangle_mesh.rs @@ -1,5 +1,4 @@ -use crate::sys::*; -use crate::{BufferUsage, Device, Format, GeometryType, BufferGeometry, Geometry}; +use crate::{sys::*, BufferGeometry, BufferUsage, Device, Format, Geometry, GeometryType}; use std::ops::{Deref, DerefMut}; #[derive(Debug)] @@ -8,15 +7,11 @@ pub struct TriangleMesh(BufferGeometry); impl Deref for TriangleMesh { type Target = BufferGeometry; - fn deref(&self) -> &Self::Target { - &self.0 - } + fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for TriangleMesh { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl TriangleMesh { @@ -35,15 +30,14 @@ impl TriangleMesh { } impl Geometry for TriangleMesh { - fn new(device: &Device) -> Result where Self: Sized { + fn new(device: &Device) -> Result + where + Self: Sized, + { Ok(Self(BufferGeometry::new(device, GeometryType::TRIANGLE)?)) } - fn kind(&self) -> GeometryType { - GeometryType::TRIANGLE - } + fn kind(&self) -> GeometryType { GeometryType::TRIANGLE } - fn handle(&self) -> RTCGeometry { - self.handle - } + fn handle(&self) -> RTCGeometry { self.handle } } diff --git a/src/instance.rs b/src/instance.rs index e4c43baa2..990457de7 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -1,10 +1,6 @@ -use std::os::raw; -use std::sync::Arc; +use std::{os::raw, sync::Arc}; -use crate::geometry::Geometry; -use crate::scene::Scene; -use crate::sys::*; -use crate::{Format, GeometryType, Device}; +use crate::{geometry::Geometry, scene::Scene, sys::*, Device, Format, GeometryType}; pub struct Instance { /// The scene being instanced @@ -22,7 +18,8 @@ impl Instance { } 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? + // Will this be fine if we don't set the number of timesteps? Default should be + // 1? unsafe { rtcSetGeometryTransform( self.handle, @@ -35,17 +32,16 @@ impl Instance { } impl Geometry for Instance { - fn new(device: &Device) -> Result where Self: Sized { + fn new(device: &Device) -> Result + where + Self: Sized, + { unimplemented!() } - fn handle(&self) -> RTCGeometry { - self.handle - } + fn handle(&self) -> RTCGeometry { self.handle } - fn kind(&self) -> GeometryType { - GeometryType::INSTANCE - } + fn kind(&self) -> GeometryType { GeometryType::INSTANCE } } impl Drop for Instance { diff --git a/src/intersect_context.rs b/src/intersect_context.rs index 1ae8db2b5..572df68b5 100644 --- a/src/intersect_context.rs +++ b/src/intersect_context.rs @@ -2,32 +2,34 @@ 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). +/// 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. +/// 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. +/// 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. +/// 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). +/// 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 { diff --git a/src/lib.rs b/src/lib.rs index 5c36d0ee4..a8c3b9c5f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,7 +29,7 @@ pub mod soa_ray; #[allow(non_camel_case_types)] #[allow(non_snake_case)] pub mod sys; -pub use buffer::{Buffer, BufferSlice, BufferView, BufferViewMut, BufferSize}; +pub use buffer::{Buffer, BufferSize, BufferSlice, BufferView, BufferViewMut}; pub use device::{Config, Device, FrequencyLevel, Isa}; pub use instance::Instance; pub use intersect_context::IntersectContext; @@ -46,18 +46,16 @@ pub use geometry::*; // Pull in some cleaned up enum and bitfield types directly, // with prettier aliases -pub use sys::RTCBufferType as BufferUsage; -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::{ + RTCBufferType as BufferUsage, RTCBuildQuality as BuildQuality, + RTCDeviceProperty as DeviceProperty, RTCError as Error, RTCFormat as Format, + RTCGeometryType as GeometryType, 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; +pub use sys::{ + RTCBuildFlags as BuildFlags, RTCCurveFlags as CurveFlags, + RTCIntersectContextFlags as IntersectContextFlags, RTCSceneFlags as SceneFlags, +}; /// Utility for making specifically aligned vectors pub fn aligned_vector(len: usize, align: usize) -> Vec { diff --git a/src/ray.rs b/src/ray.rs index 1364ba0d8..02d1d390e 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -42,9 +42,7 @@ impl Hit { instID: [u32::MAX; 1], } } - pub fn hit(&self) -> bool { - self.geomID != u32::MAX - } + pub fn hit(&self) -> bool { self.geomID != u32::MAX } } impl RayHit { diff --git a/src/ray_packet.rs b/src/ray_packet.rs index 48693f63d..f959ef333 100644 --- a/src/ray_packet.rs +++ b/src/ray_packet.rs @@ -1,7 +1,9 @@ use std::{f32, u32}; -use crate::soa_ray::{SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut}; -use crate::sys; +use crate::{ + soa_ray::{SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut}, + sys, +}; pub type Ray4 = sys::RTCRay4; pub type Hit4 = sys::RTCHit4; @@ -40,74 +42,42 @@ impl Ray4 { flags: [0; 4], } } - pub fn iter(&self) -> SoARayIter { - SoARayIter::new(self, 4) - } - pub fn iter_mut(&mut self) -> SoARayIterMut { - SoARayIterMut::new(self, 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 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 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 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 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 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 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 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; - } + fn flags(&self, i: usize) -> u32 { self.flags[i] } + fn set_flags(&mut self, i: usize, flags: u32) { self.flags[i] = flags; } } impl Hit4 { @@ -123,60 +93,36 @@ impl Hit4 { instID: [[u32::MAX; 4]], } } - pub fn any_hit(&self) -> bool { - self.hits().fold(false, |acc, g| acc || g) - } + 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(&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 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 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 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 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; - } + 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 { diff --git a/src/ray_stream.rs b/src/ray_stream.rs index 670764953..8370e376e 100644 --- a/src/ray_stream.rs +++ b/src/ray_stream.rs @@ -1,9 +1,10 @@ -use std::iter::Iterator; -use std::{f32, u32}; +use std::{f32, iter::Iterator, u32}; -use crate::soa_ray::{SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut}; -use crate::sys; -use crate::{aligned_vector, aligned_vector_init}; +use crate::{ + aligned_vector, aligned_vector_init, + soa_ray::{SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut}, + sys, +}; /// A ray stream stored in SoA format pub struct RayN { @@ -39,16 +40,12 @@ impl RayN { flags: aligned_vector_init::(n, 16, 0), } } - pub fn iter(&self) -> SoARayIter { - SoARayIter::new(self, self.len()) - } + 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 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(), @@ -68,65 +65,37 @@ impl RayN { } impl SoARay for RayN { - fn org(&self, i: usize) -> [f32; 3] { - [self.org_x[i], self.org_y[i], self.org_z[i]] - } + 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 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 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 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 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 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 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; - } + 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 { @@ -153,21 +122,15 @@ impl HitN { 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 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(&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 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(), @@ -183,45 +146,25 @@ impl HitN { } impl SoAHit for HitN { - fn normal(&self, i: usize) -> [f32; 3] { - [self.ng_x[i], self.ng_y[i], self.ng_z[i]] - } + 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 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 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 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; - } + 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 { @@ -240,9 +183,7 @@ impl RayHitN { 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 len(&self) -> usize { self.ray.len() } pub unsafe fn as_rayhitnp(&mut self) -> sys::RTCRayHitNp { sys::RTCRayHitNp { ray: self.ray.as_raynp(), diff --git a/src/scene.rs b/src/scene.rs index 32c3645f5..5885ad842 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,17 +1,16 @@ -use crate::Error; -use crate::SceneFlags; -use std::collections::HashMap; -use std::mem; -use std::sync::Arc; +use crate::{Error, SceneFlags}; +use std::{collections::HashMap, mem, 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::*; +use crate::{ + callback, + device::Device, + geometry::Geometry, + intersect_context::IntersectContext, + ray::{Ray, RayHit}, + ray_packet::{Ray4, RayHit4}, + ray_stream::{RayHitN, RayN}, + sys::*, +}; /// A scene containing various geometry for rendering. Geometry /// can be added and removed by attaching and detaching it, after @@ -77,9 +76,7 @@ impl Scene { /// 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 - } + pub unsafe fn handle(&self) -> RTCScene { self.handle } /// Commit the scene to build the BVH on top of the geometry to allow /// for ray tracing the scene using the intersect/occluded methods @@ -89,9 +86,10 @@ impl Scene { } } - /// 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`]. + /// 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) { unsafe { rtcSetSceneFlags(self.handle, flags); @@ -100,8 +98,8 @@ 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().unwrap(); @@ -109,11 +107,10 @@ impl Scene { /// let flags = scene.flags(); /// scene.set_flags(flags | SceneFlags::ROBUST); /// ``` - pub fn flags(&self) -> RTCSceneFlags { - unsafe { rtcGetSceneFlags(self.handle) } - } + pub fn flags(&self) -> RTCSceneFlags { unsafe { rtcGetSceneFlags(self.handle) } } - /// Set the build quality of the scene. See [`RTCBuildQuality`] for all possible values. + /// Set the build quality of the scene. See [`RTCBuildQuality`] for all + /// possible values. pub fn set_build_quality(&self, quality: RTCBuildQuality) { unsafe { rtcSetSceneBuildQuality(self.handle, quality); @@ -129,7 +126,8 @@ 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 diff --git a/src/soa_ray.rs b/src/soa_ray.rs index 8a6822d94..423bd411e 100644 --- a/src/soa_ray.rs +++ b/src/soa_ray.rs @@ -1,6 +1,8 @@ -use std::iter::{ExactSizeIterator, Iterator}; -use std::marker::PhantomData; -use std::u32; +use std::{ + iter::{ExactSizeIterator, Iterator}, + marker::PhantomData, + u32, +}; pub trait SoARay { fn org(&self, i: usize) -> [f32; 3]; @@ -45,9 +47,7 @@ pub trait SoAHit { 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 hit(&self, i: usize) -> bool { self.geom_id(i) != u32::MAX } } pub struct SoARayRef<'a, T> { @@ -56,27 +56,13 @@ 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 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? @@ -179,9 +165,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> { @@ -219,9 +203,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 +212,12 @@ 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 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 struct SoAHitIter<'a, T> { @@ -284,9 +254,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> { @@ -381,7 +349,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/sys.rs b/src/sys.rs index cb3682191..69e036e83 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -250,28 +250,20 @@ 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, Hash, PartialEq, Eq)] @@ -2771,28 +2763,20 @@ 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, Hash, PartialEq, Eq)] @@ -3889,28 +3873,20 @@ 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, Hash, PartialEq, Eq)] @@ -4392,28 +4368,20 @@ 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, Hash, PartialEq, Eq)] From 61f6a22d70060d1758b8a419a6555c3e763cc722 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Tue, 14 Feb 2023 15:38:53 +0100 Subject: [PATCH 18/65] minimal example & add all examples in workspace Twinklebear/embree-r#21 --- Cargo.toml | 5 ++ examples/instancing/src/main.rs | 3 +- examples/minimal/Cargo.toml | 10 ++++ examples/minimal/src/main.rs | 94 +++++++++++++++++++++++++++++++++ examples/support/src/display.rs | 4 +- examples/triangle/src/main.rs | 20 ++++--- src/buffer.rs | 18 +++++-- src/device.rs | 3 +- src/geometry.rs | 21 ++++---- src/geometry/triangle_mesh.rs | 13 +++-- src/lib.rs | 4 ++ src/ray.rs | 23 ++++---- src/scene.rs | 14 ++++- 13 files changed, 195 insertions(+), 37 deletions(-) create mode 100644 examples/minimal/Cargo.toml create mode 100644 examples/minimal/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 09caf9f64..f45c30c7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,8 @@ exclude = [ [dependencies] static_assertions = "1.1.0" + +[workspace] +members = [ + "examples/*", +] diff --git a/examples/instancing/src/main.rs b/examples/instancing/src/main.rs index b0f58b60d..8f732b747 100644 --- a/examples/instancing/src/main.rs +++ b/examples/instancing/src/main.rs @@ -211,7 +211,8 @@ fn main() { 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 + // 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]; diff --git a/examples/minimal/Cargo.toml b/examples/minimal/Cargo.toml new file mode 100644 index 000000000..579750f41 --- /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.22" diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs new file mode 100644 index 000000000..65532cced --- /dev/null +++ b/examples/minimal/src/main.rs @@ -0,0 +1,94 @@ +//! This example shows how to intersect a ray with a single triangle. + +use embree::{BufferUsage, Device, Format, IntersectContext, Ray, Scene, TriangleMesh}; +use std::sync::Arc; + +/// Casts a single ray with the given origin and direction. +fn cast_ray(scene: &Scene, origin: [f32; 3], direction: [f32; 3]) { + let ray = Ray::new(origin, direction); + + // 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 ray_hit = scene.intersect(&mut context, ray); + + 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!("error {:?}: {}", 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::().unwrap(); + triangle + .set_new_buffer( + BufferUsage::VERTEX, + 0, + Format::FLOAT3, + 3 * std::mem::size_of::(), + 3, + ) + .unwrap(); + triangle + .set_new_buffer( + BufferUsage::INDEX, + 0, + Format::UINT3, + 3 * std::mem::size_of::(), + 1, + ) + .unwrap(); + + // Fill the vertex and index buffers with the triangle data. + triangle + .get_buffer(BufferUsage::VERTEX, 0) + .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 + .get_buffer(BufferUsage::INDEX, 0) + .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(Arc::new(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/src/display.rs b/examples/support/src/display.rs index d46a6e8dd..f9dee2e94 100644 --- a/examples/support/src/display.rs +++ b/examples/support/src/display.rs @@ -114,8 +114,8 @@ impl Display { } } } -/// The function passed should render and update the image to be displayed in the window, -/// optionally using the camera pose information passed. +/// 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), diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index 691994321..183d7d356 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -1,10 +1,11 @@ #![allow(dead_code)] -extern crate cgmath; extern crate embree; extern crate support; -use embree::{BufferUsage, Device, Geometry, IntersectContext, RayHitN, RayN, TriangleMesh}; +use embree::{ + BufferUsage, Device, Format, Geometry, IntersectContext, RayHitN, RayN, TriangleMesh, +}; use std::sync::Arc; fn main() { @@ -18,10 +19,17 @@ fn main() { // Make a triangle let mut triangle = TriangleMesh::unanimated(&device, 1, 3); - triangle.get_buffer(BufferUsage::VERTEX, 0).unwrap() - .view_mut::<[f32; 4]>().unwrap() - .copy_from_slice(&[[-1.0, 0.0, 0.0, 1.0], [0.0, 1.0, 0.0, 1.0], [1.0, 0.0, 0.0, 1.0]]); - triangle.get_buffer(BufferUsage::INDEX, 0).unwrap().view_mut::<[u32; 3]>().unwrap() + triangle + .get_buffer(BufferUsage::VERTEX, 0) + .unwrap() + .view_mut::<[f32; 3]>() + .unwrap() + .copy_from_slice(&[[-1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0]]); + triangle + .get_buffer(BufferUsage::INDEX, 0) + .unwrap() + .view_mut::<[u32; 3]>() + .unwrap() .copy_from_slice(&[[0, 1, 2]]); triangle.commit(); diff --git a/src/buffer.rs b/src/buffer.rs index 3d0ea8b50..e7a32c926 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -53,7 +53,13 @@ impl Buffer { pub fn handle(&self) -> RTCBuffer { self.handle } - /// Returns the a slice of the buffer. + /// 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. pub fn slice>(&self, bounds: S) -> BufferSlice { let start = match bounds.start_bound() { Bound::Included(&n) => n, @@ -76,10 +82,10 @@ impl Buffer { /// /// # Arguments /// - /// * `range` - The range of indices to slice into the buffer. + /// * `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<'a, S: RangeBounds, T>(&'a self, bounds: S) -> BufferView<'a, T> { + 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()); @@ -87,6 +93,12 @@ impl Buffer { } /// 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, diff --git a/src/device.rs b/src/device.rs index 90753ab0f..4a3905fee 100644 --- a/src/device.rs +++ b/src/device.rs @@ -214,7 +214,8 @@ impl Device { Buffer::new(self, BufferSize::new(size).unwrap()) } - /// Creates a [`Geometry`] object bound to the device. + /// Creates a [`Geometry`] object bound to the device without any + /// buffers attached. pub fn create_geometry(&self) -> Result { G::new(self) } } diff --git a/src/geometry.rs b/src/geometry.rs index 486c65e12..3f391d7a4 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -77,7 +77,8 @@ impl BufferGeometry { /// /// * `slice` - The buffer slice to bind. /// - /// * `stride` - The stride of the elements in the buffer. + /// * `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( @@ -89,6 +90,7 @@ impl BufferGeometry { stride: usize, count: usize, ) -> Result<(), Error> { + debug_assert!(stride % 4 == 0, "Stride must be a multiple of 4!"); match slice { BufferSlice::Created { buffer, @@ -144,7 +146,8 @@ impl BufferGeometry { /// 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. + /// 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. @@ -160,7 +163,7 @@ impl BufferGeometry { /// /// * `count` - The number of items in the buffer. /// - /// * `stride` - The stride of the buffer items. MUST be aligned to 4 bytes. + /// * `stride` - The stride of the buffer items. MUST be a multiple of 4. pub fn set_new_buffer( &mut self, usage: BufferUsage, @@ -169,6 +172,7 @@ impl BufferGeometry { stride: usize, count: usize, ) -> Result<(), Error> { + debug_assert!(stride % 4 == 0, "Stride must be a multiple of 4!"); let bindings = self.attachments.entry(usage).or_insert_with(Vec::new); if bindings.iter().find(|a| a.slot == slot).is_none() { let raw_ptr = unsafe { @@ -184,14 +188,13 @@ impl BufferGeometry { if raw_ptr.is_null() { Err(self.device.get_error()) } else { - let buffer_slice = BufferSlice::Managed { - ptr: raw_ptr, - size: NonZeroUsize::new(count * stride as usize).unwrap(), - marker: std::marker::PhantomData, - }; bindings.push(AttachedBuffer { slot, - buffer: buffer_slice, + buffer: BufferSlice::Managed { + ptr: raw_ptr, + size: NonZeroUsize::new(count * stride as usize).unwrap(), + marker: std::marker::PhantomData, + }, format, stride, }); diff --git a/src/geometry/triangle_mesh.rs b/src/geometry/triangle_mesh.rs index 12f9c949c..a056ca0ba 100644 --- a/src/geometry/triangle_mesh.rs +++ b/src/geometry/triangle_mesh.rs @@ -15,16 +15,23 @@ impl DerefMut for TriangleMesh { } impl TriangleMesh { + /// Creates a new triangle mesh geometry with the given number of triangles + /// and vertices. + /// + /// The geometry is unanimated, and the vertex and index buffers are + /// allocated but not initialized. The vertex buffer is in + /// [`Format::FLOAT3`] format, and the index buffer is in + /// [`Format::UINT3`] format, and are 16-byte and 4-byte aligned, + /// respectively. They are bound to the first slot of their respective + /// buffers. pub fn unanimated(device: &Device, num_tris: usize, num_verts: usize) -> TriangleMesh { let mut geometry = BufferGeometry::new(device, GeometryType::TRIANGLE).unwrap(); - geometry - .set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, num_verts) + .set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 12, num_verts) .unwrap(); geometry .set_new_buffer(BufferUsage::INDEX, 0, Format::UINT3, 12, num_tris) .unwrap(); - Self(geometry) } } diff --git a/src/lib.rs b/src/lib.rs index a8c3b9c5f..7b1a6efd9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,10 @@ pub use sys::{ RTCIntersectContextFlags as IntersectContextFlags, RTCSceneFlags as SceneFlags, }; +/// The invalid ID for Embree intersection results (e.g. `Hit::geomID`, +/// `Hit::primID`, etc.) +pub const INVALID_ID: u32 = u32::MAX; + /// Utility for making specifically aligned vectors pub fn aligned_vector(len: usize, align: usize) -> Vec { let t_size = mem::size_of::(); diff --git a/src/ray.rs b/src/ray.rs index 02d1d390e..9e8576a58 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -1,6 +1,4 @@ -use std::{f32, u32}; - -use crate::sys; +use crate::{sys, INVALID_ID}; pub type Ray = sys::RTCRay; pub type Hit = sys::RTCHit; @@ -11,6 +9,7 @@ impl Ray { 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 { org_x: origin[0], @@ -30,26 +29,30 @@ impl Ray { } impl Hit { - pub fn new() -> Hit { - sys::RTCHit { + /// Returns true if the hit is valid (i.e. the ray hit something). + pub fn is_valid(&self) -> bool { self.geomID != INVALID_ID } +} + +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 RayHit { pub fn new(ray: Ray) -> RayHit { sys::RTCRayHit { ray, - hit: Hit::new(), + hit: Hit::default(), } } } diff --git a/src/scene.rs b/src/scene.rs index 5885ad842..4865174f4 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -155,14 +155,24 @@ impl Scene { } } - pub fn intersect(&self, ctx: &mut IntersectContext, ray: &mut RayHit) { + /// Finds the closest hit of a single ray with the scene. + /// + /// Analogous to [`rtcIntersect1`]. + /// + /// # Arguments + /// + /// * `ctx` - The intersection context to use for the ray query. + /// * `ray` - The ray to intersect with the scene. + pub fn intersect(&self, ctx: &mut IntersectContext, ray: Ray) -> RayHit { + let mut ray_hit = RayHit::new(ray); unsafe { rtcIntersect1( self.handle, ctx as *mut RTCIntersectContext, - ray as *mut RTCRayHit, + &mut ray_hit as *mut RTCRayHit, ); } + ray_hit } pub fn occluded(&self, ctx: &mut IntersectContext, ray: &mut Ray) { From 074c6b5a93ed19015378e7494cf803c1285d9fe2 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Tue, 14 Feb 2023 15:47:06 +0100 Subject: [PATCH 19/65] update CI embree version --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a5b4e8910..f92cc84b6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,7 +2,7 @@ name: CI on: [push, pull_request] env: CARGO_TERM_COLOR: always - EMBREE_VERSION: 3.12.1 + EMBREE_VERSION: 3.13.5 jobs: build_linux: runs-on: ubuntu-latest From a4f0802ba597641dc5b392eeb3cdc09a35c22214 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Tue, 14 Feb 2023 18:25:22 +0100 Subject: [PATCH 20/65] try to include set shared buffer --- examples/triangle/src/main.rs | 8 +- examples/triangle_geometry/Cargo.toml | 1 - examples/triangle_geometry/src/main.rs | 117 +++++++++------- src/buffer.rs | 26 ++-- src/geometry.rs | 187 ++++++++++++++++++------- src/geometry/quad_mesh.rs | 100 ++++++------- src/geometry/triangle_mesh.rs | 13 +- 7 files changed, 276 insertions(+), 176 deletions(-) diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index 183d7d356..79c3180f1 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -22,9 +22,13 @@ fn main() { triangle .get_buffer(BufferUsage::VERTEX, 0) .unwrap() - .view_mut::<[f32; 3]>() + .view_mut::<[f32; 4]>() .unwrap() - .copy_from_slice(&[[-1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0]]); + .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 .get_buffer(BufferUsage::INDEX, 0) .unwrap() diff --git a/examples/triangle_geometry/Cargo.toml b/examples/triangle_geometry/Cargo.toml index 843abbdcd..344044926 100644 --- a/examples/triangle_geometry/Cargo.toml +++ b/examples/triangle_geometry/Cargo.toml @@ -7,5 +7,4 @@ edition = "2021" [dependencies] embree = { path = "../../" } support = { path = "../support" } -cgmath = "0.18.0" diff --git a/examples/triangle_geometry/src/main.rs b/examples/triangle_geometry/src/main.rs index 6ab0acad2..b7baf3ca0 100644 --- a/examples/triangle_geometry/src/main.rs +++ b/examples/triangle_geometry/src/main.rs @@ -5,72 +5,89 @@ extern crate embree; extern crate support; use cgmath::{Vector3, Vector4}; -use embree::{Device, Geometry, IntersectContext, QuadMesh, Ray, RayHit, Scene, TriangleMesh}; +use embree::{ + BufferUsage, Device, Geometry, GeometryVertexAttribute, IntersectContext, QuadMesh, Ray, + RayHit, Scene, TriangleMesh, +}; +use std::sync::Arc; use support::Camera; -fn make_cube<'a>(device: &'a Device) -> Geometry<'a> { +fn make_cube(device: &Device) -> Arc { let mut mesh = TriangleMesh::unanimated(device, 12, 8); { - 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.get_buffer(BufferUsage::VERTEX, 0) + .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.get_buffer(BufferUsage::INDEX, 0) + .unwrap() + .view_mut::<[u32; 3]>() + .unwrap() + .copy_from_slice(&[ + // left side + [0, 2, 1], + [1, 2, 3], + // right side + [4, 5, 6], + [5, 7, 6], + // bottom side + [0, 1, 4], + [1, 5, 4], + // top side + [2, 6, 3], + [3, 6, 7], + // front side + [0, 4, 2], + [2, 4, 6], + // back side + [1, 3, 5], + [3, 7, 5], + ]); } let mut mesh = Geometry::Triangle(mesh); mesh.commit(); - mesh + Arc::new(mesh) } -fn make_ground_plane<'a>(device: &'a Device) -> Geometry<'a> { + +fn make_ground_plane(device: &Device) -> Arc { let mut mesh = QuadMesh::unanimated(device, 1, 4); { - 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.get_buffer(BufferUsage::VERTEX, 0) + .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.get_buffer(BufferUsage::INDEX, 0) + .unwrap() + .view_mut::<[u32; 4]>() + .unwrap() + .copy_from_slice(&[[0, 1, 2, 3]]); } - let mut mesh = Geometry::Quad(mesh); + mesh.set_vertex_attribute_count(1); + // mesh.set_shared mesh.commit(); - mesh + Arc::new(mesh) } fn main() { let mut display = support::Display::new(512, 512, "triangle geometry"); - let device = Device::new(); + let device = Device::new().unwrap(); let cube = make_cube(&device); let ground = make_ground_plane(&device); diff --git a/src/buffer.rs b/src/buffer.rs index e7a32c926..af0f89abc 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -71,7 +71,7 @@ impl Buffer { Bound::Excluded(&n) => n, Bound::Unbounded => self.size.get(), }; - BufferSlice::Created { + BufferSlice::External { buffer: self.clone(), offset: start, size: NonZeroUsize::new(end - start).unwrap(), @@ -134,13 +134,15 @@ pub struct BufferViewMut<'a, T: 'a> { marker: PhantomData<&'a mut T>, } -/// Slice into a [`Buffer`]. +/// Slice into a region of memory managed by Embree. This can either be a +/// slice to a [`Buffer`] or a slice to memory managed by Embree (mostly created +/// from [`rtcSetNewGeometryBuffer`]). /// -/// Created with [`Buffer::slice`]. +/// Can be created with [`Buffer::slice`] or [`Geometry::set_new_buffer`]. #[derive(Debug, Clone)] pub enum BufferSlice { - /// Slice created from a [`Buffer`]. - Created { + /// Slice created from a [`Buffer`] object. + External { /// The buffer this slice is a part of. buffer: Buffer, /// The offset into the buffer in bytes. @@ -149,7 +151,7 @@ pub enum BufferSlice { size: BufferSize, }, /// Slice managed by Embree internally. - Managed { + Internal { ptr: *mut ::std::os::raw::c_void, size: BufferSize, marker: PhantomData<*mut ::std::os::raw::c_void>, @@ -159,7 +161,7 @@ pub enum BufferSlice { impl BufferSlice { pub fn view(&self) -> Result, Error> { match self { - BufferSlice::Created { + BufferSlice::External { buffer, offset, size, @@ -171,7 +173,7 @@ impl BufferSlice { marker: PhantomData, }) } - BufferSlice::Managed { ptr, size, .. } => { + BufferSlice::Internal { ptr, size, .. } => { debug_assert!( size.get() % mem::size_of::() == 0, "Size of the range of the mapped buffer must be multiple of T!" @@ -189,7 +191,7 @@ impl BufferSlice { pub fn view_mut(&self) -> Result, Error> { match self { - BufferSlice::Created { + BufferSlice::External { buffer, offset, size, @@ -198,7 +200,7 @@ impl BufferSlice { mapped: BufferMappedRange::from_buffer(buffer, *offset, size.get())?, marker: PhantomData, }), - BufferSlice::Managed { ptr, size, .. } => { + BufferSlice::Internal { ptr, size, .. } => { debug_assert!( size.get() % mem::size_of::() == 0, "Size of the range of the mapped buffer must be multiple of T!" @@ -224,7 +226,7 @@ impl<'a, T> BufferView<'a, T> { size: BufferSize, ) -> Result, Error> { Ok(BufferView { - range: BufferSlice::Created { + range: BufferSlice::External { buffer: buffer.clone(), offset, size, @@ -244,7 +246,7 @@ impl<'a, T> BufferViewMut<'a, T> { size: BufferSize, ) -> Result, Error> { Ok(BufferViewMut { - range: BufferSlice::Created { + range: BufferSlice::External { buffer: buffer.clone(), offset, size, diff --git a/src/geometry.rs b/src/geometry.rs index 3f391d7a4..e6befdbf6 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,9 +1,11 @@ -use std::{collections::HashMap, num::NonZeroUsize}; +use std::{borrow::Cow, collections::HashMap, num::NonZeroUsize}; use crate::{sys::*, BufferSlice, BufferUsage, Device, Error, Format, GeometryType}; +mod quad_mesh; mod triangle_mesh; +pub use quad_mesh::QuadMesh; pub use triangle_mesh::TriangleMesh; pub trait Geometry { @@ -19,12 +21,39 @@ pub trait Geometry { } } +/// A trait for geometry objects that can have vertex attributes. +/// +/// Only supported by triangle meshes, quad meshes, curves, points, and +/// subdivision geometries. +pub trait GeometryVertexAttribute: Geometry { + /// 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. + /// + /// # Arguments + /// + /// * `count` - The number of vertex attribute slots. + fn set_vertex_attribute_count(&self, count: u32) { + unsafe { + rtcSetGeometryVertexAttributeCount(self.handle(), count); + } + } +} + #[derive(Debug)] -pub(crate) struct AttachedBuffer { - pub slot: u32, - pub buffer: BufferSlice, - pub format: Format, - pub stride: usize, +pub(crate) struct AttachedBuffer<'a: 'static> { + slot: u32, + format: Format, + stride: usize, + source: BufferSource<'a>, +} + +#[derive(Debug)] +enum BufferSource<'a> { + EmbreeManaged(BufferSlice), + UserManaged(Cow<'a, [u8]>) } /// Handle to an Embree geometry object. @@ -33,14 +62,14 @@ pub(crate) struct AttachedBuffer { /// own the buffers that are bound to it, but it does own the geometry object /// itself. #[derive(Debug)] -pub struct BufferGeometry { +pub struct BufferGeometry<'buf: 'static> { pub(crate) device: Device, pub(crate) handle: RTCGeometry, pub(crate) kind: GeometryType, - pub(crate) attachments: HashMap>, + pub(crate) attachments: HashMap>>, } -impl<'a> Drop for BufferGeometry { +impl<'buf: 'static> Drop for BufferGeometry<'buf> { fn drop(&mut self) { unsafe { rtcReleaseGeometry(self.handle); @@ -48,8 +77,8 @@ impl<'a> Drop for BufferGeometry { } } -impl BufferGeometry { - pub(crate) fn new(device: &Device, kind: GeometryType) -> Result { +impl<'device, 'buf: 'static> BufferGeometry<'buf> { + pub(crate) fn new(device: &'device Device, kind: GeometryType) -> Result, Error> { let handle = unsafe { rtcNewGeometry(device.handle, kind) }; if handle.is_null() { Err(device.get_error()) @@ -65,6 +94,9 @@ impl BufferGeometry { /// Binds a view of a buffer to the geometry. /// + /// After successful completion of this function, the geometry will hold + /// a reference to the buffer object. + /// /// Analogous to [`rtcSetGeometryBuffer`](https://spec.oneapi.io/oneart/0.5-rev-1/embree-spec.html#rtcsetgeometrybuffer). /// /// # Arguments @@ -92,47 +124,76 @@ impl BufferGeometry { ) -> Result<(), Error> { debug_assert!(stride % 4 == 0, "Stride must be a multiple of 4!"); match slice { - BufferSlice::Created { + BufferSlice::External { buffer, offset, size, } => { - let mut bindings = self.attachments.entry(usage).or_insert_with(Vec::new); - if bindings.iter().find(|a| a.slot == slot).is_none() { - println!( - "Binding buffer to slot {}, offset {}, stride {}, count {}", - slot, offset, stride, count - ); - unsafe { - rtcSetGeometryBuffer( - self.handle, - usage, + let bindings = self.attachments.entry(usage).or_insert_with(Vec::new); + println!( + "Binding buffer to slot {}, offset {}, stride {}, count {}", + slot, offset, stride, count + ); + match bindings.iter().position(|a| a.slot == slot) { + Some(i) => { + eprint!( + "Buffer already attached to slot {}, will be overwritten!", + slot + ); + bindings.remove(i); + unsafe { + rtcSetGeometryBuffer( + self.handle, + usage, + slot, + format, + buffer.handle, + offset, + stride as usize, + count as usize, + ) + }; + bindings.push(AttachedBuffer { + slot, + source: BufferSource::EmbreeManaged(BufferSlice::External { + buffer, + offset, + size, + }), + format, + stride, + }); + Ok(()) + } + None => { + unsafe { + rtcSetGeometryBuffer( + self.handle, + usage, + slot, + format, + buffer.handle, + offset, + stride as usize, + count as usize, + ) + }; + bindings.push(AttachedBuffer { slot, + source: BufferSource::EmbreeManaged(BufferSlice::External { + buffer, + offset, + size, + }), format, - buffer.handle, - offset, - stride as usize, - count as usize, - ) - }; - bindings.push(AttachedBuffer { - slot, - buffer: BufferSlice::Created { - buffer, - offset, - size, - }, - format, - stride, - }); - Ok(()) - } else { - eprint!("Buffer already attached to slot {}", slot); - Err(Error::INVALID_ARGUMENT) + stride, + }); + Ok(()) + } } } - BufferSlice::Managed { .. } => { - eprint!("Internal buffer cannot be shared!"); + BufferSlice::Internal { .. } => { + eprint!("Internally managed buffer cannot be shared!"); Err(Error::INVALID_ARGUMENT) } } @@ -190,11 +251,11 @@ impl BufferGeometry { } else { bindings.push(AttachedBuffer { slot, - buffer: BufferSlice::Managed { + source: BufferSource::EmbreeManaged(BufferSlice::Internal { ptr: raw_ptr, size: NonZeroUsize::new(count * stride as usize).unwrap(), marker: std::marker::PhantomData, - }, + }), format, stride, }); @@ -208,10 +269,17 @@ impl BufferGeometry { /// Returns the buffer bound to the given slot and usage. pub fn get_buffer(&self, usage: BufferUsage, slot: u32) -> Option<&BufferSlice> { - self.attachments + let attachment = self.attachments .get(&usage) - .and_then(|v| v.iter().find(|a| a.slot == slot)) - .map(|a| &a.buffer) + .and_then(|v| v.iter().find(|a| a.slot == slot)); + if let Some(attachment) = attachment { + match attachment.source { + BufferSource::EmbreeManaged(ref slice) => Some(slice), + BufferSource::UserManaged(_) => None, + } + } else { + None + } } pub fn commit(&mut self) { @@ -244,7 +312,7 @@ impl BufferGeometry { /// * [`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) { + pub fn set_subdivision_mode(&self, topology_id: u32, mode: RTCSubdivisionMode) { unsafe { rtcSetGeometrySubdivisionMode(self.handle, topology_id, mode); } @@ -266,9 +334,26 @@ impl BufferGeometry { /// 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) { + pub fn set_vertex_attribute_topology(&self, vertex_attribute_id: u32, topology_id: u32) { unsafe { rtcSetGeometryVertexAttributeTopology(self.handle, vertex_attribute_id, topology_id); } } + + // /// Assigns a view of a shared data buffer to a geometry. + // pub fn set_shared_buffer(&mut self, usage: BufferUsage, slot: u32, buffer: + // &[u8], ) -> Result<(), Error> { let bindings = + // self.attachments.entry(usage).or_insert_with(Vec::new); if bindings. + // iter().find(|a| a.slot == slot).is_none() { bindings. + // push(AttachedBuffer { slot, + // buffer, + // format: Format::FLOAT3, + // stride: 0, + // }); + // Ok(()) + // } else { + // eprint!("Buffer already attached to slot {}", slot); + // Err(Error::INVALID_ARGUMENT) + // } + // } } diff --git a/src/geometry/quad_mesh.rs b/src/geometry/quad_mesh.rs index 1e3291deb..b582f1eed 100644 --- a/src/geometry/quad_mesh.rs +++ b/src/geometry/quad_mesh.rs @@ -1,67 +1,55 @@ -use std::sync::Arc; +use crate::{ + sys, BufferGeometry, BufferUsage, Device, Error, Format, Geometry, GeometryType, + GeometryVertexAttribute, +}; +use std::ops::{Deref, DerefMut}; -use crate::buffer::Buffer; -use crate::device::Device; -use crate::geometry::GeometryTrait; -use crate::sys::*; -use crate::{BufferType, Format, GeometryType}; +#[derive(Debug)] +pub struct QuadMesh(BufferGeometry<'static>); -pub struct QuadMesh { - device: Device, - pub(crate) handle: RTCGeometry, - pub vertex_buffer: Buffer<[f32; 4]>, - pub index_buffer: Buffer<[u32; 4]>, -} +impl Deref for QuadMesh { + type Target = BufferGeometry<'static>; -impl QuadMesh { - pub fn unanimated(device: Device, 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); + fn deref(&self) -> &Self::Target { &self.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 DerefMut for QuadMesh { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } -impl GeometryTrait for QuadMesh { - fn handle(&self) -> RTCGeometry { - self.handle +impl QuadMesh { + /// Creates a new quad mesh geometry with the given number of quads and + /// vertices. + /// + /// The geometry is unanimated, and the vertex and index buffers are + /// allocated but not initialized. The vertex buffer is in + /// [`Format::FLOAT3`] format, the index buffer is in + /// [`Format::UINT4`] format. They are 16-byte and 4-byte aligned + /// respectively, and are bound to the first slot of their respective + /// buffers. + pub fn unanimated<'a>(device: &'a Device, num_quads: usize, num_verts: usize) -> QuadMesh { + let mut geometry = BufferGeometry::new(device, GeometryType::QUAD).unwrap(); + geometry + .set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, num_verts) + .unwrap(); + geometry + .set_new_buffer(BufferUsage::INDEX, 0, Format::UINT4, 16, num_quads) + .unwrap(); + Self(geometry) } } -impl Drop for QuadMesh { - fn drop(&mut self) { - unsafe { - rtcReleaseGeometry(self.handle); - } +impl Geometry for QuadMesh { + fn new(device: &Device) -> Result + where + Self: Sized, + { + Ok(Self(BufferGeometry::new(device, GeometryType::QUAD)?)) } + + fn kind(&self) -> GeometryType { GeometryType::QUAD } + + fn handle(&self) -> sys::RTCGeometry { self.handle } } + +impl GeometryVertexAttribute for QuadMesh {} diff --git a/src/geometry/triangle_mesh.rs b/src/geometry/triangle_mesh.rs index a056ca0ba..6802a16ed 100644 --- a/src/geometry/triangle_mesh.rs +++ b/src/geometry/triangle_mesh.rs @@ -1,11 +1,14 @@ -use crate::{sys::*, BufferGeometry, BufferUsage, Device, Format, Geometry, GeometryType}; +use crate::{ + sys::*, BufferGeometry, BufferUsage, Device, Format, Geometry, GeometryType, + GeometryVertexAttribute, +}; use std::ops::{Deref, DerefMut}; #[derive(Debug)] -pub struct TriangleMesh(BufferGeometry); +pub struct TriangleMesh(BufferGeometry<'static>); impl Deref for TriangleMesh { - type Target = BufferGeometry; + type Target = BufferGeometry<'static>; fn deref(&self) -> &Self::Target { &self.0 } } @@ -27,7 +30,7 @@ impl TriangleMesh { pub fn unanimated(device: &Device, num_tris: usize, num_verts: usize) -> TriangleMesh { let mut geometry = BufferGeometry::new(device, GeometryType::TRIANGLE).unwrap(); geometry - .set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 12, num_verts) + .set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, num_verts) .unwrap(); geometry .set_new_buffer(BufferUsage::INDEX, 0, Format::UINT3, 12, num_tris) @@ -48,3 +51,5 @@ impl Geometry for TriangleMesh { fn handle(&self) -> RTCGeometry { self.handle } } + +impl GeometryVertexAttribute for TriangleMesh {} From 3d3406855bee0c1b861ebf33c906c82208409150 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Wed, 15 Feb 2023 17:56:42 +0100 Subject: [PATCH 21/65] refactor buffer & slice --- examples/minimal/src/main.rs | 22 +- examples/support/src/display.rs | 31 ++- examples/triangle/src/main.rs | 4 +- examples/triangle_geometry/Cargo.toml | 1 + examples/triangle_geometry/src/main.rs | 117 ++++++---- src/buffer.rs | 204 ++++++++++------- src/device.rs | 6 +- src/geometry.rs | 296 ++++++++++++++++--------- src/geometry/quad_mesh.rs | 9 +- src/geometry/triangle_mesh.rs | 9 +- src/ray.rs | 43 +++- src/ray_packet.rs | 6 +- src/scene.rs | 8 +- 13 files changed, 484 insertions(+), 272 deletions(-) diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 65532cced..5c1c2fa28 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -45,20 +45,6 @@ fn main() { 3 * std::mem::size_of::(), 3, ) - .unwrap(); - triangle - .set_new_buffer( - BufferUsage::INDEX, - 0, - Format::UINT3, - 3 * std::mem::size_of::(), - 1, - ) - .unwrap(); - - // Fill the vertex and index buffers with the triangle data. - triangle - .get_buffer(BufferUsage::VERTEX, 0) .unwrap() .view_mut::<[f32; 3]>() .unwrap() @@ -68,7 +54,13 @@ fn main() { [0.0, 1.0, 0.0], // vertex 2 ]); triangle - .get_buffer(BufferUsage::INDEX, 0) + .set_new_buffer( + BufferUsage::INDEX, + 0, + Format::UINT3, + 3 * std::mem::size_of::(), + 1, + ) .unwrap() .view_mut::<[u32; 3]>() .unwrap() diff --git a/examples/support/src/display.rs b/examples/support/src/display.rs index f9dee2e94..8c82c7340 100644 --- a/examples/support/src/display.rs +++ b/examples/support/src/display.rs @@ -9,7 +9,7 @@ use image::RgbaImage; use wgpu; use winit::{ dpi::{LogicalSize, Size}, - event::{Event, VirtualKeyCode, WindowEvent}, + event::{ElementState, Event, MouseButton, MouseScrollDelta, VirtualKeyCode, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::{Window, WindowBuilder}, }; @@ -293,12 +293,11 @@ where a: 1.0, }; - let mut mouse_pressed = [false, false]; - //let mut prev_mouse = None; + 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(); 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, @@ -307,6 +306,30 @@ where { *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_camera.rotate(mouse_prev, mouse_cur); + } + if mouse_pressed[2] { + arcball_camera.pan(mouse_cur - mouse_prev); + } + mouse_prev = mouse_cur; + } + WindowEvent::MouseWheel { delta, .. } => match delta { + MouseScrollDelta::LineDelta(_, y) => { + arcball_camera.zoom(y, 0.1); + } + MouseScrollDelta::PixelDelta(pos) => { + arcball_camera.zoom(pos.y as f32, 0.01); + } + }, _ => (), }, Event::MainEventsCleared => { diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index 79c3180f1..6a2a3eac3 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -3,9 +3,7 @@ extern crate embree; extern crate support; -use embree::{ - BufferUsage, Device, Format, Geometry, IntersectContext, RayHitN, RayN, TriangleMesh, -}; +use embree::{BufferUsage, Device, Geometry, IntersectContext, RayHitN, RayN, TriangleMesh}; use std::sync::Arc; fn main() { diff --git a/examples/triangle_geometry/Cargo.toml b/examples/triangle_geometry/Cargo.toml index 344044926..e1aaff6dd 100644 --- a/examples/triangle_geometry/Cargo.toml +++ b/examples/triangle_geometry/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Will Usher "] edition = "2021" [dependencies] +glam = "0.22.0" embree = { path = "../../" } support = { path = "../support" } diff --git a/examples/triangle_geometry/src/main.rs b/examples/triangle_geometry/src/main.rs index b7baf3ca0..54e12644e 100644 --- a/examples/triangle_geometry/src/main.rs +++ b/examples/triangle_geometry/src/main.rs @@ -1,21 +1,19 @@ #![allow(dead_code)] -extern crate cgmath; extern crate embree; extern crate support; -use cgmath::{Vector3, Vector4}; use embree::{ - BufferUsage, Device, Geometry, GeometryVertexAttribute, IntersectContext, QuadMesh, Ray, - RayHit, Scene, TriangleMesh, + BufferSlice, BufferUsage, Device, Format, Geometry, IntersectContext, QuadMesh, Ray, + TriangleMesh, }; use std::sync::Arc; use support::Camera; -fn make_cube(device: &Device) -> Arc { - let mut mesh = TriangleMesh::unanimated(device, 12, 8); +fn make_cube(device: &Device, vertex_colors: &[[f32; 3]]) -> TriangleMesh { + let mut mesh = TriangleMesh::new(device).unwrap(); { - mesh.get_buffer(BufferUsage::VERTEX, 0) + mesh.set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 12, 8) .unwrap() .view_mut::<[f32; 3]>() .unwrap() @@ -29,8 +27,7 @@ fn make_cube(device: &Device) -> Arc { [1.0, 1.0, -1.0], [1.0, 1.0, 1.0], ]); - - mesh.get_buffer(BufferUsage::INDEX, 0) + mesh.set_new_buffer(BufferUsage::INDEX, 0, Format::UINT3, 12, 12) .unwrap() .view_mut::<[u32; 3]>() .unwrap() @@ -54,13 +51,23 @@ fn make_cube(device: &Device) -> Arc { [1, 3, 5], [3, 7, 5], ]); + + 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(); - Arc::new(mesh) + mesh } -fn make_ground_plane(device: &Device) -> Arc { +fn make_ground_plane(device: &Device) -> QuadMesh { let mut mesh = QuadMesh::unanimated(device, 1, 4); { mesh.get_buffer(BufferUsage::VERTEX, 0) @@ -79,42 +86,53 @@ fn make_ground_plane(device: &Device) -> Arc { .unwrap() .copy_from_slice(&[[0, 1, 2, 3]]); } - mesh.set_vertex_attribute_count(1); - // mesh.set_shared mesh.commit(); - Arc::new(mesh) + mesh } fn main() { - let mut display = support::Display::new(512, 512, "triangle geometry"); + let display = support::Display::new(512, 512, "triangle geometry"); let device = Device::new().unwrap(); - let cube = make_cube(&device); - let ground = make_ground_plane(&device); - // TODO: Support for Embree3's new vertex attributes + 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 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), + [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], ]; - let mut scene = Scene::new(&device); - scene.attach_geometry(cube); + let cube = Arc::new(make_cube(&device, &vertex_colors)); + let ground = Arc::new(make_ground_plane(&device)); + + let mut scene = device.create_scene().unwrap(); + let _ = scene.attach_geometry(cube); let ground_id = scene.attach_geometry(ground); - let rtscene = scene.commit(); + scene.commit(); let mut intersection_ctx = IntersectContext::coherent(); - display.run(|image, camera_pose, _| { + let light_dir = glam::vec3(1.0, 1.0, 1.0).normalize(); + + support::display::run(display, move |image, camera_pose, _| { for p in image.iter_mut() { *p = 0; } @@ -130,16 +148,29 @@ fn main() { 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) + let ray_hit = scene.intersect( + &mut intersection_ctx, + Ray::new(camera.pos.into(), dir.into()), + ); + if ray_hit.is_valid() { + let p = image.get_pixel_mut(i, j); + let diffuse = if ray_hit.hit.geomID == ground_id { + glam::vec3(0.6, 0.6, 0.6) + } else { + glam::Vec3::from(face_colors[ray_hit.hit.primID as usize]) + }; + + let mut shadow_ray = + Ray::segment(ray_hit.hit_point(), light_dir.into(), 0.001, f32::INFINITY); + + // Check if the shadow ray is occluded. + let color = if !scene.occluded(&mut intersection_ctx, &mut shadow_ray) { + diffuse } else { - face_colors[ray_hit.hit.primID as usize] + diffuse * 0.5 }; + + // Write the color to the image. p[0] = (color.x * 255.0) as u8; p[1] = (color.y * 255.0) as u8; p[2] = (color.z * 255.0) as u8; diff --git a/src/buffer.rs b/src/buffer.rs index af0f89abc..9441dda9e 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -23,9 +23,9 @@ impl Clone for Buffer { fn clone(&self) -> Self { unsafe { rtcRetainBuffer(self.handle) }; Buffer { + device: self.device.clone(), handle: self.handle, size: self.size, - device: self.device.clone(), } } } @@ -51,7 +51,14 @@ impl Buffer { } } - pub fn handle(&self) -> RTCBuffer { self.handle } + /// Returns the raw handle to the buffer. + /// + /// # Safety + /// + /// The handle returned by this function is a raw pointer to the + /// underlying Embree buffer. It is not safe to use this handle + /// outside of the Embree API. + pub unsafe fn handle(&self) -> RTCBuffer { self.handle } /// Returns a slice of the buffer. /// @@ -60,21 +67,18 @@ impl Buffer { /// [`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 start = match bounds.start_bound() { - Bound::Included(&n) => n, - Bound::Excluded(&n) => n + 1, - Bound::Unbounded => 0, - }; - let end = match bounds.end_bound() { - Bound::Included(&n) => n + 1, - Bound::Excluded(&n) => n, - Bound::Unbounded => self.size.get(), - }; - BufferSlice::External { - buffer: self.clone(), - offset: start, - size: NonZeroUsize::new(end - start).unwrap(), + 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(), } } @@ -120,56 +124,83 @@ impl Drop for Buffer { /// A read-only view into mapped buffer. #[derive(Debug)] -pub struct BufferView<'a, T: 'a> { - range: BufferSlice, - mapped: BufferMappedRange<'a, T>, - marker: PhantomData<&'a T>, +pub struct BufferView<'buf, T: 'buf> { + mapped: BufferMappedRange<'buf, T>, + marker: PhantomData<&'buf T>, } /// A write-only view into mapped buffer. #[derive(Debug)] -pub struct BufferViewMut<'a, T: 'a> { - range: BufferSlice, - mapped: BufferMappedRange<'a, T>, - marker: PhantomData<&'a mut T>, +pub struct BufferViewMut<'buf, T: 'buf> { + mapped: BufferMappedRange<'buf, T>, + marker: PhantomData<&'buf mut T>, } -/// Slice into a region of memory managed by Embree. This can either be a -/// slice to a [`Buffer`] or a slice to memory managed by Embree (mostly created -/// from [`rtcSetNewGeometryBuffer`]). -/// -/// Can be created with [`Buffer::slice`] or [`Geometry::set_new_buffer`]. -#[derive(Debug, Clone)] -pub enum BufferSlice { - /// Slice created from a [`Buffer`] object. - External { - /// The buffer this slice is a part of. - buffer: Buffer, - /// The offset into the buffer in bytes. +/// 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 memory. +#[derive(Debug, Clone, Copy)] +pub enum BufferSlice<'src> { + /// Slice into a [`Buffer`] object. + Buffer { + buffer: &'src Buffer, offset: usize, - /// The size of the slice in bytes. size: BufferSize, }, - /// Slice managed by Embree internally. + /// Slice into memory managed by Embree. Internal { ptr: *mut ::std::os::raw::c_void, size: BufferSize, - marker: PhantomData<*mut ::std::os::raw::c_void>, + 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 BufferSlice { - pub fn view(&self) -> Result, Error> { +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, S: RangeBounds>(slice: &'slice [T], bounds: S) -> Self { + let (first, count) = range_bounds_to_offset_and_size(bounds); + let count = count.unwrap_or_else(|| 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::External { + BufferSlice::Buffer { buffer, offset, size, } => { - let slice = BufferMappedRange::from_buffer(buffer, *offset, size.get())?; + let mapped = BufferMappedRange::from_buffer(buffer, *offset, size.get())?; Ok(BufferView { - range: self.clone(), - mapped: slice, + // slice: *self, + mapped, marker: PhantomData, }) } @@ -179,24 +210,39 @@ impl BufferSlice { "Size of the range of the mapped buffer must be multiple of T!" ); let len = size.get() / mem::size_of::(); - let slice = unsafe { BufferMappedRange::from_raw_parts(*ptr as *mut T, len) }; + let mapped = unsafe { BufferMappedRange::from_raw_parts(*ptr as *mut T, len) }; Ok(BufferView { - range: self.clone(), - mapped: slice, + //slice: *self, + mapped, + marker: PhantomData, + }) + } + BufferSlice::User { + ptr, offset, size, .. + } => { + // TODO(yang): should we allow this? + 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.add(*offset) as *mut T, len) }; + Ok(BufferView { + mapped, marker: PhantomData, }) } } } - pub fn view_mut(&self) -> Result, Error> { + pub fn view_mut(&self) -> Result, Error> { match self { - BufferSlice::External { + BufferSlice::Buffer { buffer, offset, size, } => Ok(BufferViewMut { - range: self.clone(), mapped: BufferMappedRange::from_buffer(buffer, *offset, size.get())?, marker: PhantomData, }), @@ -206,10 +252,25 @@ impl BufferSlice { "Size of the range of the mapped buffer must be multiple of T!" ); let len = size.get() / mem::size_of::(); - let slice = unsafe { BufferMappedRange::from_raw_parts(*ptr as *mut T, len) }; + let mapped = unsafe { BufferMappedRange::from_raw_parts(*ptr as *mut T, len) }; + Ok(BufferViewMut { + mapped, + marker: PhantomData, + }) + } + BufferSlice::User { + ptr, offset, size, .. + } => { + // TODO(yang): should we allow this? + 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.add(*offset) as *mut T, len) }; Ok(BufferViewMut { - range: self.clone(), - mapped: slice, + mapped, marker: PhantomData, }) } @@ -217,46 +278,37 @@ impl BufferSlice { } } -impl<'a, T> BufferView<'a, 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: &'a Buffer, + buffer: &'src Buffer, offset: usize, size: BufferSize, - ) -> Result, Error> { + ) -> Result, Error> { Ok(BufferView { - range: BufferSlice::External { - buffer: buffer.clone(), - offset, - size, - }, mapped: BufferMappedRange::from_buffer(buffer, offset, size.into())?, marker: PhantomData, }) } } -impl<'a, T> BufferViewMut<'a, T> { +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: &'a Buffer, + buffer: &'src Buffer, offset: usize, size: BufferSize, - ) -> Result, Error> { + ) -> Result, Error> { Ok(BufferViewMut { - range: BufferSlice::External { - buffer: buffer.clone(), - offset, - size, - }, mapped: BufferMappedRange::from_buffer(buffer, offset, size.into())?, marker: PhantomData, }) } } +/// A slice of a mapped [`Buffer`]. #[derive(Debug)] struct BufferMappedRange<'a, T: 'a> { ptr: *mut T, @@ -292,7 +344,7 @@ impl<'a, T: 'a> BufferMappedRange<'a, T> { if ptr.is_null() { return Err(buffer.device.get_error()); } - ptr.offset(offset as isize) + ptr.add(offset) } as *mut T; Ok(BufferMappedRange { ptr, @@ -317,27 +369,27 @@ impl<'a, T: 'a> BufferMappedRange<'a, T> { } } -impl AsRef<[T]> for BufferView<'_, T> { +impl<'src, T> AsRef<[T]> for BufferView<'src, T> { fn as_ref(&self) -> &[T] { self.mapped.as_slice() } } -impl AsMut<[T]> for BufferViewMut<'_, T> { +impl<'src, T> AsMut<[T]> for BufferViewMut<'src, T> { fn as_mut(&mut self) -> &mut [T] { self.mapped.as_mut_slice() } } -impl Deref for BufferView<'_, T> { +impl<'src, T> Deref for BufferView<'src, T> { type Target = [T]; fn deref(&self) -> &Self::Target { self.mapped.as_slice() } } -impl Deref for BufferViewMut<'_, T> { +impl<'src, T> Deref for BufferViewMut<'src, T> { type Target = [T]; fn deref(&self) -> &Self::Target { self.mapped.as_slice() } } -impl DerefMut for BufferViewMut<'_, T> { +impl<'src, T> DerefMut for BufferViewMut<'src, T> { fn deref_mut(&mut self) -> &mut Self::Target { self.mapped.as_mut_slice() } } diff --git a/src/device.rs b/src/device.rs index 4a3905fee..9ef4f4da1 100644 --- a/src/device.rs +++ b/src/device.rs @@ -25,7 +25,7 @@ impl Device { enable_ftz_and_daz(); let handle = unsafe { rtcNewDevice(ptr::null()) }; if handle.is_null() { - Err(unsafe { rtcGetDeviceError(ptr::null_mut()) }.into()) + Err(unsafe { rtcGetDeviceError(ptr::null_mut()) }) } else { Ok(Device { handle }) } @@ -36,7 +36,7 @@ impl Device { enable_ftz_and_daz(); let handle = unsafe { rtcNewDevice(cfg.as_ptr()) }; if handle.is_null() { - Err(unsafe { rtcGetDeviceError(ptr::null_mut()) }.into()) + Err(unsafe { rtcGetDeviceError(ptr::null_mut()) }) } else { Ok(Device { handle }) } @@ -47,7 +47,7 @@ impl Device { let cfg = config.to_c_string(); let handle = unsafe { rtcNewDevice(cfg.as_ptr()) }; if handle.is_null() { - Err(unsafe { rtcGetDeviceError(ptr::null_mut()) }.into()) + Err(unsafe { rtcGetDeviceError(ptr::null_mut()) }) } else { Ok(Device { handle }) } diff --git a/src/geometry.rs b/src/geometry.rs index e6befdbf6..ea76524e8 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, collections::HashMap, num::NonZeroUsize}; +use std::{collections::HashMap, marker::PhantomData, num::NonZeroUsize}; use crate::{sys::*, BufferSlice, BufferUsage, Device, Error, Format, GeometryType}; @@ -21,55 +21,29 @@ pub trait Geometry { } } -/// A trait for geometry objects that can have vertex attributes. -/// -/// Only supported by triangle meshes, quad meshes, curves, points, and -/// subdivision geometries. -pub trait GeometryVertexAttribute: Geometry { - /// 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. - /// - /// # Arguments - /// - /// * `count` - The number of vertex attribute slots. - fn set_vertex_attribute_count(&self, count: u32) { - unsafe { - rtcSetGeometryVertexAttributeCount(self.handle(), count); - } - } -} - +/// Information about how a (part of) buffer is bound to a geometry. #[derive(Debug)] -pub(crate) struct AttachedBuffer<'a: 'static> { +pub(crate) struct AttachedBuffer<'src> { slot: u32, format: Format, stride: usize, - source: BufferSource<'a>, -} - -#[derive(Debug)] -enum BufferSource<'a> { - EmbreeManaged(BufferSlice), - UserManaged(Cow<'a, [u8]>) + source: BufferSlice<'src>, } -/// Handle to an Embree geometry object. +/// Wrapper around an Embree geometry object. /// -/// BufferGeometry is a wrapper around an Embree geometry object. It does not -/// own the buffers that are bound to it, but it does own the geometry object -/// itself. +/// It does not own the buffers that are bound to it, but it does own the +/// geometry object itself. #[derive(Debug)] -pub struct BufferGeometry<'buf: 'static> { +pub struct BufferGeometry<'buf> { pub(crate) device: Device, pub(crate) handle: RTCGeometry, - pub(crate) kind: GeometryType, + kind: GeometryType, + vertex_attribute_count: Option, pub(crate) attachments: HashMap>>, } -impl<'buf: 'static> Drop for BufferGeometry<'buf> { +impl<'buf> Drop for BufferGeometry<'buf> { fn drop(&mut self) { unsafe { rtcReleaseGeometry(self.handle); @@ -77,9 +51,16 @@ impl<'buf: 'static> Drop for BufferGeometry<'buf> { } } -impl<'device, 'buf: 'static> BufferGeometry<'buf> { - pub(crate) fn new(device: &'device Device, kind: GeometryType) -> Result, Error> { +impl<'dev, 'buf> BufferGeometry<'buf> { + pub(crate) fn new( + device: &'dev Device, + kind: GeometryType, + ) -> Result, Error> { let handle = unsafe { rtcNewGeometry(device.handle, kind) }; + let vertex_attribute_count = match kind { + GeometryType::GRID | GeometryType::USER | GeometryType::INSTANCE => None, + _ => Some(0), + }; if handle.is_null() { Err(device.get_error()) } else { @@ -87,23 +68,54 @@ impl<'device, 'buf: 'static> BufferGeometry<'buf> { device: device.clone(), handle, kind, + vertex_attribute_count, attachments: HashMap::new(), }) } } + /// Checks if the given vertex attribute slot is valid for this geometry. + fn check_vertex_attribute(&self, slot: u32) -> Result<(), Error> { + match self.vertex_attribute_count { + None => { + eprint!( + "Vertex attribute not allowed for geometries of type {:?}!", + self.kind + ); + Err(Error::INVALID_OPERATION) + } + Some(c) => { + if slot >= c { + eprint!( + "Vertex attribute slot {} is out of bounds for geometry of type {:?}!", + slot, self.kind + ); + Err(Error::INVALID_ARGUMENT) + } else { + Ok(()) + } + } + } + } + /// Binds a view of a buffer to the geometry. /// - /// After successful completion of this function, the geometry will hold - /// a reference to the buffer object. + /// 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. /// - /// Analogous to [`rtcSetGeometryBuffer`](https://spec.oneapi.io/oneart/0.5-rev-1/embree-spec.html#rtcsetgeometrybuffer). + /// Under the hood, function call [`rtcSetGeometryBuffer`] is used to bind + /// [`BufferSlice::Buffer`] or [`BufferSlice::Internal`] 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. + /// * `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. /// @@ -113,33 +125,37 @@ impl<'device, 'buf: 'static> BufferGeometry<'buf> { /// multiple of 4. /// /// * `count` - The number of elements in the buffer. - pub fn set_buffer( - &mut self, + pub fn set_buffer<'a>( + &'a mut self, usage: BufferUsage, slot: u32, format: Format, - slice: BufferSlice, + 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(slot)?; + } match slice { - BufferSlice::External { + BufferSlice::Buffer { buffer, offset, size, } => { - let bindings = self.attachments.entry(usage).or_insert_with(Vec::new); - println!( - "Binding buffer to slot {}, offset {}, stride {}, count {}", - slot, offset, stride, count + dbg!( + "Binding buffer slice to slot {}, offset {}, stride {}, count {}", + slot, + offset, + stride, + count ); + let bindings = self.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) => { - eprint!( - "Buffer already attached to slot {}, will be overwritten!", - slot - ); bindings.remove(i); unsafe { rtcSetGeometryBuffer( @@ -149,22 +165,23 @@ impl<'device, 'buf: 'static> BufferGeometry<'buf> { format, buffer.handle, offset, - stride as usize, - count as usize, + stride, + count, ) }; bindings.push(AttachedBuffer { slot, - source: BufferSource::EmbreeManaged(BufferSlice::External { + source: BufferSlice::Buffer { buffer, offset, size, - }), + }, format, stride, }); Ok(()) } + // If the slot is not bound, just bind the new buffer. None => { unsafe { rtcSetGeometryBuffer( @@ -174,17 +191,17 @@ impl<'device, 'buf: 'static> BufferGeometry<'buf> { format, buffer.handle, offset, - stride as usize, - count as usize, + stride, + count, ) }; bindings.push(AttachedBuffer { slot, - source: BufferSource::EmbreeManaged(BufferSlice::External { + source: BufferSlice::Buffer { buffer, offset, size, - }), + }, format, stride, }); @@ -196,6 +213,69 @@ impl<'device, 'buf: 'static> BufferGeometry<'buf> { eprint!("Internally managed buffer cannot be shared!"); Err(Error::INVALID_ARGUMENT) } + BufferSlice::User { + ptr, offset, size, .. + } => { + let bindings = self.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(()) + } + } + } } } @@ -232,34 +312,30 @@ impl<'device, 'buf: 'static> BufferGeometry<'buf> { format: Format, stride: usize, count: usize, - ) -> Result<(), Error> { + ) -> Result { debug_assert!(stride % 4 == 0, "Stride must be a multiple of 4!"); + if usage == BufferUsage::VERTEX_ATTRIBUTE { + self.check_vertex_attribute(slot)?; + } let bindings = self.attachments.entry(usage).or_insert_with(Vec::new); - if bindings.iter().find(|a| a.slot == slot).is_none() { - let raw_ptr = unsafe { - rtcSetNewGeometryBuffer( - self.handle, - usage, - slot, - format, - stride as usize, - count as usize, - ) - }; + 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::Internal { + ptr: raw_ptr, + size: NonZeroUsize::new(count * stride).unwrap(), + marker: std::marker::PhantomData, + }; bindings.push(AttachedBuffer { slot, - source: BufferSource::EmbreeManaged(BufferSlice::Internal { - ptr: raw_ptr, - size: NonZeroUsize::new(count * stride as usize).unwrap(), - marker: std::marker::PhantomData, - }), + source: slice, format, stride, }); - Ok(()) + Ok(slice) } } else { eprint!("Buffer already attached to slot {}", slot); @@ -269,25 +345,56 @@ impl<'device, 'buf: 'static> BufferGeometry<'buf> { /// Returns the buffer bound to the given slot and usage. pub fn get_buffer(&self, usage: BufferUsage, slot: u32) -> Option<&BufferSlice> { - let attachment = self.attachments + let attachment = self + .attachments .get(&usage) .and_then(|v| v.iter().find(|a| a.slot == slot)); if let Some(attachment) = attachment { - match attachment.source { - BufferSource::EmbreeManaged(ref slice) => Some(slice), - BufferSource::UserManaged(_) => None, - } + Some(&attachment.source) } else { None } } + /// Returns the type of geometry of this geometry. + pub fn kind(&self) -> GeometryType { self.kind } + pub fn commit(&mut self) { unsafe { rtcCommitGeometry(self.handle); } } + /// 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.vertex_attribute_count { + None => { + panic!( + "set_vertex_attribute_count is not supported by geometry of type {:?}.", + self.kind + ); + } + Some(_) => { + // Update the vertex attribute count. + unsafe { + rtcSetGeometryVertexAttributeCount(self.handle, count); + } + self.vertex_attribute_count = Some(count); + } + } + } + /// Set the subdivision mode for the topology of the specified subdivision /// geometry. /// @@ -339,21 +446,4 @@ impl<'device, 'buf: 'static> BufferGeometry<'buf> { rtcSetGeometryVertexAttributeTopology(self.handle, vertex_attribute_id, topology_id); } } - - // /// Assigns a view of a shared data buffer to a geometry. - // pub fn set_shared_buffer(&mut self, usage: BufferUsage, slot: u32, buffer: - // &[u8], ) -> Result<(), Error> { let bindings = - // self.attachments.entry(usage).or_insert_with(Vec::new); if bindings. - // iter().find(|a| a.slot == slot).is_none() { bindings. - // push(AttachedBuffer { slot, - // buffer, - // format: Format::FLOAT3, - // stride: 0, - // }); - // Ok(()) - // } else { - // eprint!("Buffer already attached to slot {}", slot); - // Err(Error::INVALID_ARGUMENT) - // } - // } } diff --git a/src/geometry/quad_mesh.rs b/src/geometry/quad_mesh.rs index b582f1eed..aceb209c4 100644 --- a/src/geometry/quad_mesh.rs +++ b/src/geometry/quad_mesh.rs @@ -1,7 +1,4 @@ -use crate::{ - sys, BufferGeometry, BufferUsage, Device, Error, Format, Geometry, GeometryType, - GeometryVertexAttribute, -}; +use crate::{sys, BufferGeometry, BufferUsage, Device, Error, Format, Geometry, GeometryType}; use std::ops::{Deref, DerefMut}; #[derive(Debug)] @@ -27,7 +24,7 @@ impl QuadMesh { /// [`Format::UINT4`] format. They are 16-byte and 4-byte aligned /// respectively, and are bound to the first slot of their respective /// buffers. - pub fn unanimated<'a>(device: &'a Device, num_quads: usize, num_verts: usize) -> QuadMesh { + pub fn unanimated(device: &Device, num_quads: usize, num_verts: usize) -> QuadMesh { let mut geometry = BufferGeometry::new(device, GeometryType::QUAD).unwrap(); geometry .set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, num_verts) @@ -51,5 +48,3 @@ impl Geometry for QuadMesh { fn handle(&self) -> sys::RTCGeometry { self.handle } } - -impl GeometryVertexAttribute for QuadMesh {} diff --git a/src/geometry/triangle_mesh.rs b/src/geometry/triangle_mesh.rs index 6802a16ed..9e0a46276 100644 --- a/src/geometry/triangle_mesh.rs +++ b/src/geometry/triangle_mesh.rs @@ -1,7 +1,4 @@ -use crate::{ - sys::*, BufferGeometry, BufferUsage, Device, Format, Geometry, GeometryType, - GeometryVertexAttribute, -}; +use crate::{sys::*, BufferGeometry, BufferUsage, Device, Format, Geometry, GeometryType}; use std::ops::{Deref, DerefMut}; #[derive(Debug)] @@ -40,7 +37,7 @@ impl TriangleMesh { } impl Geometry for TriangleMesh { - fn new(device: &Device) -> Result + fn new(device: &Device) -> Result where Self: Sized, { @@ -51,5 +48,3 @@ impl Geometry for TriangleMesh { fn handle(&self) -> RTCGeometry { self.handle } } - -impl GeometryVertexAttribute for TriangleMesh {} diff --git a/src/ray.rs b/src/ray.rs index 9e8576a58..17c163d3b 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -18,8 +18,8 @@ impl Ray { dir_x: dir[0], dir_y: dir[1], dir_z: dir[2], - tnear: tnear, - tfar: tfar, + tnear, + tfar, time: 0.0, mask: u32::MAX, id: 0, @@ -28,11 +28,6 @@ impl Ray { } } -impl Hit { - /// Returns true if the hit is valid (i.e. the ray hit something). - pub fn is_valid(&self) -> bool { self.geomID != INVALID_ID } -} - impl Default for Hit { fn default() -> Self { Hit { @@ -55,4 +50,38 @@ impl RayHit { hit: Hit::default(), } } + + /// Returns true if the hit is valid (i.e. the ray hit something). + pub fn is_valid(&self) -> bool { self.hit.geomID != INVALID_ID } + + /// Returns the normal of the hit point (normalized). + pub fn normal(&self) -> [f32; 3] { + let len = (self.hit.Ng_x * self.hit.Ng_x + + self.hit.Ng_y * self.hit.Ng_y + + self.hit.Ng_z * self.hit.Ng_z) + .sqrt(); + if len == 0.0 { + [0.0, 0.0, 0.0] + } else { + let len = 1.0 / len; + [ + self.hit.Ng_x * len, + self.hit.Ng_y * len, + self.hit.Ng_z * len, + ] + } + } + + /// Returns the barycentric coordinates of the hit point. + pub fn uv(&self) -> [f32; 2] { [self.hit.u, self.hit.v] } + + /// Returns the hit point. + pub fn hit_point(&self) -> [f32; 3] { + let t = self.ray.tfar; + [ + self.ray.org_x + self.ray.dir_x * t, + self.ray.org_y + self.ray.dir_y * t, + self.ray.org_z + self.ray.dir_z * t, + ] + } } diff --git a/src/ray_packet.rs b/src/ray_packet.rs index f959ef333..3090ca57a 100644 --- a/src/ray_packet.rs +++ b/src/ray_packet.rs @@ -34,8 +34,8 @@ impl Ray4 { 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, + tnear, + tfar, time: [0.0; 4], mask: [u32::MAX; 4], id: [0; 4], @@ -128,7 +128,7 @@ impl SoAHit for Hit4 { impl RayHit4 { pub fn new(ray: Ray4) -> RayHit4 { sys::RTCRayHit4 { - ray: ray, + ray, hit: Hit4::new(), } } diff --git a/src/scene.rs b/src/scene.rs index 4865174f4..05868b867 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -76,6 +76,10 @@ impl Scene { /// Get the underlying handle to the scene, e.g. for passing it to /// native code or ISPC kernels. + /// + /// # Safety + /// + /// The handle is only valid as long as the scene is alive. pub unsafe fn handle(&self) -> RTCScene { self.handle } /// Commit the scene to build the BVH on top of the geometry to allow @@ -175,7 +179,8 @@ impl Scene { ray_hit } - pub fn occluded(&self, ctx: &mut IntersectContext, ray: &mut Ray) { + /// Checks for a single ray if whether there is any hit with the scene. + pub fn occluded(&self, ctx: &mut IntersectContext, ray: &mut Ray) -> bool { unsafe { rtcOccluded1( self.handle, @@ -183,6 +188,7 @@ impl Scene { ray as *mut RTCRay, ); } + ray.tfar == -f32::INFINITY } pub fn intersect4(&self, ctx: &mut IntersectContext, ray: &mut RayHit4, valid: &[i32; 4]) { From 73602421eefe830cfe154739e4042ea86df6ac51 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Wed, 15 Feb 2023 17:59:18 +0100 Subject: [PATCH 22/65] update minimal example --- examples/minimal/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 5c1c2fa28..88672bfbb 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -16,7 +16,7 @@ fn cast_ray(scene: &Scene, origin: [f32; 3], direction: [f32; 3]) { print!("{origin:?} "); - if ray_hit.hit.is_valid() { + if ray_hit.is_valid() { println!( "Found intersection on geometry {}, primitive {} at tfar = {}", ray_hit.hit.geomID, ray_hit.hit.primID, ray_hit.ray.tfar From 6e22e8956a090a97ddaec8f5c07302f13651a874 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Wed, 15 Feb 2023 23:15:27 +0100 Subject: [PATCH 23/65] fix some clippy warnings --- src/buffer.rs | 4 ++-- src/geometry.rs | 3 +++ src/geometry/quad_mesh.rs | 4 ++-- src/geometry/triangle_mesh.rs | 4 ++-- src/ray_packet.rs | 2 -- src/ray_stream.rs | 3 ++- src/soa_ray.rs | 24 ++++++++++++------------ 7 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/buffer.rs b/src/buffer.rs index 9441dda9e..64d26bcd2 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -174,9 +174,9 @@ impl<'src> BufferSlice<'src> { /// * `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, S: RangeBounds>(slice: &'slice [T], bounds: S) -> Self { + pub fn from_slice>(slice: &[T], bounds: S) -> Self { let (first, count) = range_bounds_to_offset_and_size(bounds); - let count = count.unwrap_or_else(|| slice.len() - first); + let count = count.unwrap_or(slice.len() - first); debug_assert!( first + count <= slice.len() && first < slice.len(), "Invalid slice range" diff --git a/src/geometry.rs b/src/geometry.rs index ea76524e8..505014a10 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -21,11 +21,14 @@ pub trait Geometry { } } +// 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)] pub(crate) struct AttachedBuffer<'src> { slot: u32, + #[allow(dead_code)] format: Format, + #[allow(dead_code)] stride: usize, source: BufferSlice<'src>, } diff --git a/src/geometry/quad_mesh.rs b/src/geometry/quad_mesh.rs index aceb209c4..3da8806f1 100644 --- a/src/geometry/quad_mesh.rs +++ b/src/geometry/quad_mesh.rs @@ -26,10 +26,10 @@ impl QuadMesh { /// buffers. pub fn unanimated(device: &Device, num_quads: usize, num_verts: usize) -> QuadMesh { let mut geometry = BufferGeometry::new(device, GeometryType::QUAD).unwrap(); - geometry + let _ = geometry .set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, num_verts) .unwrap(); - geometry + let _ = geometry .set_new_buffer(BufferUsage::INDEX, 0, Format::UINT4, 16, num_quads) .unwrap(); Self(geometry) diff --git a/src/geometry/triangle_mesh.rs b/src/geometry/triangle_mesh.rs index 9e0a46276..902afbc87 100644 --- a/src/geometry/triangle_mesh.rs +++ b/src/geometry/triangle_mesh.rs @@ -26,10 +26,10 @@ impl TriangleMesh { /// buffers. pub fn unanimated(device: &Device, num_tris: usize, num_verts: usize) -> TriangleMesh { let mut geometry = BufferGeometry::new(device, GeometryType::TRIANGLE).unwrap(); - geometry + let _ = geometry .set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, num_verts) .unwrap(); - geometry + let _ = geometry .set_new_buffer(BufferUsage::INDEX, 0, Format::UINT3, 12, num_tris) .unwrap(); Self(geometry) diff --git a/src/ray_packet.rs b/src/ray_packet.rs index 3090ca57a..5906f018b 100644 --- a/src/ray_packet.rs +++ b/src/ray_packet.rs @@ -1,5 +1,3 @@ -use std::{f32, u32}; - use crate::{ soa_ray::{SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut}, sys, diff --git a/src/ray_stream.rs b/src/ray_stream.rs index 8370e376e..81d08c8bf 100644 --- a/src/ray_stream.rs +++ b/src/ray_stream.rs @@ -46,6 +46,7 @@ impl RayN { SoARayIterMut::new(self, n) } pub fn len(&self) -> usize { self.org_x.len() } + pub fn is_empty(&self) -> bool { self.len() == 0 } pub unsafe fn as_raynp(&mut self) -> sys::RTCRayNp { sys::RTCRayNp { org_x: self.org_x.as_mut_ptr(), @@ -176,7 +177,7 @@ impl RayHitN { pub fn new(ray: RayN) -> RayHitN { let n = ray.len(); RayHitN { - ray: ray, + ray, hit: HitN::new(n), } } diff --git a/src/soa_ray.rs b/src/soa_ray.rs index 423bd411e..6a590956d 100644 --- a/src/soa_ray.rs +++ b/src/soa_ray.rs @@ -140,9 +140,9 @@ 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, + ray, cur: 0, - len: len, + len, } } } @@ -155,7 +155,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, @@ -177,9 +177,9 @@ 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, + ray, cur: 0, - len: len, + len, } } } @@ -192,7 +192,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, @@ -229,9 +229,9 @@ 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, + hit, cur: 0, - len: len, + len, } } } @@ -244,7 +244,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, @@ -323,9 +323,9 @@ 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, + hit, cur: 0, - len: len, + len, } } } @@ -338,7 +338,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, From 9a2a39c6db2bf16a8d27efae607348881e695dd4 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 16 Feb 2023 17:30:35 +0100 Subject: [PATCH 24/65] remove Geometry trait --- .github/workflows/main.yml | 2 +- examples/minimal/src/main.rs | 7 +- examples/triangle/src/main.rs | 10 +- examples/triangle_geometry/src/main.rs | 22 +-- src/buffer.rs | 55 ++----- src/device.rs | 6 +- src/geometry.rs | 193 ++++++++++++++++--------- src/geometry/quad_mesh.rs | 50 ------- src/geometry/triangle_mesh.rs | 50 ------- src/instance.rs | 15 +- src/ray_packet.rs | 2 +- src/scene.rs | 76 +++++++--- src/soa_ray.rs | 28 +--- 13 files changed, 225 insertions(+), 291 deletions(-) delete mode 100644 src/geometry/quad_mesh.rs delete mode 100644 src/geometry/triangle_mesh.rs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f92cc84b6..0e83edc8c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,7 +34,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/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 88672bfbb..68497395b 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -1,7 +1,6 @@ //! This example shows how to intersect a ray with a single triangle. -use embree::{BufferUsage, Device, Format, IntersectContext, Ray, Scene, TriangleMesh}; -use std::sync::Arc; +use embree::{BufferUsage, Device, Format, GeometryType, IntersectContext, Ray, Scene}; /// Casts a single ray with the given origin and direction. fn cast_ray(scene: &Scene, origin: [f32; 3], direction: [f32; 3]) { @@ -36,7 +35,7 @@ fn main() { let mut scene = device.create_scene().unwrap(); { // Create a triangle mesh geometry, and initialise a single triangle. - let mut triangle = device.create_geometry::().unwrap(); + let mut triangle = device.create_geometry(GeometryType::TRIANGLE).unwrap(); triangle .set_new_buffer( BufferUsage::VERTEX, @@ -72,7 +71,7 @@ fn main() { // otherwise you will not get any intersection results. triangle.commit(); - scene.attach_geometry(Arc::new(triangle)); + scene.attach_geometry(&triangle); // The scene must also be committed when you are done setting it up. scene.commit(); diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index 6a2a3eac3..091932729 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -3,7 +3,7 @@ extern crate embree; extern crate support; -use embree::{BufferUsage, Device, Geometry, IntersectContext, RayHitN, RayN, TriangleMesh}; +use embree::{BufferUsage, Device, Format, IntersectContext, RayHitN, RayN, TriangleMesh}; use std::sync::Arc; fn main() { @@ -16,9 +16,9 @@ fn main() { }); // Make a triangle - let mut triangle = TriangleMesh::unanimated(&device, 1, 3); + let mut triangle = TriangleMesh::new(&device).unwrap(); triangle - .get_buffer(BufferUsage::VERTEX, 0) + .set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, 3) .unwrap() .view_mut::<[f32; 4]>() .unwrap() @@ -28,7 +28,7 @@ fn main() { [1.0, 0.0, 0.0, 0.0], ]); triangle - .get_buffer(BufferUsage::INDEX, 0) + .set_new_buffer(BufferUsage::INDEX, 0, Format::UINT3, 12, 1) .unwrap() .view_mut::<[u32; 3]>() .unwrap() @@ -37,7 +37,7 @@ fn main() { let triangle = Arc::new(triangle); let mut scene = device.create_scene().unwrap(); - scene.attach_geometry(triangle); + scene.attach_geometry(&triangle); scene.commit(); support::display::run(display, move |image, _, _| { diff --git a/examples/triangle_geometry/src/main.rs b/examples/triangle_geometry/src/main.rs index 54e12644e..32c1c243a 100644 --- a/examples/triangle_geometry/src/main.rs +++ b/examples/triangle_geometry/src/main.rs @@ -4,10 +4,8 @@ extern crate embree; extern crate support; use embree::{ - BufferSlice, BufferUsage, Device, Format, Geometry, IntersectContext, QuadMesh, Ray, - TriangleMesh, + BufferSlice, BufferUsage, Device, Format, IntersectContext, QuadMesh, Ray, TriangleMesh, }; -use std::sync::Arc; use support::Camera; fn make_cube(device: &Device, vertex_colors: &[[f32; 3]]) -> TriangleMesh { @@ -68,9 +66,9 @@ fn make_cube(device: &Device, vertex_colors: &[[f32; 3]]) -> TriangleMesh { } fn make_ground_plane(device: &Device) -> QuadMesh { - let mut mesh = QuadMesh::unanimated(device, 1, 4); + let mut mesh = QuadMesh::new(device).unwrap(); { - mesh.get_buffer(BufferUsage::VERTEX, 0) + mesh.set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, 4) .unwrap() .view_mut::<[f32; 4]>() .unwrap() @@ -80,7 +78,7 @@ fn make_ground_plane(device: &Device) -> QuadMesh { [10.0, -2.0, 10.0, 0.0], [10.0, -2.0, -10.0, 0.0], ]); - mesh.get_buffer(BufferUsage::INDEX, 0) + mesh.set_new_buffer(BufferUsage::INDEX, 0, Format::UINT4, 16, 1) .unwrap() .view_mut::<[u32; 4]>() .unwrap() @@ -93,7 +91,9 @@ fn make_ground_plane(device: &Device) -> QuadMesh { fn main() { let display = support::Display::new(512, 512, "triangle geometry"); let device = Device::new().unwrap(); - + device.set_error_function(|err, msg| { + println!("{}: {}", err, msg); + }); let vertex_colors = vec![ [0.0, 0.0, 0.0], [0.0, 0.0, 1.0], @@ -120,12 +120,12 @@ fn main() { [1.0, 1.0, 0.0], ]; - let cube = Arc::new(make_cube(&device, &vertex_colors)); - let ground = Arc::new(make_ground_plane(&device)); + let cube = make_cube(&device, &vertex_colors); + let ground = make_ground_plane(&device); let mut scene = device.create_scene().unwrap(); - let _ = scene.attach_geometry(cube); - let ground_id = scene.attach_geometry(ground); + let _ = scene.attach_geometry(&cube); + let ground_id = scene.attach_geometry(&ground); scene.commit(); let mut intersection_ctx = IntersectContext::coherent(); diff --git a/src/buffer.rs b/src/buffer.rs index 64d26bcd2..f54282a9b 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -55,9 +55,11 @@ impl Buffer { /// /// # Safety /// - /// The handle returned by this function is a raw pointer to the - /// underlying Embree buffer. It is not safe to use this handle - /// outside of the Embree API. + /// 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. @@ -138,7 +140,7 @@ pub struct BufferViewMut<'buf, T: 'buf> { /// 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 memory. +/// [`rtcSetNewGeometryBuffer`]) or from user owned/borrowed memory. #[derive(Debug, Clone, Copy)] pub enum BufferSlice<'src> { /// Slice into a [`Buffer`] object. @@ -147,8 +149,8 @@ pub enum BufferSlice<'src> { offset: usize, size: BufferSize, }, - /// Slice into memory managed by Embree. - Internal { + /// 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]>, @@ -204,7 +206,7 @@ impl<'src> BufferSlice<'src> { marker: PhantomData, }) } - BufferSlice::Internal { ptr, size, .. } => { + 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!" @@ -212,26 +214,13 @@ impl<'src> BufferSlice<'src> { let len = size.get() / mem::size_of::(); let mapped = unsafe { BufferMappedRange::from_raw_parts(*ptr as *mut T, len) }; Ok(BufferView { - //slice: *self, mapped, marker: PhantomData, }) } - BufferSlice::User { - ptr, offset, size, .. - } => { - // TODO(yang): should we allow this? - 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.add(*offset) 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) } } } @@ -246,7 +235,7 @@ impl<'src> BufferSlice<'src> { mapped: BufferMappedRange::from_buffer(buffer, *offset, size.get())?, marker: PhantomData, }), - BufferSlice::Internal { ptr, size, .. } => { + 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!" @@ -258,21 +247,9 @@ impl<'src> BufferSlice<'src> { marker: PhantomData, }) } - BufferSlice::User { - ptr, offset, size, .. - } => { - // TODO(yang): should we allow this? - 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.add(*offset) 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) } } } diff --git a/src/device.rs b/src/device.rs index 9ef4f4da1..6c285ef4b 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,4 +1,4 @@ -use crate::{sys::*, Buffer, BufferSize, Error, Geometry, Scene}; +use crate::{sys::*, Buffer, BufferSize, Error, Geometry, GeometryType, Scene}; use std::{ ffi::CString, fmt::{self, Display, Formatter}, @@ -216,7 +216,9 @@ impl Device { /// Creates a [`Geometry`] object bound to the device without any /// buffers attached. - pub fn create_geometry(&self) -> Result { G::new(self) } + pub fn create_geometry(&self, kind: GeometryType) -> Result, Error> { + Geometry::new(self, kind) + } } impl Drop for Device { diff --git a/src/geometry.rs b/src/geometry.rs index 505014a10..700204f40 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,29 +1,21 @@ -use std::{collections::HashMap, marker::PhantomData, num::NonZeroUsize}; +use std::{ + collections::HashMap, + marker::PhantomData, + num::NonZeroUsize, + sync::{Arc, Mutex}, +}; use crate::{sys::*, BufferSlice, BufferUsage, Device, Error, Format, GeometryType}; -mod quad_mesh; -mod triangle_mesh; +// mod quad_mesh; +// mod triangle_mesh; -pub use quad_mesh::QuadMesh; -pub use triangle_mesh::TriangleMesh; - -pub trait Geometry { - fn new(device: &Device) -> Result - where - Self: Sized; - fn kind(&self) -> GeometryType; - fn handle(&self) -> RTCGeometry; - fn commit(&mut self) { - unsafe { - rtcCommitGeometry(self.handle()); - } - } -} +// pub use quad_mesh::QuadMesh; +// pub use triangle_mesh::TriangleMesh; // 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)] +#[derive(Debug, Clone)] pub(crate) struct AttachedBuffer<'src> { slot: u32, #[allow(dead_code)] @@ -33,20 +25,40 @@ pub(crate) struct AttachedBuffer<'src> { source: BufferSlice<'src>, } +/// State of a geometry object. +#[derive(Default, Debug, Clone)] +struct GeometryState<'buf> { + vertex_attribute_count: Option, + attachments: HashMap>>, +} + /// Wrapper around an Embree geometry object. /// /// It does not own the buffers that are bound to it, but it does own the /// geometry object itself. #[derive(Debug)] -pub struct BufferGeometry<'buf> { +pub struct Geometry<'buf> { pub(crate) device: Device, pub(crate) handle: RTCGeometry, kind: GeometryType, - vertex_attribute_count: Option, - pub(crate) attachments: HashMap>>, + state: Arc>>, } -impl<'buf> Drop for BufferGeometry<'buf> { +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, + state: self.state.clone(), + } + } +} + +impl<'buf> Drop for Geometry<'buf> { fn drop(&mut self) { unsafe { rtcReleaseGeometry(self.handle); @@ -54,11 +66,8 @@ impl<'buf> Drop for BufferGeometry<'buf> { } } -impl<'dev, 'buf> BufferGeometry<'buf> { - pub(crate) fn new( - device: &'dev Device, - kind: GeometryType, - ) -> Result, Error> { +impl<'dev, 'buf> Geometry<'buf> { + pub(crate) fn new(device: &'dev Device, kind: GeometryType) -> Result, Error> { let handle = unsafe { rtcNewGeometry(device.handle, kind) }; let vertex_attribute_count = match kind { GeometryType::GRID | GeometryType::USER | GeometryType::INSTANCE => None, @@ -67,19 +76,32 @@ impl<'dev, 'buf> BufferGeometry<'buf> { if handle.is_null() { Err(device.get_error()) } else { - Ok(BufferGeometry { + Ok(Geometry { device: device.clone(), handle, kind, - vertex_attribute_count, - attachments: HashMap::new(), + state: Arc::new(Mutex::new(GeometryState { + vertex_attribute_count, + attachments: HashMap::new(), + })), }) } } + /// 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 given vertex attribute slot is valid for this geometry. fn check_vertex_attribute(&self, slot: u32) -> Result<(), Error> { - match self.vertex_attribute_count { + match self.state.lock().unwrap().vertex_attribute_count { None => { eprint!( "Vertex attribute not allowed for geometries of type {:?}!", @@ -108,8 +130,8 @@ impl<'dev, 'buf> BufferGeometry<'buf> { /// See the documentation of [`BufferSlice`] for more information. /// /// Under the hood, function call [`rtcSetGeometryBuffer`] is used to bind - /// [`BufferSlice::Buffer`] or [`BufferSlice::Internal`] to the geometry, - /// and [`rtcSetSharedGeometryBuffer`] is used to bind + /// [`BufferSlice::Buffer`] or [`BufferSlice::GeometryLocal`] to the + /// geometry, and [`rtcSetSharedGeometryBuffer`] is used to bind /// [`BufferSlice::User`]. /// /// # Arguments @@ -154,7 +176,8 @@ impl<'dev, 'buf> BufferGeometry<'buf> { stride, count ); - let bindings = self.attachments.entry(usage).or_insert_with(Vec::new); + let mut state = self.state.lock().unwrap(); + let bindings = state.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. @@ -212,14 +235,15 @@ impl<'dev, 'buf> BufferGeometry<'buf> { } } } - BufferSlice::Internal { .. } => { - eprint!("Internally managed buffer cannot be shared!"); + BufferSlice::GeometryLocal { .. } => { + eprint!("Sharing geometry local buffer is not allowed!"); Err(Error::INVALID_ARGUMENT) } BufferSlice::User { ptr, offset, size, .. } => { - let bindings = self.attachments.entry(usage).or_insert_with(Vec::new); + let mut state = self.state.lock().unwrap(); + let bindings = state.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. @@ -320,43 +344,44 @@ impl<'dev, 'buf> BufferGeometry<'buf> { if usage == BufferUsage::VERTEX_ATTRIBUTE { self.check_vertex_attribute(slot)?; } - let bindings = self.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::Internal { - ptr: raw_ptr, - size: NonZeroUsize::new(count * stride).unwrap(), - marker: std::marker::PhantomData, + { + let mut state = self.state.lock().unwrap(); + let bindings = state.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) }; - bindings.push(AttachedBuffer { - slot, - source: slice, - format, - stride, - }); - Ok(slice) + 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: std::marker::PhantomData, + }; + bindings.push(AttachedBuffer { + slot, + source: slice, + format, + stride, + }); + Ok(slice) + } + } else { + eprint!("Buffer already attached to slot {}", slot); + Err(Error::INVALID_ARGUMENT) } - } 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<&BufferSlice> { - let attachment = self + pub fn get_buffer(&self, usage: BufferUsage, slot: u32) -> Option { + let state = self.state.lock().unwrap(); + state .attachments .get(&usage) - .and_then(|v| v.iter().find(|a| a.slot == slot)); - if let Some(attachment) = attachment { - Some(&attachment.source) - } else { - None - } + .and_then(|v| v.iter().find(|a| a.slot == slot)) + .map(|a| a.source) } /// Returns the type of geometry of this geometry. @@ -381,7 +406,8 @@ impl<'dev, 'buf> BufferGeometry<'buf> { /// /// * `count` - The number of vertex attribute slots. pub fn set_vertex_attribute_count(&mut self, count: u32) { - match self.vertex_attribute_count { + let mut state = self.state.lock().unwrap(); + match state.vertex_attribute_count { None => { panic!( "set_vertex_attribute_count is not supported by geometry of type {:?}.", @@ -393,7 +419,7 @@ impl<'dev, 'buf> BufferGeometry<'buf> { unsafe { rtcSetGeometryVertexAttributeCount(self.handle, count); } - self.vertex_attribute_count = Some(count); + state.vertex_attribute_count = Some(count); } } } @@ -450,3 +476,32 @@ impl<'dev, 'buf> BufferGeometry<'buf> { } } } + +macro_rules! impl_geometry_type { + ($name:ident, $kind:path, #[$doc:meta]) => { + #[derive(Debug)] + pub struct $name(Geometry<'static>); + + impl Deref for $name { + type Target = Geometry<'static>; + + fn deref(&self) -> &Self::Target { &self.0 } + } + + impl DerefMut for $name { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } + } + + impl $name { + #[$doc] + pub fn new(device: &Device) -> Result { + Ok(Self(Geometry::new(device, $kind)?)) + } + } + }; +} + +use std::ops::{Deref, DerefMut}; + +impl_geometry_type!(TriangleMesh, GeometryType::TRIANGLE, #[doc = "A triangle mesh geometry."]); +impl_geometry_type!(QuadMesh, GeometryType::QUAD, #[doc = "A quad mesh geometry."]); diff --git a/src/geometry/quad_mesh.rs b/src/geometry/quad_mesh.rs deleted file mode 100644 index 3da8806f1..000000000 --- a/src/geometry/quad_mesh.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::{sys, BufferGeometry, BufferUsage, Device, Error, Format, Geometry, GeometryType}; -use std::ops::{Deref, DerefMut}; - -#[derive(Debug)] -pub struct QuadMesh(BufferGeometry<'static>); - -impl Deref for QuadMesh { - type Target = BufferGeometry<'static>; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -impl DerefMut for QuadMesh { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } -} - -impl QuadMesh { - /// Creates a new quad mesh geometry with the given number of quads and - /// vertices. - /// - /// The geometry is unanimated, and the vertex and index buffers are - /// allocated but not initialized. The vertex buffer is in - /// [`Format::FLOAT3`] format, the index buffer is in - /// [`Format::UINT4`] format. They are 16-byte and 4-byte aligned - /// respectively, and are bound to the first slot of their respective - /// buffers. - pub fn unanimated(device: &Device, num_quads: usize, num_verts: usize) -> QuadMesh { - let mut geometry = BufferGeometry::new(device, GeometryType::QUAD).unwrap(); - let _ = geometry - .set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, num_verts) - .unwrap(); - let _ = geometry - .set_new_buffer(BufferUsage::INDEX, 0, Format::UINT4, 16, num_quads) - .unwrap(); - Self(geometry) - } -} - -impl Geometry for QuadMesh { - fn new(device: &Device) -> Result - where - Self: Sized, - { - Ok(Self(BufferGeometry::new(device, GeometryType::QUAD)?)) - } - - fn kind(&self) -> GeometryType { GeometryType::QUAD } - - fn handle(&self) -> sys::RTCGeometry { self.handle } -} diff --git a/src/geometry/triangle_mesh.rs b/src/geometry/triangle_mesh.rs deleted file mode 100644 index 902afbc87..000000000 --- a/src/geometry/triangle_mesh.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::{sys::*, BufferGeometry, BufferUsage, Device, Format, Geometry, GeometryType}; -use std::ops::{Deref, DerefMut}; - -#[derive(Debug)] -pub struct TriangleMesh(BufferGeometry<'static>); - -impl Deref for TriangleMesh { - type Target = BufferGeometry<'static>; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -impl DerefMut for TriangleMesh { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } -} - -impl TriangleMesh { - /// Creates a new triangle mesh geometry with the given number of triangles - /// and vertices. - /// - /// The geometry is unanimated, and the vertex and index buffers are - /// allocated but not initialized. The vertex buffer is in - /// [`Format::FLOAT3`] format, and the index buffer is in - /// [`Format::UINT3`] format, and are 16-byte and 4-byte aligned, - /// respectively. They are bound to the first slot of their respective - /// buffers. - pub fn unanimated(device: &Device, num_tris: usize, num_verts: usize) -> TriangleMesh { - let mut geometry = BufferGeometry::new(device, GeometryType::TRIANGLE).unwrap(); - let _ = geometry - .set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, num_verts) - .unwrap(); - let _ = geometry - .set_new_buffer(BufferUsage::INDEX, 0, Format::UINT3, 12, num_tris) - .unwrap(); - Self(geometry) - } -} - -impl Geometry for TriangleMesh { - fn new(device: &Device) -> Result - where - Self: Sized, - { - Ok(Self(BufferGeometry::new(device, GeometryType::TRIANGLE)?)) - } - - fn kind(&self) -> GeometryType { GeometryType::TRIANGLE } - - fn handle(&self) -> RTCGeometry { self.handle } -} diff --git a/src/instance.rs b/src/instance.rs index 990457de7..ee16566ce 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -1,6 +1,6 @@ use std::{os::raw, sync::Arc}; -use crate::{geometry::Geometry, scene::Scene, sys::*, Device, Format, GeometryType}; +use crate::{scene::Scene, sys::*, Format, GeometryType}; pub struct Instance { /// The scene being instanced @@ -31,19 +31,6 @@ impl Instance { } } -impl Geometry for Instance { - fn new(device: &Device) -> Result - where - Self: Sized, - { - unimplemented!() - } - - fn handle(&self) -> RTCGeometry { self.handle } - - fn kind(&self) -> GeometryType { GeometryType::INSTANCE } -} - impl Drop for Instance { fn drop(&mut self) { unsafe { diff --git a/src/ray_packet.rs b/src/ray_packet.rs index 5906f018b..f9541037b 100644 --- a/src/ray_packet.rs +++ b/src/ray_packet.rs @@ -91,7 +91,7 @@ impl Hit4 { instID: [[u32::MAX; 4]], } } - pub fn any_hit(&self) -> bool { self.hits().fold(false, |acc, g| acc || g) } + pub fn any_hit(&self) -> bool { self.hits().any(|h| h) } pub fn hits<'a>(&'a self) -> impl Iterator + 'a { self.geomID.iter().map(|g| *g != u32::MAX) } diff --git a/src/scene.rs b/src/scene.rs index 05868b867..d631cdabc 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,5 +1,9 @@ use crate::{Error, SceneFlags}; -use std::{collections::HashMap, mem, sync::Arc}; +use std::{ + collections::HashMap, + mem, + sync::{Arc, Mutex}, +}; use crate::{ callback, @@ -12,14 +16,12 @@ 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. +/// A scene containing various geometries. +#[derive(Debug)] pub struct Scene { pub(crate) handle: RTCScene, pub(crate) device: Device, - geometry: HashMap>, + geometries: Arc>>>, } impl Clone for Scene { @@ -28,7 +30,7 @@ impl Clone for Scene { Self { handle: self.handle, device: self.device.clone(), - geometry: self.geometry.clone(), + geometries: self.geometries.clone(), } } } @@ -43,7 +45,7 @@ impl Scene { Ok(Scene { handle, device, - geometry: HashMap::new(), + geometries: Default::default(), }) } } @@ -55,31 +57,63 @@ impl Scene { Ok(scene) } - /// 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); + /// 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<'a>(&'a mut self, geometry: &'a Geometry<'static>) -> 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<'a>(&'a mut self, geometry: &'a Geometry<'static>, 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 + /// Returns the raw underlying handle to the scene, e.g. for passing it to /// native code or ISPC kernels. /// /// # Safety /// - /// The handle is only valid as long as the scene is alive. + /// 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 } /// Commit the scene to build the BVH on top of the geometry to allow diff --git a/src/soa_ray.rs b/src/soa_ray.rs index 6a590956d..10d597a48 100644 --- a/src/soa_ray.rs +++ b/src/soa_ray.rs @@ -138,13 +138,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, - cur: 0, - 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> { @@ -176,11 +170,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, - cur: 0, - len, - } + SoARayIterMut { ray, cur: 0, len } } } @@ -227,13 +217,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, - cur: 0, - 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> { @@ -322,11 +306,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, - cur: 0, - len, - } + SoAHitIterMut { hit, cur: 0, len } } } From 300735330edabe47bc66e2113c14a8fd5d8777ae Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 16 Feb 2023 21:53:19 +0100 Subject: [PATCH 25/65] try to port dynamic scene --- Cargo.toml | 3 - examples/dynamic_scene/Cargo.toml | 11 ++ examples/dynamic_scene/src/main.rs | 177 ++++++++++++++++++++++++++ examples/minimal/Cargo.toml | 2 +- examples/support/src/display.rs | 2 +- examples/triangle_geometry/Cargo.toml | 2 +- src/geometry.rs | 41 ++++-- src/scene.rs | 33 +++++ 8 files changed, 256 insertions(+), 15 deletions(-) create mode 100644 examples/dynamic_scene/Cargo.toml create mode 100644 examples/dynamic_scene/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index f45c30c7f..6f5a2e08c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,9 +21,6 @@ exclude = [ "examples/*" ] -[dependencies] -static_assertions = "1.1.0" - [workspace] members = [ "examples/*", diff --git a/examples/dynamic_scene/Cargo.toml b/examples/dynamic_scene/Cargo.toml new file mode 100644 index 000000000..6742709cb --- /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.22.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..1673a6b61 --- /dev/null +++ b/examples/dynamic_scene/src/main.rs @@ -0,0 +1,177 @@ +//! This example show how to create a dynamic scene. + +use embree::{BufferUsage, BuildQuality, Device, Format, Geometry, IntersectContext, Ray, Scene, SceneFlags}; +use support::Camera; + +const NUM_SPHERES: usize = 20; +const NUM_PHI: usize = 120; +const NUM_THETA: usize = 2 * NUM_PHI; + +fn create_sphere(device: &Device, quality: BuildQuality, pos: glam::Vec3, radius: f32) -> Geometry<'static> { + // Create a triangulated sphere + let mut geometry = device.create_geometry(embree::GeometryType::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(device: &Device) -> Geometry<'static> { + let mut geometry = Geometry::new(device, embree::GeometryType::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, time: f32) { +} + +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 = [glam::Vec3::ZERO; NUM_SPHERES]; + let mut radii = [1.0; NUM_SPHERES]; + let mut colors = [glam::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 * glam::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] = glam::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] = glam::Vec3::new(1.0, 1.0, 1.0); + scene.commit(); + + let display = support::Display::new(512, 512, "Dynamic Scene"); + let light_dir = glam::vec3(1.0, 1.0, 1.0).normalize(); + let mut time = 0; + support::display::run(display, move |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, + ); + // 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 intersection_ctx = IntersectContext::coherent(); + let ray_hit = scene.intersect( + &mut intersection_ctx, + Ray::new(camera.pos.into(), dir.into()), + ); + + if ray_hit.is_valid() { + let p = image.get_pixel_mut(i, j); + let diffuse = colors[ray_hit.hit.geomID as usize]; + + let mut shadow_ray = + Ray::segment(ray_hit.hit_point(), light_dir.into(), 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 + }; + + // Write the color to the image. + p[0] = (color.x * 255.0) as u8; + p[1] = (color.y * 255.0) as u8; + p[2] = (color.z * 255.0) as u8; + } + } + } + time += 1; + }); +} diff --git a/examples/minimal/Cargo.toml b/examples/minimal/Cargo.toml index 579750f41..fc931a474 100644 --- a/examples/minimal/Cargo.toml +++ b/examples/minimal/Cargo.toml @@ -5,6 +5,6 @@ authors = ["Yang Chen "] edition = "2021" [dependencies] -embree = { path = "../../" } +embree = { path = "../.." } support = { path = "../support" } glam = "0.22" diff --git a/examples/support/src/display.rs b/examples/support/src/display.rs index 8c82c7340..ac98b28a3 100644 --- a/examples/support/src/display.rs +++ b/examples/support/src/display.rs @@ -128,7 +128,7 @@ where 1.0, [window_size.width as f32, window_size.height as f32], ); - arcball_camera.zoom(-50.0, 0.16); + arcball_camera.zoom(-30.0, 0.16); arcball_camera.rotate( Vector2::new( window_size.width as f32 / 2.0, diff --git a/examples/triangle_geometry/Cargo.toml b/examples/triangle_geometry/Cargo.toml index e1aaff6dd..53589204a 100644 --- a/examples/triangle_geometry/Cargo.toml +++ b/examples/triangle_geometry/Cargo.toml @@ -6,6 +6,6 @@ edition = "2021" [dependencies] glam = "0.22.0" -embree = { path = "../../" } +embree = { path = "../.." } support = { path = "../support" } diff --git a/src/geometry.rs b/src/geometry.rs index 700204f40..5858270b4 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -5,13 +5,7 @@ use std::{ sync::{Arc, Mutex}, }; -use crate::{sys::*, BufferSlice, BufferUsage, Device, Error, Format, GeometryType}; - -// mod quad_mesh; -// mod triangle_mesh; - -// pub use quad_mesh::QuadMesh; -// pub use triangle_mesh::TriangleMesh; +use crate::{sys::*, BufferSlice, BufferUsage, BuildQuality, Device, Error, Format, GeometryType}; // TODO(yang): maybe enforce format and stride when get the view? /// Information about how a (part of) buffer is bound to a geometry. @@ -67,7 +61,8 @@ impl<'buf> Drop for Geometry<'buf> { } impl<'dev, 'buf> Geometry<'buf> { - pub(crate) fn new(device: &'dev Device, kind: GeometryType) -> Result, Error> { + /// Creates a new geometry object. + pub fn new(device: &'dev Device, kind: GeometryType) -> Result, Error> { let handle = unsafe { rtcNewGeometry(device.handle, kind) }; let vertex_attribute_count = match kind { GeometryType::GRID | GeometryType::USER | GeometryType::INSTANCE => None, @@ -339,7 +334,7 @@ impl<'dev, 'buf> Geometry<'buf> { format: Format, stride: usize, count: usize, - ) -> Result { + ) -> Result, Error> { debug_assert!(stride % 4 == 0, "Stride must be a multiple of 4!"); if usage == BufferUsage::VERTEX_ATTRIBUTE { self.check_vertex_attribute(slot)?; @@ -393,6 +388,34 @@ impl<'dev, 'buf> Geometry<'buf> { } } + /// 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); + } + } + /// Sets the number of vertex attributes of the geometry. /// /// This function sets the number of slots for vertex attributes buffers diff --git a/src/scene.rs b/src/scene.rs index d631cdabc..f13935efa 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -104,6 +104,18 @@ impl Scene { self.geometries.lock().unwrap().remove(&id); } + /// Returns the geometry bound to the specified geometry ID. + pub fn get_geometry(&self, id: u32) -> Option { + let geometry = unsafe { + rtcGetGeometry(self.handle, id) + }; + if geometry.is_null() { + None + } else { + Some(Geometry::new(geometry)) + } + } + /// Returns the raw underlying handle to the scene, e.g. for passing it to /// native code or ISPC kernels. /// @@ -149,6 +161,27 @@ impl Scene { /// Set the build quality of the scene. See [`RTCBuildQuality`] 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: RTCBuildQuality) { unsafe { rtcSetSceneBuildQuality(self.handle, quality); From de8b864bbeb90e6727b61e4a10e9eca800912ca9 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Sat, 18 Feb 2023 00:14:58 +0100 Subject: [PATCH 26/65] add some geometry related methods --- examples/dynamic_scene/src/main.rs | 68 ++++++++++++------- src/device.rs | 16 ++++- src/geometry.rs | 105 +++++++++++++++++++++++++++++ src/scene.rs | 6 +- 4 files changed, 165 insertions(+), 30 deletions(-) diff --git a/examples/dynamic_scene/src/main.rs b/examples/dynamic_scene/src/main.rs index 1673a6b61..8de3a11d8 100644 --- a/examples/dynamic_scene/src/main.rs +++ b/examples/dynamic_scene/src/main.rs @@ -1,32 +1,49 @@ //! This example show how to create a dynamic scene. -use embree::{BufferUsage, BuildQuality, Device, Format, Geometry, IntersectContext, Ray, Scene, SceneFlags}; +use embree::{ + BufferUsage, BuildQuality, Device, Format, Geometry, IntersectContext, Ray, Scene, SceneFlags, +}; use support::Camera; const NUM_SPHERES: usize = 20; const NUM_PHI: usize = 120; const NUM_THETA: usize = 2 * NUM_PHI; -fn create_sphere(device: &Device, quality: BuildQuality, pos: glam::Vec3, radius: f32) -> Geometry<'static> { +fn create_sphere( + device: &Device, + quality: BuildQuality, + pos: glam::Vec3, + radius: f32, +) -> Geometry<'static> { // Create a triangulated sphere - let mut geometry = device.create_geometry(embree::GeometryType::TRIANGLE).unwrap(); + let mut geometry = device + .create_geometry(embree::GeometryType::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 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; @@ -70,7 +87,8 @@ fn create_sphere(device: &Device, quality: BuildQuality, pos: glam::Vec3, radius fn create_ground_plane(device: &Device) -> Geometry<'static> { let mut geometry = Geometry::new(device, embree::GeometryType::TRIANGLE).unwrap(); { - geometry.set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, 4) + geometry + .set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, 4) .unwrap() .view_mut::<[f32; 4]>() .unwrap() @@ -80,7 +98,8 @@ fn create_ground_plane(device: &Device) -> Geometry<'static> { [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) + geometry + .set_new_buffer(BufferUsage::INDEX, 0, Format::UINT3, 12, 2) .unwrap() .view_mut::<[u32; 3]>() .unwrap() @@ -90,8 +109,7 @@ fn create_ground_plane(device: &Device) -> Geometry<'static> { geometry } -fn animate_sphere(scene: &Scene, id: u32, time: f32) { -} +fn animate_sphere(scene: &Scene, id: u32, time: f32) {} fn main() { let device = Device::new().unwrap(); @@ -120,7 +138,11 @@ fn main() { let id = scene.attach_geometry(&sphere); positions[id as usize] = pos; radii[id as usize] = radius; - colors[id as usize] = glam::Vec3::new((i % 16 + 1) as f32 / 17.0, (i % 8 + 1) as f32 / 9.0, (i % 4 + 1) as f32 / 5.0); + colors[id as usize] = glam::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] = glam::Vec3::new(1.0, 1.0, 1.0); diff --git a/src/device.rs b/src/device.rs index 6c285ef4b..48c2d01d1 100644 --- a/src/device.rs +++ b/src/device.rs @@ -27,7 +27,9 @@ impl Device { if handle.is_null() { Err(unsafe { rtcGetDeviceError(ptr::null_mut()) }) } else { - Ok(Device { handle }) + let device = Device { handle }; + device.set_error_function(default_error_function); + Ok(device) } } @@ -38,7 +40,9 @@ impl Device { if handle.is_null() { Err(unsafe { rtcGetDeviceError(ptr::null_mut()) }) } else { - Ok(Device { handle }) + let device = Device { handle }; + device.set_error_function(default_error_function); + Ok(device) } } @@ -49,7 +53,9 @@ impl Device { if handle.is_null() { Err(unsafe { rtcGetDeviceError(ptr::null_mut()) }) } else { - Ok(Device { handle }) + let device = Device { handle }; + device.set_error_function(default_error_function); + Ok(device) } } @@ -387,3 +393,7 @@ pub fn enable_ftz_and_daz() { } } } + +fn default_error_function(error: Error, msg: &str) { + eprintln!("Embree error {:?} - {}", error, msg); +} diff --git a/src/geometry.rs b/src/geometry.rs index 5858270b4..b4ea7a6e7 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -28,6 +28,43 @@ struct GeometryState<'buf> { /// Wrapper around an Embree geometry object. /// +/// 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`] function (to automatically +/// assign a geometry ID) or using the [`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)] @@ -62,6 +99,24 @@ impl<'buf> Drop for Geometry<'buf> { impl<'dev, 'buf> Geometry<'buf> { /// Creates a new geometry object. + /// + /// # Examples + /// + /// ```no_run + /// use embree::{Device, Geometry, GeometryType}; + /// + /// let device = Device::new().unwrap(); + /// let geometry = Geometry::new(&device, GeometryType::TRIANGLE).unwrap(); + /// ``` + /// + /// or use the [`Device::create_geometry`] method: + /// + /// ```no_run + /// use embree::{Device, GeometryType}; + /// + /// let device = Device::new().unwrap(); + /// let geometry = device.create_geometry(GeometryType::TRIANGLE).unwrap(); + /// ``` pub fn new(device: &'dev Device, kind: GeometryType) -> Result, Error> { let handle = unsafe { rtcNewGeometry(device.handle, kind) }; let vertex_attribute_count = match kind { @@ -416,6 +471,56 @@ impl<'dev, 'buf> Geometry<'buf> { } } + /// 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 number of vertex attributes of the geometry. /// /// This function sets the number of slots for vertex attributes buffers diff --git a/src/scene.rs b/src/scene.rs index f13935efa..2121a4e78 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -106,13 +106,11 @@ impl Scene { /// Returns the geometry bound to the specified geometry ID. pub fn get_geometry(&self, id: u32) -> Option { - let geometry = unsafe { - rtcGetGeometry(self.handle, id) - }; + let geometry = unsafe { rtcGetGeometry(self.handle, id) }; if geometry.is_null() { None } else { - Some(Geometry::new(geometry)) + Some(Geometry::new(self.device)) } } From bfae8d46557edd2f265a31dd6ae5ecdddb1c4390 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Sun, 19 Feb 2023 16:04:28 +0100 Subject: [PATCH 27/65] dynamic scene --- examples/dynamic_scene/Cargo.toml | 5 +++ examples/dynamic_scene/src/main.rs | 65 ++++++++++++++++++++++++++---- src/geometry.rs | 26 +++++++++--- src/scene.rs | 9 +++-- 4 files changed, 88 insertions(+), 17 deletions(-) diff --git a/examples/dynamic_scene/Cargo.toml b/examples/dynamic_scene/Cargo.toml index 6742709cb..e7168ebce 100644 --- a/examples/dynamic_scene/Cargo.toml +++ b/examples/dynamic_scene/Cargo.toml @@ -8,4 +8,9 @@ edition = "2021" glam = "0.22.0" embree = { path = "../.." } support = { path = "../support" } +rayon = { version = "1.6.1", optional = true } + +[features] +default = ["rayon"] +rayon = ["dep:rayon"] diff --git a/examples/dynamic_scene/src/main.rs b/examples/dynamic_scene/src/main.rs index 8de3a11d8..395d192b9 100644 --- a/examples/dynamic_scene/src/main.rs +++ b/examples/dynamic_scene/src/main.rs @@ -4,6 +4,7 @@ use embree::{ BufferUsage, BuildQuality, Device, Format, Geometry, IntersectContext, Ray, Scene, SceneFlags, }; use support::Camera; +use glam::{Vec3, vec3}; const NUM_SPHERES: usize = 20; const NUM_PHI: usize = 120; @@ -12,7 +13,7 @@ const NUM_THETA: usize = 2 * NUM_PHI; fn create_sphere( device: &Device, quality: BuildQuality, - pos: glam::Vec3, + pos: Vec3, radius: f32, ) -> Geometry<'static> { // Create a triangulated sphere @@ -109,7 +110,49 @@ fn create_ground_plane(device: &Device) -> Geometry<'static> { geometry } -fn animate_sphere(scene: &Scene, id: u32, time: f32) {} +fn animate_sphere(scene: &Scene, id: u32, pos: Vec3, radius: f32, time: f32) { + let mut geometry = scene.get_geometry(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()); + + #[cfg(feature = "rayon")] + { + use rayon::prelude::*; + 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(); + } + }); + } + #[cfg(not(feature = "rayon"))] + { + for phi_idx in 0..NUM_PHI { + for theta_idx in 0..NUM_THETA { + let phi = phi_idx as f32 * num_phi_rcp * std::f32::consts::PI; + let theta = theta_idx as f32 * num_theta_rcp * 2.0 * std::f32::consts::PI; + let mut v = vertices[phi_idx * NUM_THETA + theta_idx]; + 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(); +} fn main() { let device = Device::new().unwrap(); @@ -120,15 +163,15 @@ fn main() { scene.set_flags(SceneFlags::DYNAMIC | SceneFlags::ROBUST); scene.set_build_quality(BuildQuality::LOW); - let mut positions = [glam::Vec3::ZERO; NUM_SPHERES]; + let mut positions = [Vec3::ZERO; NUM_SPHERES]; let mut radii = [1.0; NUM_SPHERES]; - let mut colors = [glam::Vec3::ZERO; NUM_SPHERES + 1]; + 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 * glam::Vec3::new(phi.sin(), 0.0, -phi.cos()); + let pos = 2.0 * Vec3::new(phi.sin(), 0.0, -phi.cos()); let quality = if i % 2 == 0 { BuildQuality::LOW } else { @@ -138,18 +181,18 @@ fn main() { let id = scene.attach_geometry(&sphere); positions[id as usize] = pos; radii[id as usize] = radius; - colors[id as usize] = glam::Vec3::new( + 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] = glam::Vec3::new(1.0, 1.0, 1.0); + colors[id as usize] = Vec3::new(1.0, 1.0, 1.0); scene.commit(); let display = support::Display::new(512, 512, "Dynamic Scene"); - let light_dir = glam::vec3(1.0, 1.0, 1.0).normalize(); + let light_dir = vec3(1.0, 1.0, 1.0).normalize(); let mut time = 0; support::display::run(display, move |image, camera_pose, _| { for p in image.iter_mut() { @@ -163,6 +206,12 @@ fn main() { 75.0, img_dims, ); + + for i in 0..NUM_SPHERES { + animate_sphere(&scene, i as u32, positions[i], radii[i], time as f32); + } + scene.commit(); + // Render the scene for j in 0..img_dims.1 { for i in 0..img_dims.0 { diff --git a/src/geometry.rs b/src/geometry.rs index b4ea7a6e7..ef04bfc06 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,3 +1,5 @@ +//! + use std::{ collections::HashMap, marker::PhantomData, @@ -28,11 +30,12 @@ struct GeometryState<'buf> { /// Wrapper around an Embree geometry object. /// -/// 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. +/// 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 @@ -434,6 +437,19 @@ impl<'dev, 'buf> Geometry<'buf> { .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) -> GeometryType { self.kind } diff --git a/src/scene.rs b/src/scene.rs index 2121a4e78..292d864b6 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -105,12 +105,13 @@ impl Scene { } /// Returns the geometry bound to the specified geometry ID. - pub fn get_geometry(&self, id: u32) -> Option { - let geometry = unsafe { rtcGetGeometry(self.handle, id) }; - if geometry.is_null() { + pub fn get_geometry(&self, id: u32) -> Option> { + let raw = unsafe { rtcGetGeometry(self.handle, id) }; + if raw.is_null() { None } else { - Some(Geometry::new(self.device)) + let geometries = self.geometries.lock().unwrap(); + geometries.get(&id).cloned() } } From 7f5b1cf90e5262bbf31ebbe5b74ed6bbbaa3ec17 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Sun, 19 Feb 2023 17:33:55 +0100 Subject: [PATCH 28/65] docs --- examples/dynamic_scene/src/main.rs | 10 ++-- src/device.rs | 15 ++++-- src/error.rs | 22 +++++--- src/geometry.rs | 73 ++++++++++++++++++++++++-- src/lib.rs | 25 ++++++++- src/scene.rs | 83 ++++++++++++++++++++++++++---- 6 files changed, 197 insertions(+), 31 deletions(-) diff --git a/examples/dynamic_scene/src/main.rs b/examples/dynamic_scene/src/main.rs index 395d192b9..89631d18d 100644 --- a/examples/dynamic_scene/src/main.rs +++ b/examples/dynamic_scene/src/main.rs @@ -3,8 +3,8 @@ use embree::{ BufferUsage, BuildQuality, Device, Format, Geometry, IntersectContext, Ray, Scene, SceneFlags, }; +use glam::{vec3, Vec3}; use support::Camera; -use glam::{Vec3, vec3}; const NUM_SPHERES: usize = 20; const NUM_PHI: usize = 120; @@ -111,7 +111,7 @@ fn create_ground_plane(device: &Device) -> Geometry<'static> { } fn animate_sphere(scene: &Scene, id: u32, pos: Vec3, radius: f32, time: f32) { - let mut geometry = scene.get_geometry(id).unwrap(); + let mut geometry = scene.get_geometry_unchecked(id).unwrap(); let mut vertices = geometry .get_buffer(BufferUsage::VERTEX, 0) .unwrap() @@ -193,7 +193,7 @@ fn main() { let display = support::Display::new(512, 512, "Dynamic Scene"); let light_dir = vec3(1.0, 1.0, 1.0).normalize(); - let mut time = 0; + let mut time = 0.0; support::display::run(display, move |image, camera_pose, _| { for p in image.iter_mut() { *p = 0; @@ -208,7 +208,7 @@ fn main() { ); for i in 0..NUM_SPHERES { - animate_sphere(&scene, i as u32, positions[i], radii[i], time as f32); + animate_sphere(&scene, i as u32, positions[i], radii[i], time); } scene.commit(); @@ -243,6 +243,6 @@ fn main() { } } } - time += 1; + time += 1.0; }); } diff --git a/src/device.rs b/src/device.rs index 48c2d01d1..2934810e1 100644 --- a/src/device.rs +++ b/src/device.rs @@ -185,22 +185,27 @@ impl Device { /// # 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: RTCDeviceProperty) -> 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 + /// 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 `error_code`. + /// error occurred since the last invocation of [`Device::get_error`]. /// /// # Returns /// - /// Error code encoded as `RTCError`. + /// Error code encoded as [`Error`]. pub fn get_error(&self) -> RTCError { unsafe { rtcGetDeviceError(self.handle) } } /// Creates a new scene bound to the device. diff --git a/src/error.rs b/src/error.rs index 2874a05b9..c06ac0078 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,17 +5,25 @@ 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"), - RTCError::UNKNOWN => write!(f, "Unknown error"), - RTCError::INVALID_ARGUMENT => write!(f, "Invalid argument"), + 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, "Invalid operation.") + write!(f, "The operation is not allowed for the specified object.") } - RTCError::OUT_OF_MEMORY => write!(f, "Out of memory"), - RTCError::UNSUPPORTED_CPU => write!(f, "Unsupported CPU"), + 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, - "Cancelled by a memory monitor callback or progress monitor callback function" + "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 ef04bfc06..4bd8766f9 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -622,7 +622,7 @@ impl<'dev, 'buf> Geometry<'buf> { } macro_rules! impl_geometry_type { - ($name:ident, $kind:path, #[$doc:meta]) => { + ($name:ident, $kind:path, $(#[$meta:meta])*) => { #[derive(Debug)] pub struct $name(Geometry<'static>); @@ -636,8 +636,8 @@ macro_rules! impl_geometry_type { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } + $(#[$meta])* impl $name { - #[$doc] pub fn new(device: &Device) -> Result { Ok(Self(Geometry::new(device, $kind)?)) } @@ -647,5 +647,70 @@ macro_rules! impl_geometry_type { use std::ops::{Deref, DerefMut}; -impl_geometry_type!(TriangleMesh, GeometryType::TRIANGLE, #[doc = "A triangle mesh geometry."]); -impl_geometry_type!(QuadMesh, GeometryType::QUAD, #[doc = "A quad mesh geometry."]); +impl_geometry_type!(TriangleMesh, GeometryType::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, GeometryType::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 +); diff --git a/src/lib.rs b/src/lib.rs index 7b1a6efd9..702c955b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,7 +53,7 @@ pub use sys::{ }; pub use sys::{ - RTCBuildFlags as BuildFlags, RTCCurveFlags as CurveFlags, + RTCBounds as Bounds, RTCBuildFlags as BuildFlags, RTCCurveFlags as CurveFlags, RTCIntersectContextFlags as IntersectContextFlags, RTCSceneFlags as SceneFlags, }; @@ -61,6 +61,29 @@ pub use sys::{ /// `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] } +} + /// Utility for making specifically aligned vectors pub fn aligned_vector(len: usize, align: usize) -> Vec { let t_size = mem::size_of::(); diff --git a/src/scene.rs b/src/scene.rs index 292d864b6..5426d79a1 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,4 +1,4 @@ -use crate::{Error, SceneFlags}; +use crate::{Bounds, Error, SceneFlags}; use std::{ collections::HashMap, mem, @@ -57,6 +57,9 @@ impl Scene { 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 @@ -105,7 +108,14 @@ impl Scene { } /// Returns the geometry bound to the specified geometry ID. - pub fn get_geometry(&self, id: u32) -> Option> { + /// + /// 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 @@ -115,6 +125,8 @@ impl Scene { } } + // TODO: add get_geometry with rtcGetGeometryThreadSafe + /// Returns the raw underlying handle to the scene, e.g. for passing it to /// native code or ISPC kernels. /// @@ -127,18 +139,70 @@ impl Scene { /// destroyed. pub unsafe fn handle(&self) -> RTCScene { self.handle } - /// Commit the scene to build the BVH on top of the geometry to allow - /// for ray tracing the scene using the intersect/occluded methods + /// 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); } } + /// 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 [`RTCSceneFlags`] 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: RTCSceneFlags) { unsafe { rtcSetSceneFlags(self.handle, flags); @@ -153,10 +217,10 @@ impl Scene { /// use embree::{Device, Scene, SceneFlags}; /// let device = Device::new().unwrap(); /// let scene = device.create_scene().unwrap(); - /// let flags = scene.flags(); + /// 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) -> RTCSceneFlags { unsafe { rtcGetSceneFlags(self.handle) } } /// Set the build quality of the scene. See [`RTCBuildQuality`] for all /// possible values. @@ -331,8 +395,9 @@ impl Scene { } } - 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, @@ -343,7 +408,7 @@ impl Scene { align1: 0.0, }; unsafe { - rtcGetSceneBounds(self.handle(), &mut bounds as *mut RTCBounds); + rtcGetSceneBounds(self.handle(), &mut bounds as *mut Bounds); } bounds } From 1fd75fc0a0800eb846bf654db0dc9082c1ba9b19 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Sun, 19 Feb 2023 17:47:54 +0100 Subject: [PATCH 29/65] remove geometry state --- src/geometry.rs | 90 ++++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/src/geometry.rs b/src/geometry.rs index 4bd8766f9..4b44d0fe0 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -21,13 +21,6 @@ pub(crate) struct AttachedBuffer<'src> { source: BufferSlice<'src>, } -/// State of a geometry object. -#[derive(Default, Debug, Clone)] -struct GeometryState<'buf> { - vertex_attribute_count: Option, - attachments: HashMap>>, -} - /// Wrapper around an Embree geometry object. /// /// A new geometry is created using [`Device::create_geometry`] or @@ -75,7 +68,7 @@ pub struct Geometry<'buf> { pub(crate) device: Device, pub(crate) handle: RTCGeometry, kind: GeometryType, - state: Arc>>, + attachments: Arc>>>>, } impl<'buf> Clone for Geometry<'buf> { @@ -87,7 +80,7 @@ impl<'buf> Clone for Geometry<'buf> { device: self.device.clone(), handle: self.handle, kind: self.kind, - state: self.state.clone(), + attachments: self.attachments.clone(), } } } @@ -122,10 +115,6 @@ impl<'dev, 'buf> Geometry<'buf> { /// ``` pub fn new(device: &'dev Device, kind: GeometryType) -> Result, Error> { let handle = unsafe { rtcNewGeometry(device.handle, kind) }; - let vertex_attribute_count = match kind { - GeometryType::GRID | GeometryType::USER | GeometryType::INSTANCE => None, - _ => Some(0), - }; if handle.is_null() { Err(device.get_error()) } else { @@ -133,14 +122,36 @@ impl<'dev, 'buf> Geometry<'buf> { device: device.clone(), handle, kind, - state: Arc::new(Mutex::new(GeometryState { - vertex_attribute_count, - attachments: HashMap::new(), - })), + attachments: Arc::new(Mutex::new(HashMap::new())), }) } } + /// 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 @@ -154,25 +165,15 @@ impl<'dev, 'buf> Geometry<'buf> { /// Checks if the given vertex attribute slot is valid for this geometry. fn check_vertex_attribute(&self, slot: u32) -> Result<(), Error> { - match self.state.lock().unwrap().vertex_attribute_count { - None => { + match self.kind { + GeometryType::GRID | GeometryType::USER | GeometryType::INSTANCE => { eprint!( "Vertex attribute not allowed for geometries of type {:?}!", self.kind ); Err(Error::INVALID_OPERATION) } - Some(c) => { - if slot >= c { - eprint!( - "Vertex attribute slot {} is out of bounds for geometry of type {:?}!", - slot, self.kind - ); - Err(Error::INVALID_ARGUMENT) - } else { - Ok(()) - } - } + _ => Ok(()) } } @@ -229,8 +230,8 @@ impl<'dev, 'buf> Geometry<'buf> { stride, count ); - let mut state = self.state.lock().unwrap(); - let bindings = state.attachments.entry(usage).or_insert_with(Vec::new); + 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. @@ -295,8 +296,8 @@ impl<'dev, 'buf> Geometry<'buf> { BufferSlice::User { ptr, offset, size, .. } => { - let mut state = self.state.lock().unwrap(); - let bindings = state.attachments.entry(usage).or_insert_with(Vec::new); + 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. @@ -398,8 +399,8 @@ impl<'dev, 'buf> Geometry<'buf> { self.check_vertex_attribute(slot)?; } { - let mut state = self.state.lock().unwrap(); - let bindings = state.attachments.entry(usage).or_insert_with(Vec::new); + 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) @@ -429,9 +430,8 @@ impl<'dev, 'buf> Geometry<'buf> { /// Returns the buffer bound to the given slot and usage. pub fn get_buffer(&self, usage: BufferUsage, slot: u32) -> Option { - let state = self.state.lock().unwrap(); - state - .attachments + let attachments = self.attachments.lock().unwrap(); + attachments .get(&usage) .and_then(|v| v.iter().find(|a| a.slot == slot)) .map(|a| a.source) @@ -550,20 +550,18 @@ impl<'dev, 'buf> Geometry<'buf> { /// /// * `count` - The number of vertex attribute slots. pub fn set_vertex_attribute_count(&mut self, count: u32) { - let mut state = self.state.lock().unwrap(); - match state.vertex_attribute_count { - None => { - panic!( - "set_vertex_attribute_count is not supported by geometry of type {:?}.", + match self.kind { + GeometryType::GRID | GeometryType::USER | GeometryType::INSTANCE => { + eprint!( + "Vertex attribute not allowed for geometries of type {:?}!", self.kind ); } - Some(_) => { + _ => { // Update the vertex attribute count. unsafe { rtcSetGeometryVertexAttributeCount(self.handle, count); } - state.vertex_attribute_count = Some(count); } } } From 0597773db0bdf9ad5c0ce6985417f6fcbc979256 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Sun, 19 Feb 2023 18:08:06 +0100 Subject: [PATCH 30/65] set geometry mask --- src/geometry.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/geometry.rs b/src/geometry.rs index 4b44d0fe0..c173a3e34 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -487,6 +487,26 @@ impl<'dev, 'buf> Geometry<'buf> { } } + /// 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`] 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. /// From 0883655be7ef9a1ece9c9f3ae294f39e208caa24 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Mon, 20 Feb 2023 21:20:14 +0100 Subject: [PATCH 31/65] implementation of geometry related callback functions --- examples/dynamic_scene/src/main.rs | 4 +- src/callback.rs | 199 ++++++- src/device.rs | 4 +- src/geometry.rs | 779 +++++++++++++++++++++++++-- src/instance.rs | 4 +- src/lib.rs | 86 +-- src/ray.rs | 150 ++++-- src/{ray_packet.rs => ray/packet.rs} | 33 +- src/{soa_ray.rs => ray/soa.rs} | 0 src/{ray_stream.rs => ray/stream.rs} | 2 +- src/scene.rs | 37 +- 11 files changed, 1177 insertions(+), 121 deletions(-) rename src/{ray_packet.rs => ray/packet.rs} (85%) rename src/{soa_ray.rs => ray/soa.rs} (100%) rename src/{ray_stream.rs => ray/stream.rs} (98%) diff --git a/examples/dynamic_scene/src/main.rs b/examples/dynamic_scene/src/main.rs index 89631d18d..699875eb3 100644 --- a/examples/dynamic_scene/src/main.rs +++ b/examples/dynamic_scene/src/main.rs @@ -18,7 +18,7 @@ fn create_sphere( ) -> Geometry<'static> { // Create a triangulated sphere let mut geometry = device - .create_geometry(embree::GeometryType::TRIANGLE) + .create_geometry(embree::GeometryKind::TRIANGLE) .unwrap(); geometry.set_build_quality(quality); @@ -86,7 +86,7 @@ fn create_sphere( } fn create_ground_plane(device: &Device) -> Geometry<'static> { - let mut geometry = Geometry::new(device, embree::GeometryType::TRIANGLE).unwrap(); + let mut geometry = Geometry::new(device, embree::GeometryKind::TRIANGLE).unwrap(); { geometry .set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, 4) diff --git a/src/callback.rs b/src/callback.rs index 07c19b70e..4d343fa2c 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -1,10 +1,13 @@ +use crate::{ + geometry::GeometryUserData, Bounds, IntersectContext, UserData, +}; 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 +pub fn progress_monitor_function_helper(_f: &mut F) -> RTCProgressMonitorFunction where F: FnMut(f64) -> bool, { @@ -20,7 +23,7 @@ where } /// Helper function to convert a Rust closure to `RTCErrorFunction` callback. -pub(crate) fn error_function_helper(_f: &mut F) -> RTCErrorFunction +pub fn error_function_helper(_f: &mut F) -> RTCErrorFunction where F: FnMut(RTCError, &'static str), { @@ -40,7 +43,7 @@ where /// Helper function to convert a Rust closure to `RTCMemoryMonitorFunction` /// callback. -pub(crate) fn memory_monitor_function_helper(_f: &mut F) -> RTCMemoryMonitorFunction +pub fn memory_monitor_function_helper(_f: &mut F) -> RTCMemoryMonitorFunction where F: FnMut(isize, bool) -> bool, { @@ -54,3 +57,193 @@ where Some(inner::) } + +// TODO: deal with RTCRayHitN, convert it to a SOA struct +/// Helper function to convert a Rust closure to `RTCIntersectFunctionN` +/// callback. +pub fn user_intersect_function_helper(_f: &mut F) -> RTCIntersectFunctionN +where + D: UserData, + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayHitN, u32), +{ + unsafe extern "C" fn inner(args: *const RTCIntersectFunctionNArguments) + where + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayHitN, u32), + { + let cb_ptr = + (*((*args).geometryUserPtr as *mut GeometryUserData)).user_intersect_payload as *mut F; + if !cb_ptr.is_null() { + let cb = &mut *cb_ptr; + let user_data = { + let user_data_ptr = (*((*args).geometryUserPtr as *mut GeometryUserData)).data; + user_data_ptr + .is_null() + .then(|| &mut *(user_data_ptr as *mut D)) + }; + cb( + std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), + user_data, + (*args).geomID, + (*args).primID, + &mut *(*args).context, + &mut *(*args).rayhit, + (*args).N, + ); + } + } + + Some(inner::) +} + +// TODO: deal with RTCRayN +/// Helper function to convert a Rust closure to `RTCOccludedFunctionN` +/// callback. +pub fn user_occluded_function_helper(_f: &mut F) -> RTCOccludedFunctionN +where + D: UserData, + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayN, u32), +{ + unsafe extern "C" fn inner(args: *const RTCOccludedFunctionNArguments) + where + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayN, u32), + { + let cb_ptr = + (*((*args).geometryUserPtr as *mut GeometryUserData)).user_occluded_payload as *mut F; + if !cb_ptr.is_null() { + let cb = &mut *cb_ptr; + let user_data = { + let user_data_ptr = (*((*args).geometryUserPtr as *mut GeometryUserData)).data; + user_data_ptr + .is_null() + .then(|| &mut *(user_data_ptr as *mut D)) + }; + cb( + std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), + user_data, + (*args).geomID, + (*args).primID, + &mut *(*args).context, + &mut *(*args).ray, + (*args).N, + ) + } + } + + Some(inner::) +} + +/// Helper function to convert a Rust closure to `RTCFilterFunctionN` callback +/// for intersect. +pub fn intersect_filter_function_helper(_f: &mut F) -> RTCFilterFunctionN +where + D: UserData, + F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, &mut RTCRayN, &mut RTCHitN, u32), +{ + unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) + where + F: FnMut( + &mut [i32], + Option<&mut D>, + &mut IntersectContext, + &mut RTCRayN, + &mut RTCHitN, + u32, + ), + { + let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryUserData)).intersect_filter_payload + as *mut F; + if !cb_ptr.is_null() { + let cb = &mut *cb_ptr; + let user_data = { + let user_data_ptr = (*((*args).geometryUserPtr as *mut GeometryUserData)).data; + user_data_ptr + .is_null() + .then(|| &mut *(user_data_ptr as *mut D)) + }; + cb( + std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), + user_data, + &mut *(*args).context, + &mut *(*args).ray, + &mut *(*args).hit, + (*args).N, + ); + } + } + + Some(inner::) +} + +/// Helper function to convert a Rust closure to `RTCFilterFunctionN` callback +/// for occuluded. +pub fn occluded_filter_function_helper(_f: &mut F) -> RTCFilterFunctionN +where + D: UserData, + F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, &mut RTCRayN, &mut RTCHitN, u32), +{ + unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) + where + F: FnMut( + &mut [i32], + Option<&mut D>, + &mut IntersectContext, + &mut RTCRayN, + &mut RTCHitN, + u32, + ), + { + let cb_ptr = + (*((*args).geometryUserPtr as *mut GeometryUserData)).occluded_filter_payload as *mut F; + if !cb_ptr.is_null() { + let cb = &mut *cb_ptr; + let user_data = { + let user_data_ptr = (*((*args).geometryUserPtr as *mut GeometryUserData)).data; + user_data_ptr + .is_null() + .then(|| &mut *(user_data_ptr as *mut D)) + }; + cb( + std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), + user_data, + &mut *(*args).context, + &mut *(*args).ray, + &mut *(*args).hit, + (*args).N, + ); + } + } + + Some(inner::) +} + +/// Helper function to convert a Rust closure to `RTCBoundsFunction` callback. +pub fn user_bounds_function_helper(_f: &mut F) -> RTCBoundsFunction +where + D: UserData, + F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), +{ + unsafe extern "C" fn inner(args: *const RTCBoundsFunctionArguments) + where + F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), + { + let cb_ptr = + (*((*args).geometryUserPtr as *mut GeometryUserData)).user_bounds_payload as *mut F; + if !cb_ptr.is_null() { + let cb = &mut *cb_ptr; + let user_data = { + let user_data_ptr = (*((*args).geometryUserPtr as *mut GeometryUserData)).data; + user_data_ptr + .is_null() + .then(|| &mut *(user_data_ptr as *mut D)) + }; + cb( + user_data, + (*args).primID, + (*args).timeStep, + &mut *(*args).bounds_o, + ); + } + } + + Some(inner::) +} diff --git a/src/device.rs b/src/device.rs index 2934810e1..298b22e1d 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,4 +1,4 @@ -use crate::{sys::*, Buffer, BufferSize, Error, Geometry, GeometryType, Scene}; +use crate::{sys::*, Buffer, BufferSize, Error, Geometry, GeometryKind, Scene}; use std::{ ffi::CString, fmt::{self, Display, Formatter}, @@ -227,7 +227,7 @@ impl Device { /// Creates a [`Geometry`] object bound to the device without any /// buffers attached. - pub fn create_geometry(&self, kind: GeometryType) -> Result, Error> { + pub fn create_geometry(&self, kind: GeometryKind) -> Result, Error> { Geometry::new(self, kind) } } diff --git a/src/geometry.rs b/src/geometry.rs index c173a3e34..a22e8e8d4 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,13 +1,17 @@ -//! - use std::{ collections::HashMap, marker::PhantomData, num::NonZeroUsize, + ptr, sync::{Arc, Mutex}, }; -use crate::{sys::*, BufferSlice, BufferUsage, BuildQuality, Device, Error, Format, GeometryType}; +use crate::{ + sys::*, Bounds, BufferSlice, BufferUsage, BuildQuality, Device, Error, Format, GeometryKind, + IntersectContext, QuaternionDecomposition, Scene, +}; + +use std::ops::{Deref, DerefMut}; // TODO(yang): maybe enforce format and stride when get the view? /// Information about how a (part of) buffer is bound to a geometry. @@ -21,6 +25,51 @@ pub(crate) struct AttachedBuffer<'src> { source: BufferSlice<'src>, } +/// Trait for user-defined data that can be attached to a geometry. +pub trait UserData: Sized + Send + Sync + 'static {} + +impl UserData for T where T: Sized + Send + Sync + 'static {} + +/// 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, Copy, Clone)] +pub(crate) struct GeometryUserData { + /// Pointer to the user-defined data. + pub data: *mut std::os::raw::c_void, + /// Payload for the [`Geometry::set_intersect_filter_function`] call. + pub intersect_filter_payload: *mut std::os::raw::c_void, + /// Payload for the [`Geometry::set_occluded_filter_function`] call. + pub occluded_filter_payload: *mut std::os::raw::c_void, + /// Payload for the [`Geometry::set_user_intersect_function`] call. + pub user_intersect_payload: *mut std::os::raw::c_void, + /// Payload for the [`Geometry::set_user_occluded_function`] call. + pub user_occluded_payload: *mut std::os::raw::c_void, + /// Payload for the [`Geometry::set_user_bounds_function`] call. + pub user_bounds_payload: *mut std::os::raw::c_void, +} + +impl Default for GeometryUserData { + fn default() -> Self { + Self { + data: ptr::null_mut(), + user_intersect_payload: ptr::null_mut(), + user_occluded_payload: ptr::null_mut(), + intersect_filter_payload: ptr::null_mut(), + occluded_filter_payload: ptr::null_mut(), + user_bounds_payload: ptr::null_mut(), + } + } +} + +/// Extra data for a geometry. +#[derive(Debug, Clone, Default)] +struct GeometryState<'buf> { + attachments: HashMap>>, + user_data: GeometryUserData, +} + /// Wrapper around an Embree geometry object. /// /// A new geometry is created using [`Device::create_geometry`] or @@ -33,10 +82,11 @@ pub(crate) struct AttachedBuffer<'src> { /// 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`] function (to automatically -/// assign a geometry ID) or using the [`Scene::attach_geometry_by_id`] function -/// (to specify the geometry ID manually). A geometry can get attached to -/// multiple scenes. +/// 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 @@ -67,8 +117,8 @@ pub(crate) struct AttachedBuffer<'src> { pub struct Geometry<'buf> { pub(crate) device: Device, pub(crate) handle: RTCGeometry, - kind: GeometryType, - attachments: Arc>>>>, + kind: GeometryKind, + state: Arc>>, } impl<'buf> Clone for Geometry<'buf> { @@ -80,7 +130,7 @@ impl<'buf> Clone for Geometry<'buf> { device: self.device.clone(), handle: self.handle, kind: self.kind, - attachments: self.attachments.clone(), + state: Arc::new(Mutex::new(GeometryState::default())), } } } @@ -93,27 +143,27 @@ impl<'buf> Drop for Geometry<'buf> { } } -impl<'dev, 'buf> Geometry<'buf> { +impl<'buf> Geometry<'buf> { /// Creates a new geometry object. /// /// # Examples /// /// ```no_run - /// use embree::{Device, Geometry, GeometryType}; + /// use embree::{Device, Geometry, GeometryKind}; /// /// let device = Device::new().unwrap(); - /// let geometry = Geometry::new(&device, GeometryType::TRIANGLE).unwrap(); + /// let geometry = Geometry::new(&device, GeometryKind::TRIANGLE).unwrap(); /// ``` /// /// or use the [`Device::create_geometry`] method: /// /// ```no_run - /// use embree::{Device, GeometryType}; + /// use embree::{Device, GeometryKind}; /// /// let device = Device::new().unwrap(); - /// let geometry = device.create_geometry(GeometryType::TRIANGLE).unwrap(); + /// let geometry = device.create_geometry(GeometryKind::TRIANGLE).unwrap(); /// ``` - pub fn new(device: &'dev Device, kind: GeometryType) -> Result, Error> { + 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()) @@ -122,7 +172,7 @@ impl<'dev, 'buf> Geometry<'buf> { device: device.clone(), handle, kind, - attachments: Arc::new(Mutex::new(HashMap::new())), + state: Arc::new(Mutex::new(Default::default())), }) } } @@ -163,17 +213,19 @@ impl<'dev, 'buf> Geometry<'buf> { /// destroyed. pub unsafe fn handle(&self) -> RTCGeometry { self.handle } - /// Checks if the given vertex attribute slot is valid for this geometry. - fn check_vertex_attribute(&self, slot: u32) -> Result<(), Error> { + /// 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 { - GeometryType::GRID | GeometryType::USER | GeometryType::INSTANCE => { + GeometryKind::GRID | GeometryKind::USER | GeometryKind::INSTANCE => { eprint!( "Vertex attribute not allowed for geometries of type {:?}!", self.kind ); Err(Error::INVALID_OPERATION) } - _ => Ok(()) + _ => Ok(()), } } @@ -215,7 +267,7 @@ impl<'dev, 'buf> Geometry<'buf> { ) -> Result<(), Error> { debug_assert!(stride % 4 == 0, "Stride must be a multiple of 4!"); if usage == BufferUsage::VERTEX { - self.check_vertex_attribute(slot)?; + self.check_vertex_attribute()?; } match slice { BufferSlice::Buffer { @@ -230,8 +282,8 @@ impl<'dev, 'buf> Geometry<'buf> { stride, count ); - let mut attachments = self.attachments.lock().unwrap(); - let bindings = attachments.entry(usage).or_insert_with(Vec::new); + let mut state = self.state.lock().unwrap(); + let bindings = state.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. @@ -296,8 +348,8 @@ impl<'dev, 'buf> Geometry<'buf> { BufferSlice::User { ptr, offset, size, .. } => { - let mut attachments = self.attachments.lock().unwrap(); - let bindings = attachments.entry(usage).or_insert_with(Vec::new); + let mut state = self.state.lock().unwrap(); + let bindings = state.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. @@ -396,11 +448,11 @@ impl<'dev, 'buf> Geometry<'buf> { ) -> Result, Error> { debug_assert!(stride % 4 == 0, "Stride must be a multiple of 4!"); if usage == BufferUsage::VERTEX_ATTRIBUTE { - self.check_vertex_attribute(slot)?; + self.check_vertex_attribute()?; } { - let mut attachments = self.attachments.lock().unwrap(); - let bindings = attachments.entry(usage).or_insert_with(Vec::new); + let mut state = self.state.lock().unwrap(); + let bindings = state.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) @@ -411,7 +463,7 @@ impl<'dev, 'buf> Geometry<'buf> { let slice = BufferSlice::GeometryLocal { ptr: raw_ptr, size: NonZeroUsize::new(count * stride).unwrap(), - marker: std::marker::PhantomData, + marker: PhantomData, }; bindings.push(AttachedBuffer { slot, @@ -430,8 +482,9 @@ impl<'dev, 'buf> Geometry<'buf> { /// 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 + let state = self.state.lock().unwrap(); + state + .attachments .get(&usage) .and_then(|v| v.iter().find(|a| a.slot == slot)) .map(|a| a.source) @@ -451,7 +504,7 @@ impl<'dev, 'buf> Geometry<'buf> { } /// Returns the type of geometry of this geometry. - pub fn kind(&self) -> GeometryType { self.kind } + pub fn kind(&self) -> GeometryKind { self.kind } pub fn commit(&mut self) { unsafe { @@ -487,6 +540,473 @@ impl<'dev, 'buf> Geometry<'buf> { } } + /// 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. + /// Passing `None` as the filter function removes the 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::intersect1M`], + /// [`Scene::intersect1Mp`], [`Scene::intersectNM`], or + /// [`Scene::intersectNp`] 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. + /// + /// The implementation of the filter function can choose to implement a + /// single code path that uses the ray access helper functions + /// `RTCRay_XXX` and hit access helper functions `RTCHit_XXX` to access + /// ray and hit data. Alternatively the code can branch to optimized + /// implementations for specific sizes of N and cast the ray + /// and hit inputs to the proper packet types. + /// + /// # Safety + /// + /// Because the Embree filter function does not provide the user + /// data pointer, we cannot use a closure but instead a static function + /// pointer. + pub unsafe fn set_intersect_filter_function(&mut self, filter: F) + where + D: UserData, + F: FnMut( + &mut [i32], + Option<&mut D>, + &mut IntersectContext, + &mut RTCRayN, + &mut RTCHitN, + u32, + ), + { + let mut state = self.state.lock().unwrap(); + unsafe { + let mut closure = filter; + state.user_data.intersect_filter_payload = + &mut closure as *mut _ as *mut std::os::raw::c_void; + rtcSetGeometryIntersectFilterFunction( + self.handle, + crate::callback::intersect_filter_function_helper(&mut closure), + ); + } + } + + /// 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. + /// Passing `None` as the filter function removes the 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. + /// + /// # Safety + /// + /// Because the Embree filter function does not provide the user + /// data pointer, we cannot use a closure but instead a static function + /// pointer. + pub unsafe fn set_occluded_filter_function(&mut self, filter: F) + where + D: UserData, + F: FnMut( + &mut [i32], + Option<&mut D>, + &mut IntersectContext, + &mut RTCRayN, + &mut RTCHitN, + u32, + ), + { + let mut state = self.state.lock().unwrap(); + unsafe { + let mut closure = filter; + state.user_data.occluded_filter_payload = + &mut closure as *mut _ as *mut std::os::raw::c_void; + rtcSetGeometryOccludedFilterFunction( + self.handle, + crate::callback::occluded_filter_function_helper(&mut closure), + ); + } + } + + /// 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. + /// Passing `None` as function pointer disables the registered callback + /// 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_user_bounds_function(&mut self, bounds: F) + where + D: UserData, + F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), + { + match self.kind { + GeometryKind::USER => unsafe { + let mut state = self.state.lock().unwrap(); + let mut closure = bounds; + state.user_data.user_bounds_payload = + &mut closure as *mut _ as *mut std::os::raw::c_void; + rtcSetGeometryBoundsFunction( + self.handle, + crate::callback::user_bounds_function_helper(&mut closure), + ptr::null_mut(), + ); + }, + _ => panic!("Only user-defined geometries can have a bounds function"), + } + } + + // TODO(yang): deal with RTCRayHitN, then we can make this function safe + /// Sets the callback function to intersect a user geometry. + /// + /// Only a single callback function can be registered per geometry and + /// further invocations overwrite the previously set callback function. + /// Passing `None` as function pointer disables the registered callback + /// function. + /// + /// 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. The callback function of type + /// [`RTCIntersectFunctionN`] gets passed a number of arguments through + /// the [`RTCIntersectFunctionNArguments`] structure. The value N + /// specifies the ray packet size, valid points to an array of + /// integers that specify whether the corresponding ray is valid (-1) or + /// invalid (0), the `geometryUserPtr` member points to the geometry + /// user data previously set through [`Geometry::set_user_data`], the + /// context member points to the intersection context passed to the ray + /// query, the rayhit member points to a ray and hit packet of variable + /// size N, and the geomID and primID member identifies the geometry ID + /// and primitive ID of the primitive to intersect. The ray component of + /// the rayhit structure contains valid data, in particular + /// the tfar value is the current closest hit distance found. All data + /// inside the hit component of the rayhit structure are undefined and + /// should not be read by the function. + /// 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 was found in + /// the valid range (from tnear to tfar), it should update the hit distance + /// of the ray (tfar member) and the hit (u, v, Ng, 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. + /// + /// 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 rtcFilterIntersection call. Within the user geometry intersect + /// function, it is safe to trace new rays and create new scenes and + /// geometries. When performing ray queries using rtcIntersect1, it is + /// guaranteed that the packet size is 1 when the callback is invoked. + /// When performing ray queries using the rtcIntersect4/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 rtcGetDevice- Property. When performing ray queries + /// using the stream API such as rtcIntersect1M, rtcIntersect1Mp, + /// rtcIntersectNM, or rtcIntersectNp the or- der 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 unsafe fn set_user_intersect_function(&mut self, intersect: F) + where + D: UserData, + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayHitN, u32), + { + // TODO: deal with RTCRayHitN + match self.kind { + GeometryKind::USER => unsafe { + let mut state = self.state.lock().unwrap(); + let mut closure = intersect; + state.user_data.user_intersect_payload = + &mut closure as *mut _ as *mut std::os::raw::c_void; + rtcSetGeometryIntersectFunction( + self.handle, + crate::callback::user_intersect_function_helper(&mut closure), + ); + }, + _ => panic!("Only user-defined geometries can have an intersect function"), + } + } + + // TODO(yang): deal with RTCRayN, then we can make this function safe + /// Sets the callback function to occlude a user geometry. + /// + /// Similar to [`Geometry::set_user_intersect_function`], but for occlusion + /// queries. + pub unsafe fn set_user_occluded_function(&mut self, occluded: F) + where + D: UserData, + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayN, u32), + { + // TODO: deal with RTCRayN + match self.kind { + GeometryKind::USER => unsafe { + let mut state = self.state.lock().unwrap(); + let mut closure = occluded; + state.user_data.user_occluded_payload = + &mut closure as *mut _ as *mut std::os::raw::c_void; + rtcSetGeometryOccludedFunction( + self.handle, + crate::callback::user_occluded_function_helper(&mut closure), + ); + }, + _ => panic!("Only user-defined geometries can have an occluded function"), + } + } + + /// 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. + /// Passing `None` as function pointer disables the registered callback + /// 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_func: RTCPointQueryFunction) { + rtcSetGeometryPointQueryFunction(self.handle, query_func); + } + + /// 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 an instanced scene"), + } + } + + // TODO(yang): Better transform type + /// Returns the interpolated instance transformation for the specified time + /// step. + pub fn get_geometry_transform(&mut self, time: f32, format: Format) -> [f32; 16] { + match self.kind { + GeometryKind::INSTANCE => unsafe { + let mut transform = [0.0; 16]; + rtcGetGeometryTransform( + self.handle, + time, + format, + transform.as_mut_ptr() as *mut _, + ); + transform + }, + _ => { + panic!("Geometry::get_geometry_transform is only supported for instance geometries") + } + } + } + + // TODO(yang): Better transform type + /// Sets the transformation for a particular time step of an instance + /// geometry. + pub fn set_geometry_transform(&mut self, time_step: u32, format: Format, transform: &[f32]) { + match self.kind { + GeometryKind::INSTANCE => unsafe { + rtcSetGeometryTransform( + self.handle, + time_step, + format, + transform.as_ptr() as *const _, + ); + }, + _ => { + panic!("Geometry::set_geometry_transform is only supported for instance geometries") + } + } + } + + /// 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!("Geometry::set_geometry_transform is only supported for instance geometries") + } + } + } + + /// 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 @@ -557,6 +1077,61 @@ impl<'dev, 'buf> Geometry<'buf> { } } + /// Sets the number of topologies of a subdivision geometry. + /// + /// The number of topologies of a subdivision geometry must be greater + /// or equal to 1. + /// + /// 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!("Geometry::set_topology_count is only supported for subdivision meshes"), + } + } + + /// 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: UserData, + { + let mut state = self.state.lock().unwrap(); + state.user_data.data = user_data as *mut D as *mut _; + unsafe { + rtcSetGeometryUserData( + self.handle, + &mut state.user_data as *mut GeometryUserData as *mut _, + ); + } + } + + /// Returns the user data pointer of the geometry. + pub unsafe fn get_user_data(&self) -> Option<&mut D> + where + D: UserData, + { + unsafe { + let ptr = rtcGetGeometryUserData(self.handle); + if ptr.is_null() { + None + } else { + Some(&mut *std::mem::transmute::<*mut std::os::raw::c_void, *mut D>(ptr)) + } + } + } + /// Sets the number of vertex attributes of the geometry. /// /// This function sets the number of slots for vertex attributes buffers @@ -571,7 +1146,7 @@ impl<'dev, 'buf> Geometry<'buf> { /// * `count` - The number of vertex attribute slots. pub fn set_vertex_attribute_count(&mut self, count: u32) { match self.kind { - GeometryType::GRID | GeometryType::USER | GeometryType::INSTANCE => { + GeometryKind::GRID | GeometryKind::USER | GeometryKind::INSTANCE => { eprint!( "Vertex attribute not allowed for geometries of type {:?}!", self.kind @@ -611,8 +1186,34 @@ impl<'dev, 'buf> Geometry<'buf> { /// to their location during subdivision. This way all patches are linearly /// interpolated. pub fn set_subdivision_mode(&self, topology_id: u32, mode: RTCSubdivisionMode) { - unsafe { - rtcSetGeometrySubdivisionMode(self.handle, topology_id, mode); + match self.kind { + GeometryKind::SUBDIVISION => unsafe { + rtcSetGeometrySubdivisionMode(self.handle, topology_id, mode); + }, + _ => { + panic!( + "Subdivision mode not allowed for geometries of type {:?}!", + self.kind + ); + } + } + } + + /// Sets the number of primitives of a user-defined geometry. + pub fn set_user_primitive_count(&mut self, count: u32) { + match self.kind { + GeometryKind::USER => { + // Update the primitive count. + unsafe { + rtcSetGeometryUserPrimitiveCount(self.handle, count); + } + } + _ => { + panic!( + "User primitive count not allowed for geometries of type {:?}!", + self.kind + ); + } } } @@ -637,8 +1238,108 @@ impl<'dev, 'buf> Geometry<'buf> { rtcSetGeometryVertexAttributeTopology(self.handle, vertex_attribute_id, topology_id); } } + + // TODO(yang): Add documentation. + /// Sets the displacement function for a subdivision geometry. + pub unsafe fn set_displacement_function(&self, displacement: RTCDisplacementFunctionN) { + match self.kind { + GeometryKind::SUBDIVISION => unsafe { + rtcSetGeometryDisplacementFunction(self.handle, displacement); + }, + _ => { + panic!( + "Displacement function not allowed for geometries of type {:?}!", + self.kind + ); + } + } + } + + /// 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!( + "First half edge not allowed for geometries of type {:?}!", + self.kind + ); + } + } + } + + /// 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!("Face not allowed for geometries of type {:?}!", self.kind); + } + } + } + + /// 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!( + "Next half edge not allowed for geometries of type {:?}!", + self.kind + ); + } + } + } + + /// 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!( + "Prev half edge not allowed for geometries of type {:?}!", + self.kind + ); + } + } + } + + /// 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!( + "Opposite half edge not allowed for geometries of type {:?}!", + self.kind + ); + } + } + } } +// TODO(yang): rtcInterpolate, rtcInterpolateN + macro_rules! impl_geometry_type { ($name:ident, $kind:path, $(#[$meta:meta])*) => { #[derive(Debug)] @@ -663,9 +1364,7 @@ macro_rules! impl_geometry_type { }; } -use std::ops::{Deref, DerefMut}; - -impl_geometry_type!(TriangleMesh, GeometryType::TRIANGLE, +impl_geometry_type!(TriangleMesh, GeometryKind::TRIANGLE, /// A triangle mesh geometry. /// /// The index buffer must contain an array of three 32-bit indices per triangle @@ -695,7 +1394,7 @@ impl_geometry_type!(TriangleMesh, GeometryType::TRIANGLE, /// these buffers have to have the same stride and size. ); -impl_geometry_type!(QuadMesh, GeometryType::QUAD, +impl_geometry_type!(QuadMesh, GeometryKind::QUAD, /// A quad mesh geometry. /// /// The index buffer must contain an array of four 32-bit indices per triangle diff --git a/src/instance.rs b/src/instance.rs index ee16566ce..75fca561a 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -1,6 +1,6 @@ use std::{os::raw, sync::Arc}; -use crate::{scene::Scene, sys::*, Format, GeometryType}; +use crate::{scene::Scene, sys::*, Format, GeometryKind}; pub struct Instance { /// The scene being instanced @@ -10,7 +10,7 @@ pub struct Instance { impl Instance { pub fn unanimated(scene: Arc) -> Arc { - let h = unsafe { rtcNewGeometry(scene.device.handle, GeometryType::INSTANCE) }; + let h = unsafe { rtcNewGeometry(scene.device.handle, GeometryKind::INSTANCE) }; unsafe { rtcSetGeometryInstancedScene(h, scene.handle); } diff --git a/src/lib.rs b/src/lib.rs index 702c955b5..026e17625 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,49 +13,73 @@ use std::{alloc, mem}; -pub mod buffer; +mod buffer; mod callback; -pub mod device; -pub mod error; +mod device; +mod error; mod geometry; -pub mod instance; -pub mod intersect_context; -pub mod ray; -pub mod ray_packet; -pub mod ray_stream; -pub mod scene; -pub mod soa_ray; +mod instance; +mod intersect_context; +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 use buffer::{Buffer, BufferSize, BufferSlice, BufferView, BufferViewMut}; -pub use device::{Config, Device, FrequencyLevel, Isa}; -pub use instance::Instance; -pub use intersect_context::IntersectContext; -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 buffer::*; +pub use device::*; +pub use error::*; pub use geometry::*; +pub use instance::*; +pub use intersect_context::*; +pub use ray::*; +pub use scene::*; // Pull in some cleaned up enum and bitfield types directly, // with prettier aliases -pub use sys::{ - RTCBufferType as BufferUsage, RTCBuildQuality as BuildQuality, - RTCDeviceProperty as DeviceProperty, RTCError as Error, RTCFormat as Format, - RTCGeometryType as GeometryType, RTCSubdivisionMode as SubdivisionMode, -}; +pub type Bounds = sys::RTCBounds; -pub use sys::{ - RTCBounds as Bounds, RTCBuildFlags as BuildFlags, RTCCurveFlags as CurveFlags, - RTCIntersectContextFlags as IntersectContextFlags, RTCSceneFlags as SceneFlags, -}; +/// 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. +/// +/// The [`BufferUsage::VERTEX_ATTRIBUTE`] slot can get used to assign +/// arbitrary additional vertex data which can get interpolated using the +/// [`rtcInterpolate`] API call. +/// +/// 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; +pub type QuaternionDecomposition = sys::RTCQuaternionDecomposition; /// The invalid ID for Embree intersection results (e.g. `Hit::geomID`, /// `Hit::primID`, etc.) diff --git a/src/ray.rs b/src/ray.rs index 17c163d3b..b858c8ca2 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -1,24 +1,55 @@ use crate::{sys, INVALID_ID}; +pub mod packet; +pub mod soa; +pub mod 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 (RTCRay4 type), size 8 (RTCRay8 type), +/// and size 16 (RTCRay16 type). A const-generic type [`RayPacket`] is +/// defined for ray packets of arbitrary size N at compile time. +/// +/// 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) + /// Creates a new ray starting at `origin` and heading in direction `dir` + pub fn new(origin: [f32; 3], direction: [f32; 3]) -> Ray { + Ray::segment(origin, direction, 0.0, f32::INFINITY) } - pub fn segment(origin: [f32; 3], dir: [f32; 3], tnear: f32, tfar: f32) -> Ray { - sys::RTCRay { + /// Creates a new 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 { + 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, + dir_x: direction[0], + dir_y: direction[1], + dir_z: direction[2], tfar, time: 0.0, mask: u32::MAX, @@ -26,8 +57,50 @@ impl Ray { flags: 0, } } + + /// 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. + /// + /// The direction is normalized by dividing it by its length, which + /// may produce a NaN if the direction is zero. + pub fn dir_normalized(&self) -> [f32; 3] { + let dir = self.dir(); + let len = dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]; + let len = len.sqrt(); + [dir[0] / len, dir[1] / len, dir[2] / len] + } } +/// 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 (RTCHit4 type), size 8 (RTCHit8 type), and size 16 (RTCHit16 +/// type). +/// +/// [`HitPacket`] defines the type for hit packets of arbitrary size N at +/// compile time. +/// +/// See [`sys::RTCHit`] for more details. +pub type Hit = sys::RTCHit; + impl Default for Hit { fn default() -> Self { Hit { @@ -43,9 +116,41 @@ impl Default for Hit { } } +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 normal_normalized(&self) -> [f32; 3] { + let normal = self.normal(); + let len = normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]; + let len = len.sqrt(); + [normal[0] / len, normal[1] / len, normal[2] / len] + } + + /// Returns the barycentric u/v coordinates of the hit. + pub fn barycentric(&self) -> [f32; 2] { [self.u, self.v] } +} + +/// 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 (RTCRayHit4 type), size 8 (RTCRayHit8 +/// type), and size 16 (RTCRayHit16 type). A const-generic type [`RayHitPacket`] +/// is defined for ray/hit packets of arbitrary size N at compile time. +/// +/// See [`sys::RTCRayHit`] for more details. +pub type RayHit = sys::RTCRayHit; + impl RayHit { + /// Creates a new [`RayHit`] ready to be used in an intersection query. pub fn new(ray: Ray) -> RayHit { - sys::RTCRayHit { + RayHit { ray, hit: Hit::default(), } @@ -54,28 +159,7 @@ impl RayHit { /// Returns true if the hit is valid (i.e. the ray hit something). pub fn is_valid(&self) -> bool { self.hit.geomID != INVALID_ID } - /// Returns the normal of the hit point (normalized). - pub fn normal(&self) -> [f32; 3] { - let len = (self.hit.Ng_x * self.hit.Ng_x - + self.hit.Ng_y * self.hit.Ng_y - + self.hit.Ng_z * self.hit.Ng_z) - .sqrt(); - if len == 0.0 { - [0.0, 0.0, 0.0] - } else { - let len = 1.0 / len; - [ - self.hit.Ng_x * len, - self.hit.Ng_y * len, - self.hit.Ng_z * len, - ] - } - } - - /// Returns the barycentric coordinates of the hit point. - pub fn uv(&self) -> [f32; 2] { [self.hit.u, self.hit.v] } - - /// Returns the hit point. + /// Calculates the hit point from the ray and the hit distance. pub fn hit_point(&self) -> [f32; 3] { let t = self.ray.tfar; [ diff --git a/src/ray_packet.rs b/src/ray/packet.rs similarity index 85% rename from src/ray_packet.rs rename to src/ray/packet.rs index f9541037b..2fdf01c09 100644 --- a/src/ray_packet.rs +++ b/src/ray/packet.rs @@ -1,5 +1,5 @@ use crate::{ - soa_ray::{SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut}, + soa::{SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut}, sys, }; @@ -134,3 +134,34 @@ impl RayHit4 { self.ray.iter().zip(self.hit.iter()) } } + +pub struct RayPacket { + pub org_x: [f32; N], + pub org_y: [f32; N], + pub org_z: [f32; N], + pub tnear: [f32; N], + pub dir_x: [f32; N], + pub dir_y: [f32; N], + pub dir_z: [f32; N], + pub time: [f32; N], + pub tfar: [f32; N], + pub mask: [u32; N], + pub id: [u32; N], + pub flags: [u32; N], +} + +pub struct HitPacket { + pub Ng_x: [f32; N], + pub Ng_y: [f32; N], + pub Ng_z: [f32; N], + pub u: [f32; N], + pub v: [f32; N], + pub primID: [u32; N], + pub geomID: [u32; N], + pub instID: [[u32; 1]; N], +} + +pub struct RayHitPacket { + pub ray: RayPacket, + pub hit: HitPacket, +} diff --git a/src/soa_ray.rs b/src/ray/soa.rs similarity index 100% rename from src/soa_ray.rs rename to src/ray/soa.rs diff --git a/src/ray_stream.rs b/src/ray/stream.rs similarity index 98% rename from src/ray_stream.rs rename to src/ray/stream.rs index 81d08c8bf..e00c1f86c 100644 --- a/src/ray_stream.rs +++ b/src/ray/stream.rs @@ -2,7 +2,7 @@ use std::{f32, iter::Iterator, u32}; use crate::{ aligned_vector, aligned_vector_init, - soa_ray::{SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut}, + soa::{SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut}, sys, }; diff --git a/src/scene.rs b/src/scene.rs index 5426d79a1..b6960bd38 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -10,9 +10,11 @@ use crate::{ device::Device, geometry::Geometry, intersect_context::IntersectContext, - ray::{Ray, RayHit}, - ray_packet::{Ray4, RayHit4}, - ray_stream::{RayHitN, RayN}, + ray::{ + packet::{Ray4, RayHit4}, + stream::{RayHitN, RayN}, + Ray, RayHit, + }, sys::*, }; @@ -291,7 +293,30 @@ impl Scene { /// Finds the closest hit of a single ray with the scene. /// - /// Analogous to [`rtcIntersect1`]. + /// Analogous to [`sys::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 /// @@ -315,7 +340,7 @@ impl Scene { rtcOccluded1( self.handle, ctx as *mut RTCIntersectContext, - ray as *mut RTCRay, + <&mut Ray as Into<&mut RTCRay>>::into(ray) as *mut RTCRay, ); } ray.tfar == -f32::INFINITY @@ -362,7 +387,7 @@ impl Scene { rtcOccluded1M( self.handle, ctx as *mut RTCIntersectContext, - rays.as_mut_ptr(), + rays.as_mut_ptr() as *mut RTCRay, m as u32, mem::size_of::(), ); From 6c62f09122c010c34107eea5cb6a47d46c77ab9d Mon Sep 17 00:00:00 2001 From: matthiascy Date: Mon, 20 Feb 2023 21:33:38 +0100 Subject: [PATCH 32/65] update build test scripts & fix examples --- examples/minimal/src/main.rs | 2 +- examples/triangle/src/main.rs | 2 -- scripts/build-examples-linux-mac.sh | 2 +- scripts/build-test-mac.sh | 2 +- src/callback.rs | 4 +--- src/ray.rs | 10 +++++++--- src/ray/packet.rs | 5 +---- src/ray/stream.rs | 5 ++--- src/scene.rs | 6 +----- 9 files changed, 15 insertions(+), 23 deletions(-) diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 68497395b..1a28e050e 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -1,6 +1,6 @@ //! This example shows how to intersect a ray with a single triangle. -use embree::{BufferUsage, Device, Format, GeometryType, IntersectContext, Ray, Scene}; +use embree::{BufferUsage, Device, Format, IntersectContext, Ray, Scene}; /// Casts a single ray with the given origin and direction. fn cast_ray(scene: &Scene, origin: [f32; 3], direction: [f32; 3]) { diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index 091932729..0920ae6dd 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -4,7 +4,6 @@ extern crate embree; extern crate support; use embree::{BufferUsage, Device, Format, IntersectContext, RayHitN, RayN, TriangleMesh}; -use std::sync::Arc; fn main() { let display = support::Display::new(512, 512, "triangle"); @@ -35,7 +34,6 @@ fn main() { .copy_from_slice(&[[0, 1, 2]]); triangle.commit(); - let triangle = Arc::new(triangle); let mut scene = device.create_scene().unwrap(); scene.attach_geometry(&triangle); scene.commit(); diff --git a/scripts/build-examples-linux-mac.sh b/scripts/build-examples-linux-mac.sh index 186ec98da..987e8ee43 100755 --- a/scripts/build-examples-linux-mac.sh +++ b/scripts/build-examples-linux-mac.sh @@ -3,7 +3,7 @@ # build the examples cd examples #for d in `ls ./`; do -for d in ./triangle; do +for d in ./triangle ./minimal ./dynamic_triangle; do cd $d pwd cargo build diff --git a/scripts/build-test-mac.sh b/scripts/build-test-mac.sh index b7fb09091..101ebb969 100755 --- a/scripts/build-test-mac.sh +++ b/scripts/build-test-mac.sh @@ -10,7 +10,7 @@ fi # build the examples cd examples #for d in `ls ./`; do -for d in ./triangle; do +for d in ./triangle ./minimal ./dynamic_triangle; do cd $d pwd cargo build diff --git a/src/callback.rs b/src/callback.rs index 4d343fa2c..26393cad6 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -1,6 +1,4 @@ -use crate::{ - geometry::GeometryUserData, Bounds, IntersectContext, UserData, -}; +use crate::{geometry::GeometryUserData, Bounds, IntersectContext, UserData}; use std::os::raw::c_void; use crate::sys::*; diff --git a/src/ray.rs b/src/ray.rs index b858c8ca2..9f99d5a71 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -1,8 +1,12 @@ use crate::{sys, INVALID_ID}; -pub mod packet; -pub mod soa; -pub mod stream; +mod packet; +mod soa; +mod stream; + +pub use packet::*; +pub use soa::*; +pub use stream::*; /// New type alias for [`sys::RTCRay`] that provides some convenience /// methods. diff --git a/src/ray/packet.rs b/src/ray/packet.rs index 2fdf01c09..c6b41ee63 100644 --- a/src/ray/packet.rs +++ b/src/ray/packet.rs @@ -1,7 +1,4 @@ -use crate::{ - soa::{SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut}, - sys, -}; +use crate::{sys, SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut}; pub type Ray4 = sys::RTCRay4; pub type Hit4 = sys::RTCHit4; diff --git a/src/ray/stream.rs b/src/ray/stream.rs index e00c1f86c..1c201aa1a 100644 --- a/src/ray/stream.rs +++ b/src/ray/stream.rs @@ -1,9 +1,8 @@ use std::{f32, iter::Iterator, u32}; use crate::{ - aligned_vector, aligned_vector_init, - soa::{SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut}, - sys, + aligned_vector, aligned_vector_init, sys, SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, + SoARayIterMut, }; /// A ray stream stored in SoA format diff --git a/src/scene.rs b/src/scene.rs index b6960bd38..01fe9c175 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -10,11 +10,7 @@ use crate::{ device::Device, geometry::Geometry, intersect_context::IntersectContext, - ray::{ - packet::{Ray4, RayHit4}, - stream::{RayHitN, RayN}, - Ray, RayHit, - }, + ray::{Ray, Ray4, RayHit, RayHit4, RayHitN, RayN}, sys::*, }; From 13be2ac9c4a4bb31a240aba24d54f013ec1e3a2e Mon Sep 17 00:00:00 2001 From: matthiascy Date: Mon, 20 Feb 2023 21:38:39 +0100 Subject: [PATCH 33/65] fix minimal --- examples/minimal/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 1a28e050e..7f104ffae 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -1,6 +1,6 @@ //! This example shows how to intersect a ray with a single triangle. -use embree::{BufferUsage, Device, Format, IntersectContext, Ray, Scene}; +use embree::{BufferUsage, Device, Format, GeometryKind, IntersectContext, Ray, Scene}; /// Casts a single ray with the given origin and direction. fn cast_ray(scene: &Scene, origin: [f32; 3], direction: [f32; 3]) { @@ -35,7 +35,7 @@ fn main() { let mut scene = device.create_scene().unwrap(); { // Create a triangle mesh geometry, and initialise a single triangle. - let mut triangle = device.create_geometry(GeometryType::TRIANGLE).unwrap(); + let mut triangle = device.create_geometry(GeometryKind::TRIANGLE).unwrap(); triangle .set_new_buffer( BufferUsage::VERTEX, From f112cbb0e6657690568657e613f2b584ed2c9a43 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Mon, 20 Feb 2023 23:35:59 +0100 Subject: [PATCH 34/65] put user/instance related methods under its own struct --- examples/dynamic_scene/src/main.rs | 2 +- src/geometry.rs | 254 +---------------------------- src/geometry/instance.rs | 75 +++++++++ src/geometry/user_geom.rs | 175 ++++++++++++++++++++ src/instance.rs | 40 ----- src/lib.rs | 2 - 6 files changed, 259 insertions(+), 289 deletions(-) create mode 100644 src/geometry/instance.rs create mode 100644 src/geometry/user_geom.rs delete mode 100644 src/instance.rs diff --git a/examples/dynamic_scene/src/main.rs b/examples/dynamic_scene/src/main.rs index 699875eb3..fe2404bf6 100644 --- a/examples/dynamic_scene/src/main.rs +++ b/examples/dynamic_scene/src/main.rs @@ -243,6 +243,6 @@ fn main() { } } } - time += 1.0; + time += 0.2; }); } diff --git a/src/geometry.rs b/src/geometry.rs index a22e8e8d4..296f8aa6c 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -13,6 +13,11 @@ use crate::{ use std::ops::{Deref, DerefMut}; +mod instance; +mod user_geom; + +pub use instance::*; + // 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)] @@ -30,6 +35,7 @@ pub trait UserData: Sized + Send + Sync + 'static {} impl UserData for T where T: Sized + Send + Sync + 'static {} +// TODO(yang): remove user geometry related payload to user geometry module. /// User-defined data for a geometry. /// /// This contains also the payloads for different callbacks, which makes it @@ -130,7 +136,7 @@ impl<'buf> Clone for Geometry<'buf> { device: self.device.clone(), handle: self.handle, kind: self.kind, - state: Arc::new(Mutex::new(GeometryState::default())), + state: self.state.clone(), } } } @@ -482,7 +488,7 @@ impl<'buf> Geometry<'buf> { /// Returns the buffer bound to the given slot and usage. pub fn get_buffer(&self, usage: BufferUsage, slot: u32) -> Option { - let state = self.state.lock().unwrap(); + let mut state = self.state.lock().unwrap(); state .attachments .get(&usage) @@ -685,160 +691,6 @@ impl<'buf> Geometry<'buf> { } } - /// 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. - /// Passing `None` as function pointer disables the registered callback - /// 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_user_bounds_function(&mut self, bounds: F) - where - D: UserData, - F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), - { - match self.kind { - GeometryKind::USER => unsafe { - let mut state = self.state.lock().unwrap(); - let mut closure = bounds; - state.user_data.user_bounds_payload = - &mut closure as *mut _ as *mut std::os::raw::c_void; - rtcSetGeometryBoundsFunction( - self.handle, - crate::callback::user_bounds_function_helper(&mut closure), - ptr::null_mut(), - ); - }, - _ => panic!("Only user-defined geometries can have a bounds function"), - } - } - - // TODO(yang): deal with RTCRayHitN, then we can make this function safe - /// Sets the callback function to intersect a user geometry. - /// - /// Only a single callback function can be registered per geometry and - /// further invocations overwrite the previously set callback function. - /// Passing `None` as function pointer disables the registered callback - /// function. - /// - /// 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. The callback function of type - /// [`RTCIntersectFunctionN`] gets passed a number of arguments through - /// the [`RTCIntersectFunctionNArguments`] structure. The value N - /// specifies the ray packet size, valid points to an array of - /// integers that specify whether the corresponding ray is valid (-1) or - /// invalid (0), the `geometryUserPtr` member points to the geometry - /// user data previously set through [`Geometry::set_user_data`], the - /// context member points to the intersection context passed to the ray - /// query, the rayhit member points to a ray and hit packet of variable - /// size N, and the geomID and primID member identifies the geometry ID - /// and primitive ID of the primitive to intersect. The ray component of - /// the rayhit structure contains valid data, in particular - /// the tfar value is the current closest hit distance found. All data - /// inside the hit component of the rayhit structure are undefined and - /// should not be read by the function. - /// 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 was found in - /// the valid range (from tnear to tfar), it should update the hit distance - /// of the ray (tfar member) and the hit (u, v, Ng, 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. - /// - /// 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 rtcFilterIntersection call. Within the user geometry intersect - /// function, it is safe to trace new rays and create new scenes and - /// geometries. When performing ray queries using rtcIntersect1, it is - /// guaranteed that the packet size is 1 when the callback is invoked. - /// When performing ray queries using the rtcIntersect4/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 rtcGetDevice- Property. When performing ray queries - /// using the stream API such as rtcIntersect1M, rtcIntersect1Mp, - /// rtcIntersectNM, or rtcIntersectNp the or- der 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 unsafe fn set_user_intersect_function(&mut self, intersect: F) - where - D: UserData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayHitN, u32), - { - // TODO: deal with RTCRayHitN - match self.kind { - GeometryKind::USER => unsafe { - let mut state = self.state.lock().unwrap(); - let mut closure = intersect; - state.user_data.user_intersect_payload = - &mut closure as *mut _ as *mut std::os::raw::c_void; - rtcSetGeometryIntersectFunction( - self.handle, - crate::callback::user_intersect_function_helper(&mut closure), - ); - }, - _ => panic!("Only user-defined geometries can have an intersect function"), - } - } - - // TODO(yang): deal with RTCRayN, then we can make this function safe - /// Sets the callback function to occlude a user geometry. - /// - /// Similar to [`Geometry::set_user_intersect_function`], but for occlusion - /// queries. - pub unsafe fn set_user_occluded_function(&mut self, occluded: F) - where - D: UserData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayN, u32), - { - // TODO: deal with RTCRayN - match self.kind { - GeometryKind::USER => unsafe { - let mut state = self.state.lock().unwrap(); - let mut closure = occluded; - state.user_data.user_occluded_payload = - &mut closure as *mut _ as *mut std::os::raw::c_void; - rtcSetGeometryOccludedFunction( - self.handle, - crate::callback::user_occluded_function_helper(&mut closure), - ); - }, - _ => panic!("Only user-defined geometries can have an occluded function"), - } - } - /// Sets the point query callback function for a geometry. /// /// Only a single callback function can be registered per geometry and @@ -914,78 +766,6 @@ impl<'buf> Geometry<'buf> { rtcSetGeometryPointQueryFunction(self.handle, query_func); } - /// 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 an instanced scene"), - } - } - - // TODO(yang): Better transform type - /// Returns the interpolated instance transformation for the specified time - /// step. - pub fn get_geometry_transform(&mut self, time: f32, format: Format) -> [f32; 16] { - match self.kind { - GeometryKind::INSTANCE => unsafe { - let mut transform = [0.0; 16]; - rtcGetGeometryTransform( - self.handle, - time, - format, - transform.as_mut_ptr() as *mut _, - ); - transform - }, - _ => { - panic!("Geometry::get_geometry_transform is only supported for instance geometries") - } - } - } - - // TODO(yang): Better transform type - /// Sets the transformation for a particular time step of an instance - /// geometry. - pub fn set_geometry_transform(&mut self, time_step: u32, format: Format, transform: &[f32]) { - match self.kind { - GeometryKind::INSTANCE => unsafe { - rtcSetGeometryTransform( - self.handle, - time_step, - format, - transform.as_ptr() as *const _, - ); - }, - _ => { - panic!("Geometry::set_geometry_transform is only supported for instance geometries") - } - } - } - - /// 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!("Geometry::set_geometry_transform is only supported for instance geometries") - } - } - } - /// Sets the tessellation rate for a subdivision mesh or flat curves. /// /// For curves, the tessellation rate specifies the number of ray-facing @@ -1199,24 +979,6 @@ impl<'buf> Geometry<'buf> { } } - /// Sets the number of primitives of a user-defined geometry. - pub fn set_user_primitive_count(&mut self, count: u32) { - match self.kind { - GeometryKind::USER => { - // Update the primitive count. - unsafe { - rtcSetGeometryUserPrimitiveCount(self.handle, count); - } - } - _ => { - panic!( - "User primitive count not allowed for geometries of type {:?}!", - self.kind - ); - } - } - } - /// Binds a vertex attribute to a topology of the geometry. /// /// This function binds a vertex attribute buffer slot to a topology for the diff --git a/src/geometry/instance.rs b/src/geometry/instance.rs new file mode 100644 index 000000000..b5b4c6715 --- /dev/null +++ b/src/geometry/instance.rs @@ -0,0 +1,75 @@ +use crate::{sys, + Device, Error, Format, Geometry, GeometryKind, QuaternionDecomposition, Scene, +}; +use std::ops::{Deref, DerefMut}; + +#[derive(Debug)] +pub struct Instance(Geometry<'static>); + +impl Deref for Instance { + type Target = Geometry<'static>; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl DerefMut for Instance { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } +} + +impl Instance { + pub fn new(device: &Device) -> Result { + Ok(Self(Geometry::new(device, GeometryKind::INSTANCE)?)) + } + + /// Sets the instanced scene of an instance geometry. + pub fn set_instanced_scene(&mut self, scene: &Scene) { + unsafe { sys::rtcSetGeometryInstancedScene(self.handle, scene.handle) } + } + + // TODO(yang): Better transform type + /// Returns the interpolated instance transformation for the specified time + /// step. + pub fn get_geometry_transform(&mut self, time: f32, format: Format) -> [f32; 16] { + unsafe { + let mut transform = [0.0; 16]; + sys::rtcGetGeometryTransform( + self.handle, + time, + format, + transform.as_mut_ptr() as *mut _, + ); + transform + } + } + + // TODO(yang): Better transform type + /// Sets the transformation for a particular time step of an instance + /// geometry. + pub fn set_geometry_transform(&mut self, time_step: u32, format: Format, transform: &[f32]) { + unsafe { + sys::rtcSetGeometryTransform( + self.handle, + time_step, + format, + transform.as_ptr() as *const _, + ); + } + } + + /// 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, + ) { + unsafe { + sys::rtcSetGeometryTransformQuaternion( + self.handle, + time_step, + transform as &QuaternionDecomposition as *const _, + ); + } + } +} diff --git a/src/geometry/user_geom.rs b/src/geometry/user_geom.rs new file mode 100644 index 000000000..e2a1356ee --- /dev/null +++ b/src/geometry/user_geom.rs @@ -0,0 +1,175 @@ +use std::ops::{Deref, DerefMut}; +use std::ptr; +use crate::{Bounds, Device, Error, Geometry, GeometryKind, IntersectContext, sys, UserData}; +use crate::sys::{RTCRayHitN, RTCRayN}; + +#[derive(Debug)] +pub struct UserGeometry(Geometry<'static>); + +impl Deref for UserGeometry { + type Target = Geometry<'static>; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl DerefMut for UserGeometry { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } +} + +impl UserGeometry { + pub fn new(device: &Device) -> Result { + Ok(Self(Geometry::new(device, GeometryKind::USER)?)) + } + + /// 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. + /// Passing `None` as function pointer disables the registered callback + /// 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: UserData, + F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), + { + match self.kind { + GeometryKind::USER => unsafe { + let mut state = self.state.lock().unwrap(); + let mut closure = bounds; + state.user_data.user_bounds_payload = + &mut closure as *mut _ as *mut std::os::raw::c_void; + sys::rtcSetGeometryBoundsFunction( + self.handle, + crate::callback::user_bounds_function_helper(&mut closure), + ptr::null_mut(), + ); + }, + _ => panic!("Only user-defined geometries can have a bounds function"), + } + } + + // TODO(yang): deal with RTCRayHitN, then we can make this function safe + /// Sets the callback function to intersect a user geometry. + /// + /// Only a single callback function can be registered per geometry and + /// further invocations overwrite the previously set callback function. + /// Passing `None` as function pointer disables the registered callback + /// function. + /// + /// 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. The callback function of type + /// [`RTCIntersectFunctionN`] gets passed a number of arguments through + /// the [`RTCIntersectFunctionNArguments`] structure. The value N + /// specifies the ray packet size, valid points to an array of + /// integers that specify whether the corresponding ray is valid (-1) or + /// invalid (0), the `geometryUserPtr` member points to the geometry + /// user data previously set through [`Geometry::set_user_data`], the + /// context member points to the intersection context passed to the ray + /// query, the rayhit member points to a ray and hit packet of variable + /// size N, and the geomID and primID member identifies the geometry ID + /// and primitive ID of the primitive to intersect. The ray component of + /// the rayhit structure contains valid data, in particular + /// the tfar value is the current closest hit distance found. All data + /// inside the hit component of the rayhit structure are undefined and + /// should not be read by the function. + /// 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 was found in + /// the valid range (from tnear to tfar), it should update the hit distance + /// of the ray (tfar member) and the hit (u, v, Ng, 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. + /// + /// 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 rtcFilterIntersection call. Within the user geometry intersect + /// function, it is safe to trace new rays and create new scenes and + /// geometries. When performing ray queries using rtcIntersect1, it is + /// guaranteed that the packet size is 1 when the callback is invoked. + /// When performing ray queries using the rtcIntersect4/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 rtcGetDevice- Property. When performing ray queries + /// using the stream API such as rtcIntersect1M, rtcIntersect1Mp, + /// rtcIntersectNM, or rtcIntersectNp the or- der 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 unsafe fn set_intersect_function(&mut self, intersect: F) + where + D: UserData, + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayHitN, u32), + { + // TODO: deal with RTCRayHitN + let mut state = self.state.lock().unwrap(); + let mut closure = intersect; + state.user_data.user_intersect_payload = + &mut closure as *mut _ as *mut std::os::raw::c_void; + sys::rtcSetGeometryIntersectFunction( + self.handle, + crate::callback::user_intersect_function_helper(&mut closure), + ); + } + + // TODO(yang): deal with RTCRayN, then we can make this function safe + /// Sets the callback function to occlude a user geometry. + /// + /// Similar to [`Geometry::set_user_intersect_function`], but for occlusion + /// queries. + pub unsafe fn set_occluded_function(&mut self, occluded: F) + where + D: UserData, + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayN, u32), + { + // TODO: deal with RTCRayN + let mut state = self.state.lock().unwrap(); + let mut closure = occluded; + state.user_data.user_occluded_payload = + &mut closure as *mut _ as *mut std::os::raw::c_void; + sys::rtcSetGeometryOccludedFunction( + self.handle, + crate::callback::user_occluded_function_helper(&mut closure), + ); + } + + /// Sets the number of primitives of a user-defined geometry. + pub fn set_user_primitive_count(&mut self, count: u32) { + // Update the primitive count. + unsafe { + sys::rtcSetGeometryUserPrimitiveCount(self.handle, count); + } + } +} \ No newline at end of file diff --git a/src/instance.rs b/src/instance.rs deleted file mode 100644 index 75fca561a..000000000 --- a/src/instance.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::{os::raw, sync::Arc}; - -use crate::{scene::Scene, sys::*, Format, GeometryKind}; - -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, GeometryKind::INSTANCE) }; - unsafe { - rtcSetGeometryInstancedScene(h, scene.handle); - } - Arc::new(Instance { handle: h, 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 Drop for Instance { - fn drop(&mut self) { - unsafe { - rtcReleaseGeometry(self.handle); - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 026e17625..62793a793 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,6 @@ mod callback; mod device; mod error; mod geometry; -mod instance; mod intersect_context; mod ray; mod scene; @@ -33,7 +32,6 @@ pub use buffer::*; pub use device::*; pub use error::*; pub use geometry::*; -pub use instance::*; pub use intersect_context::*; pub use ray::*; pub use scene::*; From 61ba9c8c851b8da381b47bbd9207d9c11154918c Mon Sep 17 00:00:00 2001 From: matthiascy Date: Mon, 20 Feb 2023 23:37:03 +0100 Subject: [PATCH 35/65] started adopting instance example --- examples/instancing/Cargo.toml | 2 +- examples/instancing/src/main.rs | 181 +++++++++++++++++++------------- src/geometry/user_geom.rs | 4 +- 3 files changed, 111 insertions(+), 76 deletions(-) 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 8f732b747..7d5b408d5 100644 --- a/examples/instancing/src/main.rs +++ b/examples/instancing/src/main.rs @@ -6,86 +6,110 @@ 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, QuadMesh, Ray, + RayHit, Scene, SceneFlags, TriangleMesh, }; 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); - } +const NUM_PHI: usize = 5; +const NUM_THETA: usize = 2 * NUM_PHI; + +fn create_sphere(device: &Device, pos: Vec3, radius: f32) -> Geometry<'static> { + // 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; } - 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>) { let t0 = 0.7 * time; @@ -113,15 +137,26 @@ fn animate_instances(time: f32, num_instances: usize) -> (Vec>, Vec fn main() { let mut display = support::Display::new(512, 512, "instancing"); - let device = Device::new(); + let device = Device::new().unwrap(); - // Make the scene we'll instance with 4 triangulated spheres. + // Create a scene. + let mut scene = device.create_scene().unwrap(); + scene.set_build_quality(BuildQuality::LOW).unwrap(); + scene.set_flags(SceneFlags::DYNAMIC); + + // 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), ]; + for s in spheres.into_iter() { + scene1.attach_geometry(&s); + } + scene1.commit(); + let mut instanced_scene = Scene::new(&device); for s in spheres.into_iter() { instanced_scene.attach_geometry(s); diff --git a/src/geometry/user_geom.rs b/src/geometry/user_geom.rs index e2a1356ee..28cdc938d 100644 --- a/src/geometry/user_geom.rs +++ b/src/geometry/user_geom.rs @@ -147,7 +147,7 @@ impl UserGeometry { // TODO(yang): deal with RTCRayN, then we can make this function safe /// Sets the callback function to occlude a user geometry. /// - /// Similar to [`Geometry::set_user_intersect_function`], but for occlusion + /// Similar to [`Geometry::set_intersect_function`], but for occlusion /// queries. pub unsafe fn set_occluded_function(&mut self, occluded: F) where @@ -166,7 +166,7 @@ impl UserGeometry { } /// Sets the number of primitives of a user-defined geometry. - pub fn set_user_primitive_count(&mut self, count: u32) { + pub fn set_primitive_count(&mut self, count: u32) { // Update the primitive count. unsafe { sys::rtcSetGeometryUserPrimitiveCount(self.handle, count); From 5443b8922ac20e5956964ca345cf3114afa581b1 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Tue, 21 Feb 2023 17:00:59 +0100 Subject: [PATCH 36/65] user geometry payloads for callbacks --- src/callback.rs | 134 ++++++++++++++++++++++++++----------- src/device.rs | 67 ++++++++----------- src/geometry.rs | 136 ++++++++++++++++++++++++++------------ src/geometry/instance.rs | 4 +- src/geometry/user_geom.rs | 63 +++++++++++------- src/scene.rs | 17 ++--- 6 files changed, 267 insertions(+), 154 deletions(-) diff --git a/src/callback.rs b/src/callback.rs index 26393cad6..d6cd84a96 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -1,5 +1,8 @@ -use crate::{geometry::GeometryUserData, Bounds, IntersectContext, UserData}; -use std::os::raw::c_void; +use crate::{geometry::GeometryData, Bounds, IntersectContext, UserGeometryData}; +use std::{ + any::{Any, TypeId}, + os::raw::c_void, +}; use crate::sys::*; @@ -61,22 +64,36 @@ where /// callback. pub fn user_intersect_function_helper(_f: &mut F) -> RTCIntersectFunctionN where - D: UserData, + D: UserGeometryData, F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayHitN, u32), { unsafe extern "C" fn inner(args: *const RTCIntersectFunctionNArguments) where + D: UserGeometryData, F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayHitN, u32), { - let cb_ptr = - (*((*args).geometryUserPtr as *mut GeometryUserData)).user_intersect_payload as *mut F; + let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) + .user_fns + .as_ref() + .expect( + "User payloads not set! Make sure the geometry was created with kind \ + GeometryKind::USER", + ) + .intersect_fn as *mut F; if !cb_ptr.is_null() { let cb = &mut *cb_ptr; let user_data = { - let user_data_ptr = (*((*args).geometryUserPtr as *mut GeometryUserData)).data; - user_data_ptr - .is_null() - .then(|| &mut *(user_data_ptr as *mut D)) + match (*((*args).geometryUserPtr as *mut GeometryData)).user_data { + Some(ref user_data) => { + if user_data.data.is_null() || user_data.data.type_id() != TypeId::of::() + { + None + } else { + Some(&mut *(user_data.data as *mut D)) + } + }, + None => None, + } }; cb( std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), @@ -98,22 +115,36 @@ where /// callback. pub fn user_occluded_function_helper(_f: &mut F) -> RTCOccludedFunctionN where - D: UserData, + D: UserGeometryData, F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayN, u32), { unsafe extern "C" fn inner(args: *const RTCOccludedFunctionNArguments) where + D: UserGeometryData, F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayN, u32), { - let cb_ptr = - (*((*args).geometryUserPtr as *mut GeometryUserData)).user_occluded_payload as *mut F; + let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) + .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 = { - let user_data_ptr = (*((*args).geometryUserPtr as *mut GeometryUserData)).data; - user_data_ptr - .is_null() - .then(|| &mut *(user_data_ptr as *mut D)) + match (*((*args).geometryUserPtr as *mut GeometryData)).user_data { + Some(ref user_data) => { + if user_data.data.is_null() || user_data.data.type_id() != TypeId::of::() + { + None + } else { + Some(&mut *(user_data.data as *mut D)) + } + }, + None => None, + } }; cb( std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), @@ -134,11 +165,12 @@ where /// for intersect. pub fn intersect_filter_function_helper(_f: &mut F) -> RTCFilterFunctionN where - D: UserData, + D: UserGeometryData, F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, &mut RTCRayN, &mut RTCHitN, u32), { unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) where + D: UserGeometryData, F: FnMut( &mut [i32], Option<&mut D>, @@ -148,15 +180,22 @@ where u32, ), { - let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryUserData)).intersect_filter_payload - as *mut F; + let cb_ptr = + (*((*args).geometryUserPtr as *mut GeometryData)).intersect_filter_fn as *mut F; if !cb_ptr.is_null() { let cb = &mut *cb_ptr; let user_data = { - let user_data_ptr = (*((*args).geometryUserPtr as *mut GeometryUserData)).data; - user_data_ptr - .is_null() - .then(|| &mut *(user_data_ptr as *mut D)) + match (*((*args).geometryUserPtr as *mut GeometryData)).user_data { + Some(ref user_data) => { + if user_data.data.is_null() || user_data.data.type_id() != TypeId::of::() + { + None + } else { + Some(&mut *(user_data.data as *mut D)) + } + }, + None => None, + } }; cb( std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), @@ -176,11 +215,12 @@ where /// for occuluded. pub fn occluded_filter_function_helper(_f: &mut F) -> RTCFilterFunctionN where - D: UserData, + D: UserGeometryData, F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, &mut RTCRayN, &mut RTCHitN, u32), { unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) where + D: UserGeometryData, F: FnMut( &mut [i32], Option<&mut D>, @@ -190,15 +230,21 @@ where u32, ), { - let cb_ptr = - (*((*args).geometryUserPtr as *mut GeometryUserData)).occluded_filter_payload as *mut F; + let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)).occluded_filter_fn as *mut F; if !cb_ptr.is_null() { let cb = &mut *cb_ptr; let user_data = { - let user_data_ptr = (*((*args).geometryUserPtr as *mut GeometryUserData)).data; - user_data_ptr - .is_null() - .then(|| &mut *(user_data_ptr as *mut D)) + match (*((*args).geometryUserPtr as *mut GeometryData)).user_data { + Some(ref user_data) => { + if user_data.data.is_null() || user_data.data.type_id() != TypeId::of::() + { + None + } else { + Some(&mut *(user_data.data as *mut D)) + } + }, + None => None, + } }; cb( std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), @@ -217,22 +263,36 @@ where /// Helper function to convert a Rust closure to `RTCBoundsFunction` callback. pub fn user_bounds_function_helper(_f: &mut F) -> RTCBoundsFunction where - D: UserData, + D: UserGeometryData, F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), { unsafe extern "C" fn inner(args: *const RTCBoundsFunctionArguments) where + D: UserGeometryData, F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), { - let cb_ptr = - (*((*args).geometryUserPtr as *mut GeometryUserData)).user_bounds_payload as *mut F; + let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) + .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 = { - let user_data_ptr = (*((*args).geometryUserPtr as *mut GeometryUserData)).data; - user_data_ptr - .is_null() - .then(|| &mut *(user_data_ptr as *mut D)) + match (*((*args).geometryUserPtr as *mut GeometryData)).user_data { + Some(ref user_data) => { + if user_data.data.is_null() || user_data.data.type_id() != TypeId::of::() + { + None + } else { + Some(&mut *(user_data.data as *mut D)) + } + }, + None => None, + } }; cb( user_data, diff --git a/src/device.rs b/src/device.rs index 298b22e1d..3650c399a 100644 --- a/src/device.rs +++ b/src/device.rs @@ -20,43 +20,29 @@ impl Clone for Device { } } +impl Drop for Device { + fn drop(&mut self) { + unsafe { + rtcReleaseDevice(self.handle); + } + } +} + +unsafe impl Sync for Device {} + impl Device { pub fn new() -> Result { - enable_ftz_and_daz(); - let handle = unsafe { rtcNewDevice(ptr::null()) }; - if handle.is_null() { - Err(unsafe { rtcGetDeviceError(ptr::null_mut()) }) - } else { - let device = Device { handle }; - device.set_error_function(default_error_function); - Ok(device) - } + create_device(None) } pub fn debug() -> Result { let cfg = CString::new("verbose=4").unwrap(); - enable_ftz_and_daz(); - let handle = unsafe { rtcNewDevice(cfg.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) - } + create_device(Some(cfg)) } pub fn with_config(config: Config) -> Result { - enable_ftz_and_daz(); let cfg = config.to_c_string(); - let handle = unsafe { rtcNewDevice(cfg.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) - } + create_device(Some(cfg)) } /// Register a callback function to be called when an error occurs. @@ -209,7 +195,9 @@ impl Device { pub fn get_error(&self) -> RTCError { unsafe { rtcGetDeviceError(self.handle) } } /// Creates a new scene bound to the device. - pub fn create_scene(&self) -> Result { Scene::new(self.clone()) } + pub fn create_scene(&self) -> Result { + 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 @@ -232,16 +220,6 @@ impl Device { } } -impl Drop for Device { - fn drop(&mut self) { - unsafe { - rtcReleaseDevice(self.handle); - } - } -} - -unsafe impl Sync for Device {} - /// Instruction Set Architecture. #[derive(Debug, Clone, Copy)] pub enum Isa { @@ -402,3 +380,16 @@ pub fn enable_ftz_and_daz() { fn default_error_function(error: Error, msg: &str) { eprintln!("Embree error {:?} - {}", error, msg); } + +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) + } +} diff --git a/src/geometry.rs b/src/geometry.rs index 296f8aa6c..8a1e40101 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,22 +1,25 @@ use std::{ + any::TypeId, collections::HashMap, marker::PhantomData, num::NonZeroUsize, ptr, - sync::{Arc, Mutex}, + sync::{Mutex}, }; use crate::{ - sys::*, Bounds, BufferSlice, BufferUsage, BuildQuality, Device, Error, Format, GeometryKind, - IntersectContext, QuaternionDecomposition, Scene, + sys::*, BufferSlice, BufferUsage, BuildQuality, Device, Error, Format, GeometryKind, + IntersectContext, }; use std::ops::{Deref, DerefMut}; +use std::rc::Rc; mod instance; mod user_geom; pub use instance::*; +pub use user_geom::*; // TODO(yang): maybe enforce format and stride when get the view? /// Information about how a (part of) buffer is bound to a geometry. @@ -31,49 +34,65 @@ pub(crate) struct AttachedBuffer<'src> { } /// Trait for user-defined data that can be attached to a geometry. -pub trait UserData: Sized + Send + Sync + 'static {} +pub trait UserGeometryData: Sized + Send + Sync + 'static {} -impl UserData for T where T: Sized + Send + Sync + 'static {} +impl UserGeometryData for T where T: Sized + Send + Sync + 'static {} -// TODO(yang): remove user geometry related payload to user geometry module. /// 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, Copy, Clone)] -pub(crate) struct GeometryUserData { +/// 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 UserData { /// Pointer to the user-defined data. pub data: *mut std::os::raw::c_void, - /// Payload for the [`Geometry::set_intersect_filter_function`] call. - pub intersect_filter_payload: *mut std::os::raw::c_void, - /// Payload for the [`Geometry::set_occluded_filter_function`] call. - pub occluded_filter_payload: *mut std::os::raw::c_void, - /// Payload for the [`Geometry::set_user_intersect_function`] call. - pub user_intersect_payload: *mut std::os::raw::c_void, - /// Payload for the [`Geometry::set_user_occluded_function`] call. - pub user_occluded_payload: *mut std::os::raw::c_void, - /// Payload for the [`Geometry::set_user_bounds_function`] call. - pub user_bounds_payload: *mut std::os::raw::c_void, + /// Type ID of the user-defined data. + pub type_id: TypeId, } -impl Default for GeometryUserData { +/// Payloads for user-defined callbacks of a geometry of kind +/// [`GeometryKind::USER`]. +#[derive(Debug, Clone)] +pub(crate) struct UserGeometryPayloads { + /// Payload for the [`UserGeometry::set_intersect_function`] call. + pub intersect_fn: *mut std::os::raw::c_void, + /// Payload for the [`UserGeometry::set_occluded_function`] call. + pub occluded_fn: *mut std::os::raw::c_void, + /// Payload for the [`UserGeometry::set_bounds_function`] call. + pub bounds_fn: *mut std::os::raw::c_void, +} + +impl Default for UserGeometryPayloads { fn default() -> Self { Self { - data: ptr::null_mut(), - user_intersect_payload: ptr::null_mut(), - user_occluded_payload: ptr::null_mut(), - intersect_filter_payload: ptr::null_mut(), - occluded_filter_payload: ptr::null_mut(), - user_bounds_payload: ptr::null_mut(), + intersect_fn: ptr::null_mut(), + occluded_fn: ptr::null_mut(), + bounds_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, +} + /// Extra data for a geometry. -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] struct GeometryState<'buf> { attachments: HashMap>>, - user_data: GeometryUserData, + data: GeometryData, } /// Wrapper around an Embree geometry object. @@ -124,7 +143,7 @@ pub struct Geometry<'buf> { pub(crate) device: Device, pub(crate) handle: RTCGeometry, kind: GeometryKind, - state: Arc>>, + state: Rc>>, } impl<'buf> Clone for Geometry<'buf> { @@ -174,11 +193,28 @@ impl<'buf> Geometry<'buf> { if handle.is_null() { Err(device.get_error()) } else { + let state = GeometryState { + attachments: Default::default(), + data: 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 + }, + }, + }; Ok(Geometry { device: device.clone(), handle, kind, - state: Arc::new(Mutex::new(Default::default())), + state: Rc::new(Mutex::new(state)), }) } } @@ -488,7 +524,7 @@ impl<'buf> Geometry<'buf> { /// Returns the buffer bound to the given slot and usage. pub fn get_buffer(&self, usage: BufferUsage, slot: u32) -> Option { - let mut state = self.state.lock().unwrap(); + let state = self.state.lock().unwrap(); state .attachments .get(&usage) @@ -623,7 +659,7 @@ impl<'buf> Geometry<'buf> { /// pointer. pub unsafe fn set_intersect_filter_function(&mut self, filter: F) where - D: UserData, + D: UserGeometryData, F: FnMut( &mut [i32], Option<&mut D>, @@ -636,7 +672,7 @@ impl<'buf> Geometry<'buf> { let mut state = self.state.lock().unwrap(); unsafe { let mut closure = filter; - state.user_data.intersect_filter_payload = + state.data.intersect_filter_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; rtcSetGeometryIntersectFilterFunction( self.handle, @@ -669,7 +705,7 @@ impl<'buf> Geometry<'buf> { /// pointer. pub unsafe fn set_occluded_filter_function(&mut self, filter: F) where - D: UserData, + D: UserGeometryData, F: FnMut( &mut [i32], Option<&mut D>, @@ -682,7 +718,7 @@ impl<'buf> Geometry<'buf> { let mut state = self.state.lock().unwrap(); unsafe { let mut closure = filter; - state.user_data.occluded_filter_payload = + state.data.occluded_filter_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; rtcSetGeometryOccludedFilterFunction( self.handle, @@ -885,29 +921,43 @@ impl<'buf> Geometry<'buf> { /// access its geometry representation. pub fn set_user_data(&mut self, user_data: &mut D) where - D: UserData, + D: UserGeometryData, { let mut state = self.state.lock().unwrap(); - state.user_data.data = user_data as *mut D as *mut _; + state.data. + user_data = Some(UserData { + data: user_data as *mut D as *mut std::os::raw::c_void, + type_id: TypeId::of::(), + }); unsafe { rtcSetGeometryUserData( self.handle, - &mut state.user_data as *mut GeometryUserData as *mut _, + &mut state.data as *mut GeometryData as *mut _, ); } } /// Returns the user data pointer of the geometry. - pub unsafe fn get_user_data(&self) -> Option<&mut D> + pub fn get_user_data(&self) -> Option<&mut D> where - D: UserData, + D: UserGeometryData, { unsafe { - let ptr = rtcGetGeometryUserData(self.handle); + let ptr = + rtcGetGeometryUserData(self.handle) as *mut GeometryData; if ptr.is_null() { None } else { - Some(&mut *std::mem::transmute::<*mut std::os::raw::c_void, *mut D>(ptr)) + match (*ptr).user_data.as_mut() { + None => None, + Some(user_data @ UserData { .. }) => { + if user_data.type_id == TypeId::of::() { + Some(&mut *(user_data.data as *mut D)) + } else { + None + } + } + } } } } diff --git a/src/geometry/instance.rs b/src/geometry/instance.rs index b5b4c6715..caa9f50e7 100644 --- a/src/geometry/instance.rs +++ b/src/geometry/instance.rs @@ -1,6 +1,4 @@ -use crate::{sys, - Device, Error, Format, Geometry, GeometryKind, QuaternionDecomposition, Scene, -}; +use crate::{sys, Device, Error, Format, Geometry, GeometryKind, QuaternionDecomposition, Scene}; use std::ops::{Deref, DerefMut}; #[derive(Debug)] diff --git a/src/geometry/user_geom.rs b/src/geometry/user_geom.rs index 28cdc938d..f48d514ec 100644 --- a/src/geometry/user_geom.rs +++ b/src/geometry/user_geom.rs @@ -1,7 +1,12 @@ -use std::ops::{Deref, DerefMut}; -use std::ptr; -use crate::{Bounds, Device, Error, Geometry, GeometryKind, IntersectContext, sys, UserData}; -use crate::sys::{RTCRayHitN, RTCRayN}; +use crate::{ + sys, + sys::{RTCRayHitN, RTCRayN}, + Bounds, Device, Error, Geometry, GeometryKind, IntersectContext, UserGeometryData, +}; +use std::{ + ops::{Deref, DerefMut}, + ptr, +}; #[derive(Debug)] pub struct UserGeometry(Geometry<'static>); @@ -50,16 +55,19 @@ impl UserGeometry { /// 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: UserData, - F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), + where + D: UserGeometryData, + F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), { match self.kind { GeometryKind::USER => unsafe { let mut state = self.state.lock().unwrap(); let mut closure = bounds; - state.user_data.user_bounds_payload = - &mut closure as *mut _ as *mut std::os::raw::c_void; + state.data + .user_fns + .as_mut() + .unwrap() + .bounds_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; sys::rtcSetGeometryBoundsFunction( self.handle, crate::callback::user_bounds_function_helper(&mut closure), @@ -128,20 +136,24 @@ impl UserGeometry { /// 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 unsafe fn set_intersect_function(&mut self, intersect: F) - where - D: UserData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayHitN, u32), + pub fn set_intersect_function(&mut self, intersect: F) + where + D: UserGeometryData, + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayHitN, u32), { // TODO: deal with RTCRayHitN let mut state = self.state.lock().unwrap(); let mut closure = intersect; - state.user_data.user_intersect_payload = + state.data + .user_fns + .as_mut() + .unwrap() + .intersect_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; - sys::rtcSetGeometryIntersectFunction( + unsafe { sys::rtcSetGeometryIntersectFunction( self.handle, crate::callback::user_intersect_function_helper(&mut closure), - ); + ) }; } // TODO(yang): deal with RTCRayN, then we can make this function safe @@ -149,20 +161,21 @@ impl UserGeometry { /// /// Similar to [`Geometry::set_intersect_function`], but for occlusion /// queries. - pub unsafe fn set_occluded_function(&mut self, occluded: F) - where - D: UserData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayN, u32), + pub fn set_occluded_function(&mut self, occluded: F) + where + D: UserGeometryData, + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayN, u32), { // TODO: deal with RTCRayN let mut state = self.state.lock().unwrap(); let mut closure = occluded; - state.user_data.user_occluded_payload = - &mut closure as *mut _ as *mut std::os::raw::c_void; - sys::rtcSetGeometryOccludedFunction( + state.data + .user_fns.as_mut().unwrap().occluded_fn + = &mut closure as *mut _ as *mut std::os::raw::c_void; + unsafe { sys::rtcSetGeometryOccludedFunction( self.handle, crate::callback::user_occluded_function_helper(&mut closure), - ); + ) }; } /// Sets the number of primitives of a user-defined geometry. @@ -172,4 +185,4 @@ impl UserGeometry { sys::rtcSetGeometryUserPrimitiveCount(self.handle, count); } } -} \ No newline at end of file +} diff --git a/src/scene.rs b/src/scene.rs index 01fe9c175..19b36fd12 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -33,6 +33,15 @@ impl Clone for Scene { } } +impl Drop for Scene { + fn drop(&mut self) { + println!("Dropping scene"); + unsafe { + rtcReleaseScene(self.handle); + } + } +} + impl Scene { /// Creates a new scene with the given device. pub(crate) fn new(device: Device) -> Result { @@ -434,11 +443,3 @@ impl Scene { bounds } } - -impl Drop for Scene { - fn drop(&mut self) { - unsafe { - rtcReleaseScene(self.handle); - } - } -} From 5becb5ac16b3964f1f9317792b41f1614f971e6d Mon Sep 17 00:00:00 2001 From: matthiascy Date: Tue, 21 Feb 2023 18:29:13 +0100 Subject: [PATCH 37/65] instancing example done --- examples/dynamic_scene/src/main.rs | 4 +- examples/instancing/src/main.rs | 149 +++++++++++------------ src/callback.rs | 12 +- src/{intersect_context.rs => context.rs} | 9 ++ src/device.rs | 8 +- src/geometry.rs | 30 ++--- src/geometry/instance.rs | 14 ++- src/geometry/user_geom.rs | 38 +++--- src/lib.rs | 10 +- src/scene.rs | 3 +- 10 files changed, 132 insertions(+), 145 deletions(-) rename src/{intersect_context.rs => context.rs} (85%) diff --git a/examples/dynamic_scene/src/main.rs b/examples/dynamic_scene/src/main.rs index fe2404bf6..9ba3e89d4 100644 --- a/examples/dynamic_scene/src/main.rs +++ b/examples/dynamic_scene/src/main.rs @@ -193,8 +193,7 @@ fn main() { let display = support::Display::new(512, 512, "Dynamic Scene"); let light_dir = vec3(1.0, 1.0, 1.0).normalize(); - let mut time = 0.0; - support::display::run(display, move |image, camera_pose, _| { + support::display::run(display, move |image, camera_pose, time| { for p in image.iter_mut() { *p = 0; } @@ -243,6 +242,5 @@ fn main() { } } } - time += 0.2; }); } diff --git a/examples/instancing/src/main.rs b/examples/instancing/src/main.rs index 7d5b408d5..f0bf2283b 100644 --- a/examples/instancing/src/main.rs +++ b/examples/instancing/src/main.rs @@ -1,26 +1,50 @@ #![allow(dead_code)] - -extern crate cgmath; extern crate embree; extern crate support; use cgmath::{InnerSpace, Matrix, Matrix4, SquareMatrix, Vector3, Vector4}; use embree::{ - BufferUsage, BuildQuality, Device, Format, Geometry, Instance, IntersectContext, QuadMesh, Ray, - RayHit, Scene, SceneFlags, TriangleMesh, + BufferUsage, BuildQuality, Device, Format, Geometry, Instance, IntersectContext, Ray, + SceneFlags, INVALID_ID, }; -use std::{f32, u32}; use support::Camera; const NUM_PHI: usize = 5; const NUM_THETA: usize = 2 * NUM_PHI; -fn create_sphere(device: &Device, pos: Vec3, radius: f32) -> Geometry<'static> { +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(quality); + geometry.set_build_quality(BuildQuality::LOW); let mut vertices = geometry .set_new_buffer( @@ -125,7 +149,7 @@ fn animate_instances(time: f32, num_instances: usize) -> (Vec>, Vec 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)), ); @@ -136,12 +160,12 @@ fn animate_instances(time: f32, num_instances: usize) -> (Vec>, Vec } fn main() { - let mut display = support::Display::new(512, 512, "instancing"); + 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).unwrap(); + scene.set_build_quality(BuildQuality::LOW); scene.set_flags(SceneFlags::DYNAMIC); // Create a scene with 4 triangulated spheres. @@ -157,78 +181,39 @@ fn main() { } scene1.commit(); - let mut instanced_scene = Scene::new(&device); - for s in spheres.into_iter() { - instanced_scene.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), + // 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 = make_ground_plane(&device); - let ground_id = scene.attach_geometry(ground); + let ground_plane = create_ground_plane(&device); + let ground_plane_id = scene.attach_geometry(&ground_plane); let light_dir = Vector3::new(1.0, 1.0, -1.0).normalize(); let mut intersection_ctx = IntersectContext::coherent(); - display.run(|image, camera_pose, time| { + support::display::run(display, move |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")); - } - // 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(); + let (transforms, normal_transforms) = animate_instances(time, instances.len()); + for (inst, tfm) in instances.iter_mut().zip(transforms.iter()) { + inst.set_transform(0, tfm.as_ref()); + inst.commit(); } - - let rtscene = scene.commit(); + scene.commit(); let img_dims = image.dimensions(); let camera = Camera::look_dir( @@ -238,29 +223,33 @@ fn main() { 55.0, img_dims, ); + // 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); + let ray_hit = scene.intersect( + &mut intersection_ctx, + Ray::new(camera.pos.into(), dir.into()), + ); - if ray_hit.hit.hit() { + if ray_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::new(hit.Ng_x, hit.Ng_y, hit.Ng_z).normalize(); - if inst_id != u32::MAX { + if inst_id != INVALID_ID { 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); + let mut shadow_ray = + Ray::segment(shadow_pos.into(), light_dir.into(), 0.001, f32::INFINITY); + scene.occluded(&mut intersection_ctx, &mut shadow_ray); if shadow_ray.tfar >= 0.0 { illum = @@ -268,16 +257,16 @@ fn main() { } let p = image.get_pixel_mut(i, j); - if inst_id == u32::MAX && geom_id == ground_id { + if inst_id == INVALID_ID && geom_id == ground_plane_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; + let color = &COLORS[inst_id as usize][geom_id as usize]; + p[0] = (255.0 * illum * color[0]) as u8; + p[1] = (255.0 * illum * color[1]) as u8; + p[2] = (255.0 * illum * color[2]) as u8; } } } diff --git a/src/callback.rs b/src/callback.rs index d6cd84a96..1cd0e35eb 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -91,7 +91,7 @@ where } else { Some(&mut *(user_data.data as *mut D)) } - }, + } None => None, } }; @@ -142,7 +142,7 @@ where } else { Some(&mut *(user_data.data as *mut D)) } - }, + } None => None, } }; @@ -193,7 +193,7 @@ where } else { Some(&mut *(user_data.data as *mut D)) } - }, + } None => None, } }; @@ -242,7 +242,7 @@ where } else { Some(&mut *(user_data.data as *mut D)) } - }, + } None => None, } }; @@ -290,7 +290,7 @@ where } else { Some(&mut *(user_data.data as *mut D)) } - }, + } None => None, } }; @@ -305,3 +305,5 @@ where Some(inner::) } + +// TODO: point query function helper diff --git a/src/intersect_context.rs b/src/context.rs similarity index 85% rename from src/intersect_context.rs rename to src/context.rs index 572df68b5..8e71acef8 100644 --- a/src/intersect_context.rs +++ b/src/context.rs @@ -51,3 +51,12 @@ impl IntersectContext { } } } + +/// 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/device.rs b/src/device.rs index 3650c399a..2114632ed 100644 --- a/src/device.rs +++ b/src/device.rs @@ -31,9 +31,7 @@ impl Drop for Device { unsafe impl Sync for Device {} impl Device { - pub fn new() -> Result { - create_device(None) - } + pub fn new() -> Result { create_device(None) } pub fn debug() -> Result { let cfg = CString::new("verbose=4").unwrap(); @@ -195,9 +193,7 @@ impl Device { pub fn get_error(&self) -> RTCError { unsafe { rtcGetDeviceError(self.handle) } } /// Creates a new scene bound to the device. - pub fn create_scene(&self) -> Result { - Scene::new(self.clone()) - } + pub fn create_scene(&self) -> Result { 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 diff --git a/src/geometry.rs b/src/geometry.rs index 8a1e40101..d605e5962 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,10 +1,5 @@ use std::{ - any::TypeId, - collections::HashMap, - marker::PhantomData, - num::NonZeroUsize, - ptr, - sync::{Mutex}, + any::TypeId, collections::HashMap, marker::PhantomData, num::NonZeroUsize, ptr, sync::Mutex, }; use crate::{ @@ -12,8 +7,10 @@ use crate::{ IntersectContext, }; -use std::ops::{Deref, DerefMut}; -use std::rc::Rc; +use std::{ + ops::{Deref, DerefMut}, + rc::Rc, +}; mod instance; mod user_geom; @@ -672,8 +669,7 @@ impl<'buf> Geometry<'buf> { let mut state = self.state.lock().unwrap(); unsafe { let mut closure = filter; - state.data.intersect_filter_fn = - &mut closure as *mut _ as *mut std::os::raw::c_void; + state.data.intersect_filter_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; rtcSetGeometryIntersectFilterFunction( self.handle, crate::callback::intersect_filter_function_helper(&mut closure), @@ -718,8 +714,7 @@ impl<'buf> Geometry<'buf> { let mut state = self.state.lock().unwrap(); unsafe { let mut closure = filter; - state.data.occluded_filter_fn = - &mut closure as *mut _ as *mut std::os::raw::c_void; + state.data.occluded_filter_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; rtcSetGeometryOccludedFilterFunction( self.handle, crate::callback::occluded_filter_function_helper(&mut closure), @@ -924,16 +919,12 @@ impl<'buf> Geometry<'buf> { D: UserGeometryData, { let mut state = self.state.lock().unwrap(); - state.data. - user_data = Some(UserData { + state.data.user_data = Some(UserData { data: user_data as *mut D as *mut std::os::raw::c_void, type_id: TypeId::of::(), }); unsafe { - rtcSetGeometryUserData( - self.handle, - &mut state.data as *mut GeometryData as *mut _, - ); + rtcSetGeometryUserData(self.handle, &mut state.data as *mut GeometryData as *mut _); } } @@ -943,8 +934,7 @@ impl<'buf> Geometry<'buf> { D: UserGeometryData, { unsafe { - let ptr = - rtcGetGeometryUserData(self.handle) as *mut GeometryData; + let ptr = rtcGetGeometryUserData(self.handle) as *mut GeometryData; if ptr.is_null() { None } else { diff --git a/src/geometry/instance.rs b/src/geometry/instance.rs index caa9f50e7..bbbc9025c 100644 --- a/src/geometry/instance.rs +++ b/src/geometry/instance.rs @@ -24,31 +24,33 @@ impl Instance { unsafe { sys::rtcSetGeometryInstancedScene(self.handle, scene.handle) } } - // TODO(yang): Better transform type /// Returns the interpolated instance transformation for the specified time /// step. - pub fn get_geometry_transform(&mut self, time: f32, format: Format) -> [f32; 16] { + /// + /// The transformation is returned as a 4x4 column-major matrix. + pub fn get_transform(&mut self, time: f32) -> [f32; 16] { unsafe { let mut transform = [0.0; 16]; sys::rtcGetGeometryTransform( self.handle, time, - format, + Format::FLOAT4X4_COLUMN_MAJOR, transform.as_mut_ptr() as *mut _, ); transform } } - // TODO(yang): Better transform type /// Sets the transformation for a particular time step of an instance /// geometry. - pub fn set_geometry_transform(&mut self, time_step: u32, format: Format, transform: &[f32]) { + /// + /// The transformation is specified as a 4x4 column-major matrix. + pub fn set_transform(&mut self, time_step: u32, transform: &[f32; 16]) { unsafe { sys::rtcSetGeometryTransform( self.handle, time_step, - format, + Format::FLOAT4X4_COLUMN_MAJOR, transform.as_ptr() as *const _, ); } diff --git a/src/geometry/user_geom.rs b/src/geometry/user_geom.rs index f48d514ec..d0bca91cf 100644 --- a/src/geometry/user_geom.rs +++ b/src/geometry/user_geom.rs @@ -63,11 +63,8 @@ impl UserGeometry { GeometryKind::USER => unsafe { let mut state = self.state.lock().unwrap(); let mut closure = bounds; - state.data - .user_fns - .as_mut() - .unwrap() - .bounds_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; + state.data.user_fns.as_mut().unwrap().bounds_fn = + &mut closure as *mut _ as *mut std::os::raw::c_void; sys::rtcSetGeometryBoundsFunction( self.handle, crate::callback::user_bounds_function_helper(&mut closure), @@ -144,16 +141,14 @@ impl UserGeometry { // TODO: deal with RTCRayHitN let mut state = self.state.lock().unwrap(); let mut closure = intersect; - state.data - .user_fns - .as_mut() - .unwrap() - .intersect_fn = + state.data.user_fns.as_mut().unwrap().intersect_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; - unsafe { sys::rtcSetGeometryIntersectFunction( - self.handle, - crate::callback::user_intersect_function_helper(&mut closure), - ) }; + unsafe { + sys::rtcSetGeometryIntersectFunction( + self.handle, + crate::callback::user_intersect_function_helper(&mut closure), + ) + }; } // TODO(yang): deal with RTCRayN, then we can make this function safe @@ -169,13 +164,14 @@ impl UserGeometry { // TODO: deal with RTCRayN let mut state = self.state.lock().unwrap(); let mut closure = occluded; - state.data - .user_fns.as_mut().unwrap().occluded_fn - = &mut closure as *mut _ as *mut std::os::raw::c_void; - unsafe { sys::rtcSetGeometryOccludedFunction( - self.handle, - crate::callback::user_occluded_function_helper(&mut closure), - ) }; + state.data.user_fns.as_mut().unwrap().occluded_fn = + &mut closure as *mut _ as *mut std::os::raw::c_void; + unsafe { + sys::rtcSetGeometryOccludedFunction( + self.handle, + crate::callback::user_occluded_function_helper(&mut closure), + ) + }; } /// Sets the number of primitives of a user-defined geometry. diff --git a/src/lib.rs b/src/lib.rs index 62793a793..7405e3ab0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,10 +15,10 @@ use std::{alloc, mem}; mod buffer; mod callback; +mod context; mod device; mod error; mod geometry; -mod intersect_context; mod ray; mod scene; @@ -29,10 +29,10 @@ mod scene; pub mod sys; pub use buffer::*; +pub use context::*; pub use device::*; pub use error::*; pub use geometry::*; -pub use intersect_context::*; pub use ray::*; pub use scene::*; @@ -106,6 +106,12 @@ impl Bounds { 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. +type PointQuery = sys::RTCPointQuery; + /// Utility for making specifically aligned vectors pub fn aligned_vector(len: usize, align: usize) -> Vec { let t_size = mem::size_of::(); diff --git a/src/scene.rs b/src/scene.rs index 19b36fd12..951f8e721 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -7,9 +7,9 @@ use std::{ use crate::{ callback, + context::IntersectContext, device::Device, geometry::Geometry, - intersect_context::IntersectContext, ray::{Ray, Ray4, RayHit, RayHit4, RayHitN, RayN}, sys::*, }; @@ -35,7 +35,6 @@ impl Clone for Scene { impl Drop for Scene { fn drop(&mut self) { - println!("Dropping scene"); unsafe { rtcReleaseScene(self.handle); } From 2b281e646b1e7a7789b453d81b17a9f29c85d1ff Mon Sep 17 00:00:00 2001 From: matthiascy Date: Tue, 21 Feb 2023 20:17:55 +0100 Subject: [PATCH 38/65] add quaternion decomposition --- examples/dynamic_scene/src/main.rs | 8 +- src/device.rs | 3 +- src/lib.rs | 122 ++++++++++++++++++++++++++++- 3 files changed, 127 insertions(+), 6 deletions(-) diff --git a/examples/dynamic_scene/src/main.rs b/examples/dynamic_scene/src/main.rs index 9ba3e89d4..9b1ca2e4f 100644 --- a/examples/dynamic_scene/src/main.rs +++ b/examples/dynamic_scene/src/main.rs @@ -10,12 +10,12 @@ const NUM_SPHERES: usize = 20; const NUM_PHI: usize = 120; const NUM_THETA: usize = 2 * NUM_PHI; -fn create_sphere( - device: &Device, +fn create_sphere<'a, 'b>( + device: &'a Device, quality: BuildQuality, pos: Vec3, radius: f32, -) -> Geometry<'static> { +) -> Geometry<'b> { // Create a triangulated sphere let mut geometry = device .create_geometry(embree::GeometryKind::TRIANGLE) @@ -85,7 +85,7 @@ fn create_sphere( geometry } -fn create_ground_plane(device: &Device) -> Geometry<'static> { +fn create_ground_plane<'a, 'b>(device: &'a Device) -> Geometry<'b> { let mut geometry = Geometry::new(device, embree::GeometryKind::TRIANGLE).unwrap(); { geometry diff --git a/src/device.rs b/src/device.rs index 2114632ed..602ac7015 100644 --- a/src/device.rs +++ b/src/device.rs @@ -211,7 +211,8 @@ impl Device { /// Creates a [`Geometry`] object bound to the device without any /// buffers attached. - pub fn create_geometry(&self, kind: GeometryKind) -> Result, Error> { + pub fn create_geometry<'a, 'b>(&'a self, kind: GeometryKind) -> Result, Error> + { Geometry::new(self, kind) } } diff --git a/src/lib.rs b/src/lib.rs index 7405e3ab0..5651c8885 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ //! for some example applications using the bindings. use std::{alloc, mem}; +use std::mem::MaybeUninit; mod buffer; mod callback; @@ -77,8 +78,127 @@ 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 +/// +/// ``` +/// | 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 +/// ``` +/// | 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 +/// ```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: +/// +/// ``` +/// 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]; + } +} + /// The invalid ID for Embree intersection results (e.g. `Hit::geomID`, /// `Hit::primID`, etc.) pub const INVALID_ID: u32 = u32::MAX; @@ -110,7 +230,7 @@ impl Bounds { /// for each primitive of the scene that intersects the query domain. /// /// See [`Scene::point_query`] for more information. -type PointQuery = sys::RTCPointQuery; +pub type PointQuery = sys::RTCPointQuery; /// Utility for making specifically aligned vectors pub fn aligned_vector(len: usize, align: usize) -> Vec { From b6bfde9250b68578e519353ea3f49dd9c671963e Mon Sep 17 00:00:00 2001 From: matthiascy Date: Tue, 21 Feb 2023 23:25:33 +0100 Subject: [PATCH 39/65] some intersection methods --- src/bvh.rs | 31 +++++++++++++++++ src/device.rs | 8 +++-- src/lib.rs | 37 ++++++++++---------- src/ray.rs | 6 ++-- src/ray/packet.rs | 19 ++++++++--- src/scene.rs | 86 +++++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 155 insertions(+), 32 deletions(-) create mode 100644 src/bvh.rs diff --git a/src/bvh.rs b/src/bvh.rs new file mode 100644 index 000000000..b962c207f --- /dev/null +++ b/src/bvh.rs @@ -0,0 +1,31 @@ +use crate::{sys, BuildFlags, BuildQuality, Device, Error}; + +pub struct Bvh { + handle: sys::RTCBVH, +} + +impl Clone for Bvh { + fn clone(&self) -> Self { + unsafe { sys::rtcRetainBVH(self.handle) } + Self { + handle: self.handle, + } + } +} + +impl Drop for Bvh { + fn drop(&mut self) { unsafe { sys::rtcReleaseBVH(self.handle) } } +} + +impl Bvh { + pub(crate) fn new(device: &Device) -> Result { + let handle = unsafe { sys::rtcNewBVH(device.handle) }; + if handle.is_null() { + Err(device.get_error()) + } else { + Ok(Self { handle }) + } + } + + // TODO: BVH build functions +} diff --git a/src/device.rs b/src/device.rs index 602ac7015..8ba90263c 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,4 +1,4 @@ -use crate::{sys::*, Buffer, BufferSize, Error, Geometry, GeometryKind, Scene}; +use crate::{sys::*, Buffer, BufferSize, Bvh, Error, Geometry, GeometryKind, Scene}; use std::{ ffi::CString, fmt::{self, Display, Formatter}, @@ -203,6 +203,9 @@ impl Device { Scene::new_with_flags(self.clone(), flags) } + /// 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 { @@ -211,8 +214,7 @@ impl Device { /// Creates a [`Geometry`] object bound to the device without any /// buffers attached. - pub fn create_geometry<'a, 'b>(&'a self, kind: GeometryKind) -> Result, Error> - { + pub fn create_geometry<'a, 'b>(&'a self, kind: GeometryKind) -> Result, Error> { Geometry::new(self, kind) } } diff --git a/src/lib.rs b/src/lib.rs index 5651c8885..68a8083a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,10 +11,10 @@ //! See the [examples/](https://github.com/Twinklebear/embree-rs/tree/master/examples) //! for some example applications using the bindings. -use std::{alloc, mem}; -use std::mem::MaybeUninit; +use std::{alloc, mem, mem::MaybeUninit}; mod buffer; +mod bvh; mod callback; mod context; mod device; @@ -30,6 +30,7 @@ mod scene; pub mod sys; pub use buffer::*; +pub use bvh::*; pub use context::*; pub use device::*; pub use error::*; @@ -79,7 +80,8 @@ 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. +/// Structure that represents a quaternion decomposition of an affine +/// transformation. /// /// The affine transformation can be decomposed into three parts: /// @@ -101,9 +103,9 @@ pub type GeometryKind = sys::RTCGeometryType; /// ``` /// /// 3. A rotation matrix R, represented as a quaternion -/// ```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. +/// ```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: @@ -114,9 +116,7 @@ pub type GeometryKind = sys::RTCGeometryType; pub type QuaternionDecomposition = sys::RTCQuaternionDecomposition; impl Default for QuaternionDecomposition { - fn default() -> Self { - QuaternionDecomposition::identity() - } + fn default() -> Self { QuaternionDecomposition::identity() } } impl QuaternionDecomposition { @@ -143,23 +143,22 @@ impl QuaternionDecomposition { } /// Returns the scale part of the decomposition. - pub fn scale(&self) -> [f32; 3] { - [self.scale_x, self.scale_y, self.scale_z] - } + 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] - } + 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] - } + 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] + [ + self.quaternion_r, + self.quaternion_i, + self.quaternion_j, + self.quaternion_k, + ] } /// Set the quaternion part of the decomposition. diff --git a/src/ray.rs b/src/ray.rs index 9f99d5a71..d1fd7a1a7 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -31,7 +31,7 @@ pub use stream::*; /// /// [`packet`](`crate::ray::packet`) defines types in SOA (structure of array) /// layout for ray packets of size 4 (RTCRay4 type), size 8 (RTCRay8 type), -/// and size 16 (RTCRay16 type). A const-generic type [`RayPacket`] is +/// and size 16 (RTCRay16 type). A const-generic type [`RayNt`] is /// defined for ray packets of arbitrary size N at compile time. /// /// See [`sys::RTCRay`] for more details. @@ -99,7 +99,7 @@ impl Ray { /// of size 4 (RTCHit4 type), size 8 (RTCHit8 type), and size 16 (RTCHit16 /// type). /// -/// [`HitPacket`] defines the type for hit packets of arbitrary size N at +/// [`HitNt`] defines the type for hit packets of arbitrary size N at /// compile time. /// /// See [`sys::RTCHit`] for more details. @@ -145,7 +145,7 @@ impl Hit { /// /// [`packet`](`crate::ray::packet`) defines types in SOA (structure of array) /// layout for ray/hit packets of size 4 (RTCRayHit4 type), size 8 (RTCRayHit8 -/// type), and size 16 (RTCRayHit16 type). A const-generic type [`RayHitPacket`] +/// type), and size 16 (RTCRayHit16 type). A const-generic type [`RayHitNt`] /// is defined for ray/hit packets of arbitrary size N at compile time. /// /// See [`sys::RTCRayHit`] for more details. diff --git a/src/ray/packet.rs b/src/ray/packet.rs index c6b41ee63..e00123ea1 100644 --- a/src/ray/packet.rs +++ b/src/ray/packet.rs @@ -4,6 +4,14 @@ pub type Ray4 = sys::RTCRay4; pub type Hit4 = sys::RTCHit4; pub type RayHit4 = sys::RTCRayHit4; +pub type Ray8 = sys::RTCRay8; +pub type Hit8 = sys::RTCHit8; +pub type RayHit8 = sys::RTCRayHit8; + +pub type Ray16 = sys::RTCRay16; +pub type Hit16 = sys::RTCHit16; +pub type RayHit16 = sys::RTCRayHit16; + impl Ray4 { pub fn empty() -> Ray4 { Ray4::segment( @@ -132,7 +140,7 @@ impl RayHit4 { } } -pub struct RayPacket { +pub struct RayNt { pub org_x: [f32; N], pub org_y: [f32; N], pub org_z: [f32; N], @@ -147,7 +155,8 @@ pub struct RayPacket { pub flags: [u32; N], } -pub struct HitPacket { +#[allow(non_snake_case)] +pub struct HitNt { pub Ng_x: [f32; N], pub Ng_y: [f32; N], pub Ng_z: [f32; N], @@ -158,7 +167,7 @@ pub struct HitPacket { pub instID: [[u32; 1]; N], } -pub struct RayHitPacket { - pub ray: RayPacket, - pub hit: HitPacket, +pub struct RayHitNt { + pub ray: RayNt, + pub hit: HitNt, } diff --git a/src/scene.rs b/src/scene.rs index 951f8e721..ad2e43a3f 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,4 +1,4 @@ -use crate::{Bounds, Error, SceneFlags}; +use crate::{Bounds, Error, RayHit8, SceneFlags}; use std::{ collections::HashMap, mem, @@ -324,7 +324,11 @@ impl Scene { /// /// # Arguments /// - /// * `ctx` - The intersection context to use for the ray query. + /// * `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 IntersectContext, ray: Ray) -> RayHit { let mut ray_hit = RayHit::new(ray); @@ -339,6 +343,19 @@ impl Scene { } /// 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 IntersectContext, ray: &mut Ray) -> bool { unsafe { rtcOccluded1( @@ -350,6 +367,20 @@ impl Scene { ray.tfar == -f32::INFINITY } + /// 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 to intersect with the scene. + /// * `valid` - A mask indicating which rays in the packet are valid. -1 means + /// valid, 0 means invalid. + /// + /// Only active rays are processed, and hit data of inactive rays is not changed. pub fn intersect4(&self, ctx: &mut IntersectContext, ray: &mut RayHit4, valid: &[i32; 4]) { unsafe { rtcIntersect4( @@ -361,6 +392,31 @@ impl Scene { } } + /// 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 to intersect with the scene. + /// * `valid` - A mask indicating which rays in the packet are valid. -1 means + /// valid, 0 means invalid. + /// + /// Only active rays are processed, and hit data of inactive rays is not changed. + pub fn intersect8(&self, ctx: &mut IntersectContext, ray: &mut RayHit8, valid: &[i32; 8]) { + unsafe { + rtcIntersect8( + valid.as_ptr(), + self.handle, + ctx as *mut RTCIntersectContext, + ray as *mut RTCRayHit8, + ); + } + } + pub fn occluded4(&self, ctx: &mut IntersectContext, ray: &mut Ray4, valid: &[i32; 4]) { unsafe { rtcOccluded4( @@ -398,6 +454,18 @@ impl Scene { } } + /// 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 IntersectContext, rays: &mut RayHitN) { let n = rays.len(); unsafe { @@ -411,6 +479,18 @@ impl Scene { } } + /// 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 IntersectContext, rays: &mut RayN) { let n = rays.len(); unsafe { @@ -421,6 +501,8 @@ impl Scene { &mut r as *mut RTCRayNp, n as u32, ); + + let a: RTCRayN; } } From 59e34f129cb4a7bc5e30ecd722b23f720887af46 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Wed, 22 Feb 2023 20:51:45 +0100 Subject: [PATCH 40/65] separate subdivision & impl safe ray packet access in callback functions --- examples/triangle/src/main.rs | 4 +- src/callback.rs | 132 ++++++++-- src/geometry.rs | 239 +++---------------- src/geometry/subdivision.rs | 134 +++++++++++ src/lib.rs | 1 + src/ray/packet.rs | 436 ++++++++++++++++++++++++++++++---- src/ray/stream.rs | 44 ++-- src/scene.rs | 350 ++++++++++++++++++++++----- 8 files changed, 983 insertions(+), 357 deletions(-) create mode 100644 src/geometry/subdivision.rs diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index 0920ae6dd..905913b3d 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -3,7 +3,7 @@ extern crate embree; extern crate support; -use embree::{BufferUsage, Device, Format, IntersectContext, RayHitN, RayN, TriangleMesh}; +use embree::{BufferUsage, Device, Format, IntersectContext, RayHitN, RayStream, TriangleMesh}; fn main() { let display = support::Display::new(512, 512, "triangle"); @@ -47,7 +47,7 @@ fn main() { let y = -(j as f32 + 0.5) / img_dims.1 as f32 + 0.5; // Try out streams of scanlines across x - let mut rays = RayN::new(img_dims.0 as usize); + let mut rays = RayStream::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); diff --git a/src/callback.rs b/src/callback.rs index 1cd0e35eb..58a6ebcde 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -1,4 +1,6 @@ -use crate::{geometry::GeometryData, Bounds, IntersectContext, UserGeometryData}; +use crate::{ + geometry::GeometryData, Bounds, Geometry, HitN, IntersectContext, RayN, UserGeometryData, +}; use std::{ any::{Any, TypeId}, os::raw::c_void, @@ -166,19 +168,12 @@ where pub fn intersect_filter_function_helper(_f: &mut F) -> RTCFilterFunctionN where D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, &mut RTCRayN, &mut RTCHitN, u32), + F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), { unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) where D: UserGeometryData, - F: FnMut( - &mut [i32], - Option<&mut D>, - &mut IntersectContext, - &mut RTCRayN, - &mut RTCHitN, - u32, - ), + F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), { let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)).intersect_filter_fn as *mut F; @@ -201,9 +196,14 @@ where std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), user_data, &mut *(*args).context, - &mut *(*args).ray, - &mut *(*args).hit, - (*args).N, + RayN { + ptr: &mut *(*args).ray, + len: (*args).N as usize, + }, + HitN { + ptr: &mut *(*args).hit, + len: (*args).N as usize, + }, ); } } @@ -216,20 +216,14 @@ where pub fn occluded_filter_function_helper(_f: &mut F) -> RTCFilterFunctionN where D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, &mut RTCRayN, &mut RTCHitN, u32), + F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), { unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) where D: UserGeometryData, - F: FnMut( - &mut [i32], - Option<&mut D>, - &mut IntersectContext, - &mut RTCRayN, - &mut RTCHitN, - u32, - ), + F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), { + let len = (*args).N as usize; let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)).occluded_filter_fn as *mut F; if !cb_ptr.is_null() { let cb = &mut *cb_ptr; @@ -247,12 +241,17 @@ where } }; cb( - std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), + std::slice::from_raw_parts_mut((*args).valid, len), user_data, &mut *(*args).context, - &mut *(*args).ray, - &mut *(*args).hit, - (*args).N, + RayN { + ptr: &mut *(*args).ray, + len, + }, + HitN { + ptr: &mut *(*args).hit, + len, + }, ); } } @@ -306,4 +305,85 @@ where Some(inner::) } +/// Helper function to convert a Rust closure to `RTCDisplacementFunctionN` +/// callback. +pub fn subdivision_displacement_function_helper(_f: &mut F) -> RTCDisplacementFunctionN +where + D: UserGeometryData, + F: FnMut( + Option<&mut D>, + RTCGeometry, + u32, + u32, + &[f32], + &[f32], + &[f32], + &[f32], + &[f32], + &mut [f32], + &mut [f32], + &mut [f32], + ), +{ + unsafe extern "C" fn inner(args: *const RTCDisplacementFunctionNArguments) + where + D: UserGeometryData, + F: FnMut( + Option<&mut D>, + RTCGeometry, + u32, + u32, + &[f32], + &[f32], + &[f32], + &[f32], + &[f32], + &mut [f32], + &mut [f32], + &mut [f32], + ), + { + let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) + .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 (*((*args).geometryUserPtr as *mut GeometryData)).user_data { + Some(ref user_data) => { + if user_data.data.is_null() || user_data.data.type_id() != TypeId::of::() + { + None + } else { + Some(&mut *(user_data.data as *mut D)) + } + } + None => None, + } + }; + cb( + user_data, + (*args).geometry, + (*args).primID, + (*args).timeStep, + std::slice::from_raw_parts((*args).u, (*args).N as usize), + std::slice::from_raw_parts((*args).v, (*args).N as usize), + std::slice::from_raw_parts((*args).Ng_x, (*args).N as usize * 3), + std::slice::from_raw_parts((*args).Ng_y, (*args).N as usize * 3), + std::slice::from_raw_parts((*args).Ng_z, (*args).N as usize * 3), + std::slice::from_raw_parts_mut((*args).P_x, (*args).N as usize * 3), + std::slice::from_raw_parts_mut((*args).P_y, (*args).N as usize * 3), + std::slice::from_raw_parts_mut((*args).P_z, (*args).N as usize * 3), + ); + } + } + + Some(inner::) +} + // TODO: point query function helper diff --git a/src/geometry.rs b/src/geometry.rs index d605e5962..69fb039bc 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -2,10 +2,7 @@ use std::{ any::TypeId, collections::HashMap, marker::PhantomData, num::NonZeroUsize, ptr, sync::Mutex, }; -use crate::{ - sys::*, BufferSlice, BufferUsage, BuildQuality, Device, Error, Format, GeometryKind, - IntersectContext, -}; +use crate::{sys::*, BufferSlice, BufferUsage, BuildQuality, Device, Error, Format, GeometryKind, HitN, HitPacket, IntersectContext, RayN, RayPacket, Scene}; use std::{ ops::{Deref, DerefMut}, @@ -13,9 +10,11 @@ use std::{ }; mod instance; +mod subdivision; mod user_geom; pub use instance::*; +pub use subdivision::*; pub use user_geom::*; // TODO(yang): maybe enforce format and stride when get the view? @@ -59,6 +58,14 @@ pub(crate) struct UserGeometryPayloads { 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 [`SubdivisionGeometry::set_vertex_function`] call. + pub displacement_fn: *mut std::os::raw::c_void, +} + impl Default for UserGeometryPayloads { fn default() -> Self { Self { @@ -69,6 +76,14 @@ impl Default for UserGeometryPayloads { } } +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 @@ -83,6 +98,8 @@ pub(crate) struct GeometryData { 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, } /// Extra data for a geometry. @@ -197,11 +214,12 @@ impl<'buf> Geometry<'buf> { 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(), - }) + Some(UserGeometryPayloads::default()) + } else { + None + }, + subdivision_fns: if kind == GeometryKind::SUBDIVISION { + Some(SubdivisionGeometryPayloads::default()) } else { None }, @@ -631,40 +649,20 @@ impl<'buf> Geometry<'buf> { /// 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::intersect1M`], - /// [`Scene::intersect1Mp`], [`Scene::intersectNM`], or - /// [`Scene::intersectNp`] the order of rays and ray packet size of the - /// callback function might change to either 1, 4, 8, or 16. + /// using the stream API such as [`Scene::intersect_stream_aos`], + /// [`Scene::intersect1Mp`], [`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. - /// - /// The implementation of the filter function can choose to implement a - /// single code path that uses the ray access helper functions - /// `RTCRay_XXX` and hit access helper functions `RTCHit_XXX` to access - /// ray and hit data. Alternatively the code can branch to optimized - /// implementations for specific sizes of N and cast the ray - /// and hit inputs to the proper packet types. - /// - /// # Safety - /// - /// Because the Embree filter function does not provide the user - /// data pointer, we cannot use a closure but instead a static function - /// pointer. - pub unsafe fn set_intersect_filter_function(&mut self, filter: F) + pub fn set_intersect_filter_function(&mut self, filter: F) where D: UserGeometryData, - F: FnMut( - &mut [i32], - Option<&mut D>, - &mut IntersectContext, - &mut RTCRayN, - &mut RTCHitN, - u32, - ), + F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), { let mut state = self.state.lock().unwrap(); unsafe { @@ -693,23 +691,10 @@ impl<'buf> Geometry<'buf> { /// inside or outside the leaf. Please see the description of the /// [`Geometry::set_intersect_filter_function`] for a description of the /// filter callback function. - /// - /// # Safety - /// - /// Because the Embree filter function does not provide the user - /// data pointer, we cannot use a closure but instead a static function - /// pointer. - pub unsafe fn set_occluded_filter_function(&mut self, filter: F) + pub fn set_occluded_filter_function(&mut self, filter: F) where D: UserGeometryData, - F: FnMut( - &mut [i32], - Option<&mut D>, - &mut IntersectContext, - &mut RTCRayN, - &mut RTCHitN, - u32, - ), + F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), { let mut state = self.state.lock().unwrap(); unsafe { @@ -888,24 +873,6 @@ impl<'buf> Geometry<'buf> { } } - /// Sets the number of topologies of a subdivision geometry. - /// - /// The number of topologies of a subdivision geometry must be greater - /// or equal to 1. - /// - /// 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!("Geometry::set_topology_count is only supported for subdivision meshes"), - } - } - /// Sets the user-defined data pointer of the geometry. /// /// The user data pointer is intended to be pointing to the application's @@ -981,44 +948,6 @@ impl<'buf> Geometry<'buf> { } } - /// 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: - /// - /// * [`RTCSubdivisionMode::NO_BOUNDARY`]: Boundary patches are ignored. - /// This way each rendered patch has a full set of control vertices. - /// - /// * [`RTCSubdivisionMode::SMOOTH_BOUNDARY`]: The sequence of boundary - /// control points are used to generate a smooth B-spline boundary curve - /// (default mode). - /// - /// * [`RTCSubdivisionMode::PIN_CORNERS`]: Corner vertices are pinned to - /// their location during subdivision. - /// - /// * [`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. - /// - /// * [`RTCSubdivisionMode::PIN_ALL`]: All vertices at the border are pinned - /// to their location during subdivision. This way all patches are linearly - /// interpolated. - pub fn set_subdivision_mode(&self, topology_id: u32, mode: RTCSubdivisionMode) { - match self.kind { - GeometryKind::SUBDIVISION => unsafe { - rtcSetGeometrySubdivisionMode(self.handle, topology_id, mode); - }, - _ => { - panic!( - "Subdivision mode not allowed for geometries of type {:?}!", - self.kind - ); - } - } - } - /// Binds a vertex attribute to a topology of the geometry. /// /// This function binds a vertex attribute buffer slot to a topology for the @@ -1040,104 +969,6 @@ impl<'buf> Geometry<'buf> { rtcSetGeometryVertexAttributeTopology(self.handle, vertex_attribute_id, topology_id); } } - - // TODO(yang): Add documentation. - /// Sets the displacement function for a subdivision geometry. - pub unsafe fn set_displacement_function(&self, displacement: RTCDisplacementFunctionN) { - match self.kind { - GeometryKind::SUBDIVISION => unsafe { - rtcSetGeometryDisplacementFunction(self.handle, displacement); - }, - _ => { - panic!( - "Displacement function not allowed for geometries of type {:?}!", - self.kind - ); - } - } - } - - /// 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!( - "First half edge not allowed for geometries of type {:?}!", - self.kind - ); - } - } - } - - /// 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!("Face not allowed for geometries of type {:?}!", self.kind); - } - } - } - - /// 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!( - "Next half edge not allowed for geometries of type {:?}!", - self.kind - ); - } - } - } - - /// 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!( - "Prev half edge not allowed for geometries of type {:?}!", - self.kind - ); - } - } - } - - /// 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!( - "Opposite half edge not allowed for geometries of type {:?}!", - self.kind - ); - } - } - } } // TODO(yang): rtcInterpolate, rtcInterpolateN diff --git a/src/geometry/subdivision.rs b/src/geometry/subdivision.rs new file mode 100644 index 000000000..e9af62f16 --- /dev/null +++ b/src/geometry/subdivision.rs @@ -0,0 +1,134 @@ +use crate::{ + sys, sys::RTCDisplacementFunctionN, Device, Error, Geometry, GeometryKind, SubdivisionMode, + UserGeometryData, +}; +use std::ops::{Deref, DerefMut}; + +#[derive(Debug)] +pub struct SubdivisionGeometry(Geometry<'static>); + +impl Deref for SubdivisionGeometry { + type Target = Geometry<'static>; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl DerefMut for SubdivisionGeometry { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } +} + +impl SubdivisionGeometry { + pub fn new(device: &Device) -> Result { + Ok(Self(Geometry::new(device, GeometryKind::SUBDIVISION)?)) + } + + /// 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: + /// + /// * [`RTCSubdivisionMode::NO_BOUNDARY`]: Boundary patches are ignored. + /// This way each rendered patch has a full set of control vertices. + /// + /// * [`RTCSubdivisionMode::SMOOTH_BOUNDARY`]: The sequence of boundary + /// control points are used to generate a smooth B-spline boundary curve + /// (default mode). + /// + /// * [`RTCSubdivisionMode::PIN_CORNERS`]: Corner vertices are pinned to + /// their location during subdivision. + /// + /// * [`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. + /// + /// * [`RTCSubdivisionMode::PIN_ALL`]: All vertices at the border are pinned + /// to their location during subdivision. This way all patches are linearly + /// interpolated. + pub fn set_subdivision_mode(&self, topology_id: u32, mode: SubdivisionMode) { + unsafe { sys::rtcSetGeometrySubdivisionMode(self.handle, topology_id, mode) } + } + + /// Sets the number of topologies of a subdivision geometry. + /// + /// The number of topologies of a subdivision geometry must be greater + /// or equal to 1. + /// + /// 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) { + unsafe { sys::rtcSetGeometryTopologyCount(self.handle, count) } + } + + /// 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 { + unsafe { sys::rtcGetGeometryFirstHalfEdge(self.handle, face_id) } + } + + /// 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 { + unsafe { sys::rtcGetGeometryFace(self.handle, half_edge_id) } + } + + /// 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 { + unsafe { sys::rtcGetGeometryNextHalfEdge(self.handle, half_edge_id) } + } + + /// Returns the previous half edge of some half edge. + pub fn get_previous_half_edge(&self, half_edge_id: u32) -> u32 { + unsafe { sys::rtcGetGeometryPreviousHalfEdge(self.handle, half_edge_id) } + } + + /// Returns the opposite half edge of some half edge. + pub fn get_opposite_half_edge(&self, topology_id: u32, edge_id: u32) -> u32 { + unsafe { sys::rtcGetGeometryOppositeHalfEdge(self.handle, topology_id, edge_id) } + } + + // TODO(yang): Add documentation. + // TODO(yang): Better way to deal with RTCGeometry, maybe we need a lookup table to get the geometry from the handle. + /// Sets the displacement function for a subdivision geometry. + pub unsafe fn set_displacement_function(&self, displacement: F) + where + D: UserGeometryData, + F: FnMut( + Option<&mut D>, + sys::RTCGeometry, + u32, + u32, + &[f32], + &[f32], + &[f32], + &[f32], + &[f32], + &mut [f32], + &mut [f32], + &mut [f32], + ), + { + let mut state = self.state.lock().unwrap(); + unsafe { + let mut closure = displacement; + state.data.intersect_filter_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; + sys::rtcSetGeometryDisplacementFunction( + self.handle, + crate::callback::subdivision_displacement_function_helper(&mut closure), + ) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 68a8083a0..a1ea37f01 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(portable_simd)] //! [![Crates.io](https://img.shields.io/crates/v/embree.svg)](https://crates.io/crates/embree) //! [![Build Status](https://travis-ci.org/Twinklebear/embree-rs.svg?branch=master)](https://travis-ci.org/Twinklebear/embree-rs) //! diff --git a/src/ray/packet.rs b/src/ray/packet.rs index e00123ea1..1d14d17ef 100644 --- a/src/ray/packet.rs +++ b/src/ray/packet.rs @@ -1,29 +1,112 @@ -use crate::{sys, SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut}; +use crate::{ + sys, Hit, Ray, RayHit, SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut, +}; +use std::{marker::PhantomData, ops::Add}; +mod sealed { + pub trait Sealed {} +} + +/// 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; -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]) +/// Represents a packet of rays. +/// +/// Used as a trait bound for functions that operate on ray packets. +/// See [`Scene::occluded_stream_aos`] and [`Scene::intersect_stream_aos`]. +pub trait RayPacket: Sized + sealed::Sealed { + const LEN: usize; +} + +pub trait HitPacket: Sized + sealed::Sealed { + const LEN: usize; +} + +pub trait RayHitPacket: Sized + sealed::Sealed { + 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 sealed::Sealed for $ray {} + impl RayPacket for $ray { + const LEN: usize = $n; + } + + impl sealed::Sealed for $hit {} + impl HitPacket for $hit { + const LEN: usize = $n; + } + + impl sealed::Sealed for $rayhit {} + 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 fn new(origin: [[f32; 3]; $n], dir: [[f32; 3]; $n]) -> $t { + $t::segment(origin, dir, [0.0; $n], [f32::INFINITY; $n]) + } + + pub 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 Ray4 { pub fn segment( origin: [[f32; 3]; 4], dir: [[f32; 3]; 4], @@ -45,10 +128,157 @@ impl Ray4 { flags: [0; 4], } } - pub fn iter(&self) -> SoARayIter { SoARayIter::new(self, 4) } - pub fn iter_mut(&mut self) -> SoARayIterMut { SoARayIterMut::new(self, 4) } } +impl Ray8 { + pub fn segment( + origin: [[f32; 3]; 8], + dir: [[f32; 3]; 8], + tnear: [f32; 8], + tfar: [f32; 8], + ) -> Ray8 { + Ray8 { + org_x: [ + origin[0][0], + origin[1][0], + origin[2][0], + origin[3][0], + origin[4][0], + origin[5][0], + origin[6][0], + origin[7][0], + ], + org_y: [ + origin[0][1], + origin[1][1], + origin[2][1], + origin[3][1], + origin[4][1], + origin[5][1], + origin[6][1], + origin[7][1], + ], + org_z: [ + origin[0][2], + origin[1][2], + origin[2][2], + origin[3][2], + origin[4][2], + origin[5][2], + origin[6][2], + origin[7][2], + ], + dir_x: [ + dir[0][0], dir[1][0], dir[2][0], dir[3][0], dir[4][0], dir[5][0], dir[6][0], + dir[7][0], + ], + dir_y: [ + dir[0][1], dir[1][1], dir[2][1], dir[3][1], dir[4][1], dir[5][1], dir[6][1], + dir[7][1], + ], + dir_z: [ + dir[0][2], dir[1][2], dir[2][2], dir[3][2], dir[4][2], dir[5][2], dir[6][2], + dir[7][2], + ], + tnear, + tfar, + time: [0.0; 8], + mask: [u32::MAX; 8], + id: [0; 8], + flags: [0; 8], + } + } +} + +impl Ray16 { + pub fn segment( + origin: [[f32; 3]; 16], + dir: [[f32; 3]; 16], + tnear: [f32; 16], + tfar: [f32; 16], + ) -> Ray16 { + Ray16 { + org_x: [ + origin[0][0], + origin[1][0], + origin[2][0], + origin[3][0], + origin[4][0], + origin[5][0], + origin[6][0], + origin[7][0], + origin[8][0], + origin[9][0], + origin[10][0], + origin[11][0], + origin[12][0], + origin[13][0], + origin[14][0], + origin[15][0], + ], + org_y: [ + origin[0][1], + origin[1][1], + origin[2][1], + origin[3][1], + origin[4][1], + origin[5][1], + origin[6][1], + origin[7][1], + origin[8][1], + origin[9][1], + origin[10][1], + origin[11][1], + origin[12][1], + origin[13][1], + origin[14][1], + origin[15][1], + ], + org_z: [ + origin[0][2], + origin[1][2], + origin[2][2], + origin[3][2], + origin[4][2], + origin[5][2], + origin[6][2], + origin[7][2], + origin[8][2], + origin[9][2], + origin[10][2], + origin[11][2], + origin[12][2], + origin[13][2], + origin[14][2], + origin[15][2], + ], + dir_x: [ + dir[0][0], dir[1][0], dir[2][0], dir[3][0], dir[4][0], dir[5][0], dir[6][0], + dir[7][0], dir[8][0], dir[9][0], dir[10][0], dir[11][0], dir[12][0], dir[13][0], + dir[14][0], dir[15][0], + ], + dir_y: [ + dir[0][1], dir[1][1], dir[2][1], dir[3][1], dir[4][1], dir[5][1], dir[6][1], + dir[7][1], dir[8][1], dir[9][1], dir[10][1], dir[11][1], dir[12][1], dir[13][1], + dir[14][1], dir[15][1], + ], + dir_z: [ + dir[0][2], dir[1][2], dir[2][2], dir[3][2], dir[4][2], dir[5][2], dir[6][2], + dir[7][2], dir[8][2], dir[9][2], dir[10][2], dir[11][2], dir[12][2], dir[13][2], + dir[14][2], dir[15][2], + ], + tnear, + tfar, + time: [0.0; 16], + mask: [u32::MAX; 16], + id: [0; 16], + flags: [0; 16], + } + } +} + +impl_ray_packets!(Ray4, 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]) { @@ -140,34 +370,152 @@ impl RayHit4 { } } -pub struct RayNt { - pub org_x: [f32; N], - pub org_y: [f32; N], - pub org_z: [f32; N], - pub tnear: [f32; N], - pub dir_x: [f32; N], - pub dir_y: [f32; N], - pub dir_z: [f32; N], - pub time: [f32; N], - pub tfar: [f32; N], - pub mask: [u32; N], - pub id: [u32; N], - pub flags: [u32; N], -} - -#[allow(non_snake_case)] -pub struct HitNt { - pub Ng_x: [f32; N], - pub Ng_y: [f32; N], - pub Ng_z: [f32; N], - pub u: [f32; N], - pub v: [f32; N], - pub primID: [u32; N], - pub geomID: [u32; N], - pub instID: [[u32; 1]; N], -} - -pub struct RayHitNt { - pub ray: RayNt, - pub hit: HitNt, +/// 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. +pub struct RayN { + pub(crate) ptr: *mut sys::RTCRayN, + pub(crate) len: usize, +} + +impl RayN { + pub fn org_x(&self, i: usize) -> f32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const f32).add(i) } + } + + pub fn org_y(&self, i: usize) -> f32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const f32).add(self.len + i) } + } + + pub fn org_z(&self, i: usize) -> f32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const f32).add(2 * self.len + i) } + } + + pub 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) } + } + + pub fn dir_x(&self, i: usize) -> f32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const f32).add(4 * self.len + i) } + } + + pub fn dir_y(&self, i: usize) -> f32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const f32).add(5 * self.len + i) } + } + + pub fn dir_z(&self, i: usize) -> f32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const f32).add(6 * self.len + i) } + } + + pub 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) } + } + + pub 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) } + } + + pub 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) } + } + + pub 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) } + } + + pub 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) } + } +} + +/// 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 { + pub(crate) ptr: *mut sys::RTCHitN, + pub(crate) len: usize, +} + +impl HitN { + pub fn ng_x(&self, i: usize) -> f32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const f32).add(i) } + } + + pub fn ng_y(&self, i: usize) -> f32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const f32).add(self.len + i) } + } + + pub fn ng_z(&self, i: usize) -> f32 { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { *(self.ptr as *const f32).add(2 * self.len + i) } + } + + pub 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) } + } + + pub 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) } + } + + pub 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) } + } + + pub 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) } + } + + pub 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) } + } +} + +/// 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 { + pub(crate) ptr: *mut sys::RTCRayHitN, + pub(crate) len: usize, +} + +impl RayHitN { + /// Returns the ray packet. + pub fn ray_n(&self) -> RayN { + RayN { + ptr: self.ptr as *mut sys::RTCRayN, + len: self.len, + } + } + + /// Returns the hit packet. + pub fn hit_n(&self) -> HitN { + HitN { + ptr: unsafe { (self.ptr as *const u32).add(12 * self.len) as *mut sys::RTCHitN }, + len: self.len, + } + } } diff --git a/src/ray/stream.rs b/src/ray/stream.rs index 1c201aa1a..21d7ee732 100644 --- a/src/ray/stream.rs +++ b/src/ray/stream.rs @@ -6,7 +6,7 @@ use crate::{ }; /// A ray stream stored in SoA format -pub struct RayN { +pub struct RayStream { org_x: Vec, org_y: Vec, org_z: Vec, @@ -21,10 +21,10 @@ pub struct RayN { flags: Vec<::std::os::raw::c_uint>, } -impl RayN { +impl RayStream { /// Allocate a new Ray stream with room for `n` rays - pub fn new(n: usize) -> RayN { - RayN { + pub fn new(n: usize) -> RayStream { + RayStream { org_x: aligned_vector::(n, 16), org_y: aligned_vector::(n, 16), org_z: aligned_vector::(n, 16), @@ -39,8 +39,8 @@ impl RayN { flags: aligned_vector_init::(n, 16, 0), } } - pub fn iter(&self) -> SoARayIter { SoARayIter::new(self, self.len()) } - pub fn iter_mut(&mut self) -> SoARayIterMut { + 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) } @@ -64,7 +64,7 @@ impl RayN { } } -impl SoARay for RayN { +impl SoARay for RayStream { 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]; @@ -98,7 +98,7 @@ impl SoARay for RayN { fn set_flags(&mut self, i: usize, flags: u32) { self.flags[i] = flags; } } -pub struct HitN { +pub struct HitStream { ng_x: Vec, ng_y: Vec, ng_z: Vec, @@ -109,9 +109,9 @@ pub struct HitN { inst_id: Vec<::std::os::raw::c_uint>, } -impl HitN { - pub fn new(n: usize) -> HitN { - HitN { +impl HitStream { + pub fn new(n: usize) -> HitStream { + HitStream { ng_x: aligned_vector::(n, 16), ng_y: aligned_vector::(n, 16), ng_z: aligned_vector::(n, 16), @@ -126,8 +126,8 @@ impl HitN { 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 { + 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() } @@ -145,7 +145,7 @@ impl HitN { } } -impl SoAHit for HitN { +impl SoAHit for HitStream { 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]; @@ -167,20 +167,20 @@ impl SoAHit for HitN { fn set_inst_id(&mut self, i: usize, id: u32) { self.inst_id[i] = id; } } -pub struct RayHitN { - pub ray: RayN, - pub hit: HitN, +pub struct RayHitStream { + pub ray: RayStream, + pub hit: HitStream, } -impl RayHitN { - pub fn new(ray: RayN) -> RayHitN { +impl RayHitStream { + pub fn new(ray: RayStream) -> RayHitStream { let n = ray.len(); - RayHitN { + RayHitStream { ray, - hit: HitN::new(n), + hit: HitStream::new(n), } } - pub fn iter(&self) -> std::iter::Zip, SoAHitIter> { + pub fn iter(&self) -> std::iter::Zip, SoAHitIter> { self.ray.iter().zip(self.hit.iter()) } pub fn len(&self) -> usize { self.ray.len() } diff --git a/src/scene.rs b/src/scene.rs index ad2e43a3f..f935c0c8c 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,4 +1,7 @@ -use crate::{Bounds, Error, RayHit8, SceneFlags}; +use crate::{ + sys, Bounds, Error, Ray16, Ray8, RayHit16, RayHit8, RayHitPacket, RayHitStream, RayPacket, + SceneFlags, SoARay, +}; use std::{ collections::HashMap, mem, @@ -10,7 +13,7 @@ use crate::{ context::IntersectContext, device::Device, geometry::Geometry, - ray::{Ray, Ray4, RayHit, RayHit4, RayHitN, RayN}, + ray::{Ray, Ray4, RayHit, RayHit4, RayHitN, RayStream}, sys::*, }; @@ -324,11 +327,12 @@ impl 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. + /// * `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 IntersectContext, ray: Ray) -> RayHit { let mut ray_hit = RayHit::new(ray); @@ -342,6 +346,108 @@ impl Scene { ray_hit } + /// 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 IntersectContext, ray: &mut RayHit4, valid: &[i32; 4]) { + unsafe { + rtcIntersect4( + valid.as_ptr(), + self.handle, + ctx as *mut RTCIntersectContext, + ray as *mut RTCRayHit4, + ); + } + } + + /// 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 IntersectContext, ray: &mut RayHit8, valid: &[i32; 8]) { + unsafe { + rtcIntersect8( + valid.as_ptr(), + self.handle, + ctx as *mut RTCIntersectContext, + 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 IntersectContext, ray: &mut RayHit16, valid: &[i32; 16]) { + unsafe { + rtcIntersect16( + valid.as_ptr(), + self.handle, + ctx as *mut RTCIntersectContext, + ray as *mut RTCRayHit16, + ); + } + } + /// 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 @@ -349,11 +455,12 @@ impl 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. + /// * `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 IntersectContext, ray: &mut Ray) -> bool { @@ -367,90 +474,215 @@ impl Scene { ray.tfar == -f32::INFINITY } - /// Finds the closest hits for a ray packet of size 4 with the scene. + /// 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 to intersect with the scene. - /// * `valid` - A mask indicating which rays in the packet are valid. -1 means + /// * `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. /// - /// Only active rays are processed, and hit data of inactive rays is not changed. - pub fn intersect4(&self, ctx: &mut IntersectContext, ray: &mut RayHit4, valid: &[i32; 4]) { + /// 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 IntersectContext, ray: &mut Ray4, valid: &[i32; 4]) { unsafe { - rtcIntersect4( + rtcOccluded4( valid.as_ptr(), self.handle, ctx as *mut RTCIntersectContext, - ray as *mut RTCRayHit4, + ray as *mut RTCRay4, ); } } - /// Finds the closest hits for a ray packet of size 8 with the scene. + /// 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 to intersect with the scene. - /// * `valid` - A mask indicating which rays in the packet are valid. -1 means + /// * `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. /// - /// Only active rays are processed, and hit data of inactive rays is not changed. - pub fn intersect8(&self, ctx: &mut IntersectContext, ray: &mut RayHit8, valid: &[i32; 8]) { + /// 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 IntersectContext, ray: &mut Ray8, valid: &[i32; 8]) { unsafe { - rtcIntersect8( + rtcOccluded8( valid.as_ptr(), self.handle, ctx as *mut RTCIntersectContext, - ray as *mut RTCRayHit8, + ray as *mut RTCRay8, ); } } - pub fn occluded4(&self, ctx: &mut IntersectContext, ray: &mut Ray4, valid: &[i32; 4]) { + /// 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 IntersectContext, ray: &mut Ray16, valid: &[i32; 16]) { unsafe { - rtcOccluded4( + rtcOccluded16( valid.as_ptr(), self.handle, ctx as *mut RTCIntersectContext, - ray as *mut RTCRay4, + ray as *mut RTCRay16, ); } } - pub fn intersect_stream_aos(&self, ctx: &mut IntersectContext, rays: &mut Vec) { + /// 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 IntersectContext, + rays: &mut Vec

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

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

(), + ); + } } } - pub fn occluded_stream_aos(&self, ctx: &mut IntersectContext, rays: &mut Vec) { + /// 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 IntersectContext, rays: &mut Vec

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

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

(), + ); + } } } @@ -466,7 +698,7 @@ impl Scene { /// /// 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 IntersectContext, rays: &mut RayHitN) { + pub fn intersect_stream_soa(&self, ctx: &mut IntersectContext, rays: &mut RayHitStream) { let n = rays.len(); unsafe { let mut rayhit = rays.as_rayhitnp(); @@ -491,7 +723,7 @@ impl Scene { /// /// 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 IntersectContext, rays: &mut RayN) { + pub fn occluded_stream_soa(&self, ctx: &mut IntersectContext, rays: &mut RayStream) { let n = rays.len(); unsafe { let mut r = rays.as_raynp(); @@ -501,8 +733,6 @@ impl Scene { &mut r as *mut RTCRayNp, n as u32, ); - - let a: RTCRayN; } } @@ -524,3 +754,5 @@ impl Scene { bounds } } + +// TODO: implement rtcIntersect1Mp \ No newline at end of file From f6abfda9cd32527496dde537510e1200f113125f Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 23 Feb 2023 17:57:49 +0100 Subject: [PATCH 41/65] implement all callback functions --- examples/minimal/src/main.rs | 2 +- examples/triangle/src/main.rs | 6 +- src/bvh.rs | 115 +++++++++- src/callback.rs | 389 ---------------------------------- src/device.rs | 66 ++++-- src/geometry.rs | 348 ++++++++++++++++++++++++++++-- src/geometry/subdivision.rs | 152 +++++++++++-- src/geometry/user_geom.rs | 227 ++++++++++++++++---- src/lib.rs | 6 +- src/ray/packet.rs | 1 - src/scene.rs | 206 +++++++++++++++++- 11 files changed, 1031 insertions(+), 487 deletions(-) delete mode 100644 src/callback.rs diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 7f104ffae..1bbec386a 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -29,7 +29,7 @@ fn main() { let device = Device::new().unwrap(); device.set_error_function(|err, msg| { - println!("error {:?}: {}", err, msg); + println!("[embree] {:?}: {}", err, msg); }); let mut scene = device.create_scene().unwrap(); diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index 905913b3d..70c00a419 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -3,7 +3,9 @@ extern crate embree; extern crate support; -use embree::{BufferUsage, Device, Format, IntersectContext, RayHitN, RayStream, TriangleMesh}; +use embree::{ + BufferUsage, Device, Format, IntersectContext, RayHitStream, RayStream, TriangleMesh, +}; fn main() { let display = support::Display::new(512, 512, "triangle"); @@ -55,7 +57,7 @@ fn main() { ray.set_dir([x / dir_len, y / dir_len, -1.0 / dir_len]); } - let mut ray_hit = RayHitN::new(rays); + let mut ray_hit = RayHitStream::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); diff --git a/src/bvh.rs b/src/bvh.rs index b962c207f..b837b2258 100644 --- a/src/bvh.rs +++ b/src/bvh.rs @@ -1,12 +1,12 @@ -use crate::{sys, BuildFlags, BuildQuality, Device, Error}; +use crate::{sys::*, BuildFlags, BuildPrimitive, BuildQuality, Device, Error}; pub struct Bvh { - handle: sys::RTCBVH, + handle: RTCBVH, } impl Clone for Bvh { fn clone(&self) -> Self { - unsafe { sys::rtcRetainBVH(self.handle) } + unsafe { rtcRetainBVH(self.handle) } Self { handle: self.handle, } @@ -14,18 +14,121 @@ impl Clone for Bvh { } impl Drop for Bvh { - fn drop(&mut self) { unsafe { sys::rtcReleaseBVH(self.handle) } } + fn drop(&mut self) { unsafe { rtcReleaseBVH(self.handle) } } } impl Bvh { pub(crate) fn new(device: &Device) -> Result { - let handle = unsafe { sys::rtcNewBVH(device.handle) }; + let handle = unsafe { rtcNewBVH(device.handle) }; if handle.is_null() { Err(device.get_error()) } else { Ok(Self { handle }) } } +} + +pub struct BvhBuilderUserData { + create_node_fn: *mut std::os::raw::c_void, + 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, +} + +pub struct BvhBuilder<'a> { + bvh: &'a Bvh, + 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>, + ready: u32, +} + +impl<'a> BvhBuilder<'a> { + pub fn new(bvh: &'a Bvh) -> Self { + Self { + bvh, + 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, + 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: u32) -> Self { + self.traversal_cost = Some(traversal_cost); + self.ready |= 1 << 7; + self + } + + pub fn intersection_cost(mut self, intersection_cost: u32) -> 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 + } - // TODO: BVH build functions + // TODO: build } diff --git a/src/callback.rs b/src/callback.rs deleted file mode 100644 index 58a6ebcde..000000000 --- a/src/callback.rs +++ /dev/null @@ -1,389 +0,0 @@ -use crate::{ - geometry::GeometryData, Bounds, Geometry, HitN, IntersectContext, RayN, UserGeometryData, -}; -use std::{ - any::{Any, TypeId}, - os::raw::c_void, -}; - -use crate::sys::*; - -/// Helper function to convert a Rust closure to `RTCProgressMonitorFunction` -/// callback. -pub 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) - } - - Some(inner::) -} - -/// Helper function to convert a Rust closure to `RTCErrorFunction` callback. -pub 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()) - } - - Some(inner::) -} - -/// Helper function to convert a Rust closure to `RTCMemoryMonitorFunction` -/// callback. -pub 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) - } - - Some(inner::) -} - -// TODO: deal with RTCRayHitN, convert it to a SOA struct -/// Helper function to convert a Rust closure to `RTCIntersectFunctionN` -/// callback. -pub fn user_intersect_function_helper(_f: &mut F) -> RTCIntersectFunctionN -where - D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayHitN, u32), -{ - unsafe extern "C" fn inner(args: *const RTCIntersectFunctionNArguments) - where - D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayHitN, u32), - { - let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) - .user_fns - .as_ref() - .expect( - "User payloads not set! Make sure the geometry was created with kind \ - GeometryKind::USER", - ) - .intersect_fn as *mut F; - if !cb_ptr.is_null() { - let cb = &mut *cb_ptr; - let user_data = { - match (*((*args).geometryUserPtr as *mut GeometryData)).user_data { - Some(ref user_data) => { - if user_data.data.is_null() || user_data.data.type_id() != TypeId::of::() - { - None - } else { - Some(&mut *(user_data.data as *mut D)) - } - } - None => None, - } - }; - cb( - std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), - user_data, - (*args).geomID, - (*args).primID, - &mut *(*args).context, - &mut *(*args).rayhit, - (*args).N, - ); - } - } - - Some(inner::) -} - -// TODO: deal with RTCRayN -/// Helper function to convert a Rust closure to `RTCOccludedFunctionN` -/// callback. -pub fn user_occluded_function_helper(_f: &mut F) -> RTCOccludedFunctionN -where - D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayN, u32), -{ - unsafe extern "C" fn inner(args: *const RTCOccludedFunctionNArguments) - where - D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayN, u32), - { - let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) - .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 (*((*args).geometryUserPtr as *mut GeometryData)).user_data { - Some(ref user_data) => { - if user_data.data.is_null() || user_data.data.type_id() != TypeId::of::() - { - None - } else { - Some(&mut *(user_data.data as *mut D)) - } - } - None => None, - } - }; - cb( - std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), - user_data, - (*args).geomID, - (*args).primID, - &mut *(*args).context, - &mut *(*args).ray, - (*args).N, - ) - } - } - - Some(inner::) -} - -/// Helper function to convert a Rust closure to `RTCFilterFunctionN` callback -/// for intersect. -pub fn intersect_filter_function_helper(_f: &mut F) -> RTCFilterFunctionN -where - D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), -{ - unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) - where - D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), - { - let cb_ptr = - (*((*args).geometryUserPtr as *mut GeometryData)).intersect_filter_fn as *mut F; - if !cb_ptr.is_null() { - let cb = &mut *cb_ptr; - let user_data = { - match (*((*args).geometryUserPtr as *mut GeometryData)).user_data { - Some(ref user_data) => { - if user_data.data.is_null() || user_data.data.type_id() != TypeId::of::() - { - None - } else { - Some(&mut *(user_data.data as *mut D)) - } - } - None => None, - } - }; - cb( - std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), - user_data, - &mut *(*args).context, - RayN { - ptr: &mut *(*args).ray, - len: (*args).N as usize, - }, - HitN { - ptr: &mut *(*args).hit, - len: (*args).N as usize, - }, - ); - } - } - - Some(inner::) -} - -/// Helper function to convert a Rust closure to `RTCFilterFunctionN` callback -/// for occuluded. -pub fn occluded_filter_function_helper(_f: &mut F) -> RTCFilterFunctionN -where - D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), -{ - unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) - where - D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), - { - let len = (*args).N as usize; - let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)).occluded_filter_fn as *mut F; - if !cb_ptr.is_null() { - let cb = &mut *cb_ptr; - let user_data = { - match (*((*args).geometryUserPtr as *mut GeometryData)).user_data { - Some(ref user_data) => { - if user_data.data.is_null() || user_data.data.type_id() != TypeId::of::() - { - None - } else { - Some(&mut *(user_data.data as *mut D)) - } - } - None => None, - } - }; - cb( - std::slice::from_raw_parts_mut((*args).valid, len), - user_data, - &mut *(*args).context, - RayN { - ptr: &mut *(*args).ray, - len, - }, - HitN { - ptr: &mut *(*args).hit, - len, - }, - ); - } - } - - Some(inner::) -} - -/// Helper function to convert a Rust closure to `RTCBoundsFunction` callback. -pub fn user_bounds_function_helper(_f: &mut F) -> RTCBoundsFunction -where - D: UserGeometryData, - F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), -{ - unsafe extern "C" fn inner(args: *const RTCBoundsFunctionArguments) - where - D: UserGeometryData, - F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), - { - let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) - .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 (*((*args).geometryUserPtr as *mut GeometryData)).user_data { - Some(ref user_data) => { - if user_data.data.is_null() || user_data.data.type_id() != TypeId::of::() - { - None - } else { - Some(&mut *(user_data.data as *mut D)) - } - } - None => None, - } - }; - cb( - user_data, - (*args).primID, - (*args).timeStep, - &mut *(*args).bounds_o, - ); - } - } - - Some(inner::) -} - -/// Helper function to convert a Rust closure to `RTCDisplacementFunctionN` -/// callback. -pub fn subdivision_displacement_function_helper(_f: &mut F) -> RTCDisplacementFunctionN -where - D: UserGeometryData, - F: FnMut( - Option<&mut D>, - RTCGeometry, - u32, - u32, - &[f32], - &[f32], - &[f32], - &[f32], - &[f32], - &mut [f32], - &mut [f32], - &mut [f32], - ), -{ - unsafe extern "C" fn inner(args: *const RTCDisplacementFunctionNArguments) - where - D: UserGeometryData, - F: FnMut( - Option<&mut D>, - RTCGeometry, - u32, - u32, - &[f32], - &[f32], - &[f32], - &[f32], - &[f32], - &mut [f32], - &mut [f32], - &mut [f32], - ), - { - let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) - .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 (*((*args).geometryUserPtr as *mut GeometryData)).user_data { - Some(ref user_data) => { - if user_data.data.is_null() || user_data.data.type_id() != TypeId::of::() - { - None - } else { - Some(&mut *(user_data.data as *mut D)) - } - } - None => None, - } - }; - cb( - user_data, - (*args).geometry, - (*args).primID, - (*args).timeStep, - std::slice::from_raw_parts((*args).u, (*args).N as usize), - std::slice::from_raw_parts((*args).v, (*args).N as usize), - std::slice::from_raw_parts((*args).Ng_x, (*args).N as usize * 3), - std::slice::from_raw_parts((*args).Ng_y, (*args).N as usize * 3), - std::slice::from_raw_parts((*args).Ng_z, (*args).N as usize * 3), - std::slice::from_raw_parts_mut((*args).P_x, (*args).N as usize * 3), - std::slice::from_raw_parts_mut((*args).P_y, (*args).N as usize * 3), - std::slice::from_raw_parts_mut((*args).P_z, (*args).N as usize * 3), - ); - } - } - - Some(inner::) -} - -// TODO: point query function helper diff --git a/src/device.rs b/src/device.rs index 8ba90263c..5079128ee 100644 --- a/src/device.rs +++ b/src/device.rs @@ -66,9 +66,9 @@ impl Device { /// ```no_run /// use embree::Device; /// let device = Device::new().unwrap(); - /// device.set_error_function(|error, msg| { + /// device.set_error_function(Some(|error, msg| { /// println!("Error: {:?} {}", error, msg); - /// }); + /// })); /// ``` pub fn set_error_function(&self, error_fn: F) where @@ -78,7 +78,7 @@ impl Device { unsafe { rtcSetDeviceErrorFunction( self.handle, - crate::callback::error_function_helper(&mut closure), + error_function(&mut closure), &mut closure as *mut _ as *mut ::std::os::raw::c_void, ); } @@ -104,12 +104,14 @@ impl Device { /// 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. + /// + /// * `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 @@ -129,14 +131,14 @@ impl Device { /// ```no_run /// use embree::Device; /// let device = Device::new().unwrap(); - /// device.set_memory_monitor_function(|bytes, post| { + /// device.set_memory_monitor_function(Some(|bytes, post| { /// if bytes > 0 { /// println!("allocated {} bytes", bytes); /// } else { /// println!("deallocated {} bytes", -bytes); /// }; /// true - /// }); + /// })); /// ``` pub fn set_memory_monitor_function(&self, monitor_fn: F) where @@ -146,7 +148,7 @@ impl Device { unsafe { rtcSetDeviceMemoryMonitorFunction( self.handle, - crate::callback::memory_monitor_function_helper(&mut closure), + memory_monitor_function(&mut closure), &mut closure as *mut _ as *mut ::std::os::raw::c_void, ); } @@ -214,7 +216,7 @@ impl Device { /// Creates a [`Geometry`] object bound to the device without any /// buffers attached. - pub fn create_geometry<'a, 'b>(&'a self, kind: GeometryKind) -> Result, Error> { + pub fn create_geometry<'a>(&self, kind: GeometryKind) -> Result, Error> { Geometry::new(self, kind) } } @@ -376,10 +378,12 @@ pub fn enable_ftz_and_daz() { } } +/// Default error function. fn default_error_function(error: Error, msg: &str) { - eprintln!("Embree error {:?} - {}", error, msg); + 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()); @@ -392,3 +396,39 @@ fn create_device(config: Option) -> Result { 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/geometry.rs b/src/geometry.rs index 69fb039bc..c9e14a4c1 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -2,9 +2,13 @@ use std::{ any::TypeId, collections::HashMap, marker::PhantomData, num::NonZeroUsize, ptr, sync::Mutex, }; -use crate::{sys::*, BufferSlice, BufferUsage, BuildQuality, Device, Error, Format, GeometryKind, HitN, HitPacket, IntersectContext, RayN, RayPacket, Scene}; +use crate::{ + aligned_vector, sys::*, BufferSlice, BufferUsage, BuildQuality, Device, Error, Format, + GeometryKind, HitN, IntersectContext, RayN, +}; use std::{ + borrow::Cow, ops::{Deref, DerefMut}, rc::Rc, }; @@ -39,7 +43,7 @@ impl UserGeometryData for T where T: Sized + Send + Sync + 'static {} /// 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 UserData { +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. @@ -91,7 +95,7 @@ impl Default for SubdivisionGeometryPayloads { #[derive(Debug, Clone)] pub(crate) struct GeometryData { /// User-defined data. - pub user_data: Option, + 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. @@ -601,7 +605,8 @@ impl<'buf> Geometry<'buf> { /// /// Only a single callback function can be registered per geometry, and /// further invocations overwrite the previously set callback function. - /// Passing `None` as the filter function removes the filter 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 @@ -670,16 +675,24 @@ impl<'buf> Geometry<'buf> { state.data.intersect_filter_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; rtcSetGeometryIntersectFilterFunction( self.handle, - crate::callback::intersect_filter_function_helper(&mut closure), + 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. - /// Passing `None` as the filter function removes the filter 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 @@ -702,17 +715,24 @@ impl<'buf> Geometry<'buf> { state.data.occluded_filter_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; rtcSetGeometryOccludedFilterFunction( self.handle, - crate::callback::occluded_filter_function_helper(&mut closure), + 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); + } + } + /// 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. - /// Passing `None` as function pointer disables the registered 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 @@ -778,8 +798,15 @@ impl<'buf> Geometry<'buf> { /// 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_func: RTCPointQueryFunction) { - rtcSetGeometryPointQueryFunction(self.handle, query_func); + 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. @@ -886,7 +913,7 @@ impl<'buf> Geometry<'buf> { D: UserGeometryData, { let mut state = self.state.lock().unwrap(); - state.data.user_data = Some(UserData { + state.data.user_data = Some(GeometryUserData { data: user_data as *mut D as *mut std::os::raw::c_void, type_id: TypeId::of::(), }); @@ -907,7 +934,7 @@ impl<'buf> Geometry<'buf> { } else { match (*ptr).user_data.as_mut() { None => None, - Some(user_data @ UserData { .. }) => { + Some(user_data @ GeometryUserData { .. }) => { if user_data.type_id == TypeId::of::() { Some(&mut *(user_data.data as *mut D)) } else { @@ -969,6 +996,208 @@ impl<'buf> Geometry<'buf> { 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 + .as_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + dPdu: output + .dp_du + .as_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + dPdv: output + .dp_dv + .as_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + ddPdudu: output + .ddp_du_du + .as_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + ddPdvdv: output + .ddp_dv_dv + .as_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + ddPdudv: output + .ddp_du_dv + .as_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + valueCount: output.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 + .as_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + dPdu: output + .dp_du + .as_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + dPdv: output + .dp_dv + .as_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + ddPdudu: output + .ddp_du_du + .as_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + ddPdvdv: output + .ddp_dv_dv + .as_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + ddPdudv: output + .ddp_du_dv + .as_mut() + .map(|p| p.as_mut_ptr()) + .unwrap_or(ptr::null_mut()), + valueCount: output.count, + }; + unsafe { + rtcInterpolateN(&args as _); + } + } +} + +/// 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 { + pub count: u32, + pub p: Option>, + pub dp_du: Option>, + pub dp_dv: Option>, + pub ddp_du_du: Option>, + pub ddp_dv_dv: Option>, + pub ddp_du_dv: Option>, +} + +impl InterpolateOutput { + pub fn new( + count: u32, + origin_value: bool, + first_order_derivative: bool, + second_order_derivative: bool, + ) -> Self { + Self { + count, + p: if origin_value { + Some(aligned_vector::(count as usize, 16)) + } else { + None + }, + dp_du: if first_order_derivative { + Some(aligned_vector::(count as usize, 16)) + } else { + None + }, + dp_dv: if first_order_derivative { + Some(aligned_vector::(count as usize, 16)) + } else { + None + }, + ddp_du_du: if second_order_derivative { + Some(aligned_vector::(count as usize, 16)) + } else { + None + }, + ddp_dv_dv: if second_order_derivative { + Some(aligned_vector::(count as usize, 16)) + } else { + None + }, + ddp_du_dv: if second_order_derivative { + Some(aligned_vector::(count as usize, 16)) + } else { + None + }, + } + } } // TODO(yang): rtcInterpolate, rtcInterpolateN @@ -1064,3 +1293,96 @@ impl_geometry_type!(QuadMesh, GeometryKind::QUAD, /// p0 ------> p1 /// u ); + +/// Helper function to convert a Rust closure to `RTCFilterFunctionN` callback +/// for intersect. +fn intersect_filter_function(_f: &mut F) -> RTCFilterFunctionN +where + D: UserGeometryData, + F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), +{ + unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) + where + D: UserGeometryData, + F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), + { + let cb_ptr = + (*((*args).geometryUserPtr as *mut GeometryData)).intersect_filter_fn as *mut F; + if !cb_ptr.is_null() { + let cb = &mut *cb_ptr; + let user_data = { + match (*((*args).geometryUserPtr as *mut GeometryData)).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( + std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), + user_data, + &mut *(*args).context, + RayN { + ptr: &mut *(*args).ray, + len: (*args).N as usize, + }, + HitN { + ptr: &mut *(*args).hit, + len: (*args).N as usize, + }, + ); + } + } + 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, + F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), +{ + unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) + where + D: UserGeometryData, + F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), + { + let len = (*args).N as usize; + let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)).occluded_filter_fn as *mut F; + if !cb_ptr.is_null() { + let cb = &mut *cb_ptr; + let user_data = { + match (*((*args).geometryUserPtr as *mut GeometryData)).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( + std::slice::from_raw_parts_mut((*args).valid, len), + user_data, + &mut *(*args).context, + RayN { + ptr: &mut *(*args).ray, + len, + }, + HitN { + ptr: &mut *(*args).hit, + len, + }, + ); + } + } + + Some(inner::) +} diff --git a/src/geometry/subdivision.rs b/src/geometry/subdivision.rs index e9af62f16..6d2feb83d 100644 --- a/src/geometry/subdivision.rs +++ b/src/geometry/subdivision.rs @@ -1,8 +1,12 @@ use crate::{ - sys, sys::RTCDisplacementFunctionN, Device, Error, Geometry, GeometryKind, SubdivisionMode, + geometry::GeometryData, sys, Device, Error, Geometry, GeometryKind, SubdivisionMode, UserGeometryData, }; -use std::ops::{Deref, DerefMut}; +use std::{ + any::TypeId, + ops::{Deref, DerefMut}, +}; +use sys::*; #[derive(Debug)] pub struct SubdivisionGeometry(Geometry<'static>); @@ -47,7 +51,7 @@ impl SubdivisionGeometry { /// to their location during subdivision. This way all patches are linearly /// interpolated. pub fn set_subdivision_mode(&self, topology_id: u32, mode: SubdivisionMode) { - unsafe { sys::rtcSetGeometrySubdivisionMode(self.handle, topology_id, mode) } + unsafe { rtcSetGeometrySubdivisionMode(self.handle, topology_id, mode) } } /// Sets the number of topologies of a subdivision geometry. @@ -60,7 +64,7 @@ impl SubdivisionGeometry { /// [`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) { - unsafe { sys::rtcSetGeometryTopologyCount(self.handle, count) } + unsafe { rtcSetGeometryTopologyCount(self.handle, count) } } /// Returns the first half edge of a face. @@ -69,7 +73,7 @@ impl SubdivisionGeometry { /// 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 { - unsafe { sys::rtcGetGeometryFirstHalfEdge(self.handle, face_id) } + unsafe { rtcGetGeometryFirstHalfEdge(self.handle, face_id) } } /// Returns the face of some half edge. @@ -78,7 +82,7 @@ impl SubdivisionGeometry { /// 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 { - unsafe { sys::rtcGetGeometryFace(self.handle, half_edge_id) } + unsafe { rtcGetGeometryFace(self.handle, half_edge_id) } } /// Returns the next half edge of some half edge. @@ -87,23 +91,58 @@ impl SubdivisionGeometry { /// 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 { - unsafe { sys::rtcGetGeometryNextHalfEdge(self.handle, half_edge_id) } + unsafe { rtcGetGeometryNextHalfEdge(self.handle, half_edge_id) } } /// Returns the previous half edge of some half edge. pub fn get_previous_half_edge(&self, half_edge_id: u32) -> u32 { - unsafe { sys::rtcGetGeometryPreviousHalfEdge(self.handle, half_edge_id) } + unsafe { rtcGetGeometryPreviousHalfEdge(self.handle, half_edge_id) } } /// Returns the opposite half edge of some half edge. pub fn get_opposite_half_edge(&self, topology_id: u32, edge_id: u32) -> u32 { - unsafe { sys::rtcGetGeometryOppositeHalfEdge(self.handle, topology_id, edge_id) } + unsafe { rtcGetGeometryOppositeHalfEdge(self.handle, topology_id, edge_id) } } - // TODO(yang): Add documentation. - // TODO(yang): Better way to deal with RTCGeometry, maybe we need a lookup table to get the geometry from the handle. + // TODO(yang): Better way to deal with RTCGeometry, maybe we need a lookup table + // to get the geometry from the handle. /// Sets the displacement function for a subdivision geometry. - pub unsafe fn set_displacement_function(&self, displacement: F) + /// + /// Only one displacement function can be set per geometry, further calls to + /// this will overwrite the previous displacement function. + /// Passing `None` will 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. + /// + /// The displacement function is called for each vertex of the subdivision + /// geometry. The function is called with the following parameters: + /// + /// * `geometry_user_data`: The user data pointer that was specified when + /// the geometry was created. + /// * `geometry`: The geometry handle. + /// * `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. + /// * `us`: The u coordinates of points to displace. + /// * `vs`: The v coordinates of points to displace. + /// * `ng_xs`: The x components of normal of vertices to displace + /// (normalized). + /// * `ng_ys`: The y component of normal of vertices to displace + /// (normalized). + /// * `ng_ys`: The z component of normal of vertices to displace + /// (normalized). + /// * `pxs`: The x components of points to displace. + /// * `pys`: The y components of points to displace. + /// * `pzs`: The z components of points to displace. + /// + /// # 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: FnMut( @@ -127,8 +166,95 @@ impl SubdivisionGeometry { state.data.intersect_filter_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; sys::rtcSetGeometryDisplacementFunction( self.handle, - crate::callback::subdivision_displacement_function_helper(&mut closure), + displacement_function(&mut closure), ) } } + + /// Removes the displacement function for a subdivision geometry. + pub fn unset_displacement_function(&mut self) { + unsafe { + sys::rtcSetGeometryDisplacementFunction(self.handle, None); + } + } +} + +/// Helper function to convert a Rust closure to `RTCDisplacementFunctionN` +/// callback. +fn displacement_function(_f: &mut F) -> RTCDisplacementFunctionN +where + D: UserGeometryData, + F: FnMut( + Option<&mut D>, + RTCGeometry, + u32, + u32, + &[f32], + &[f32], + &[f32], + &[f32], + &[f32], + &mut [f32], + &mut [f32], + &mut [f32], + ), +{ + unsafe extern "C" fn inner(args: *const RTCDisplacementFunctionNArguments) + where + D: UserGeometryData, + F: FnMut( + Option<&mut D>, + RTCGeometry, + u32, + u32, + &[f32], + &[f32], + &[f32], + &[f32], + &[f32], + &mut [f32], + &mut [f32], + &mut [f32], + ), + { + let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) + .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 (*((*args).geometryUserPtr as *mut GeometryData)).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( + user_data, + (*args).geometry, + (*args).primID, + (*args).timeStep, + std::slice::from_raw_parts((*args).u, (*args).N as usize), + std::slice::from_raw_parts((*args).v, (*args).N as usize), + std::slice::from_raw_parts((*args).Ng_x, (*args).N as usize * 3), + std::slice::from_raw_parts((*args).Ng_y, (*args).N as usize * 3), + std::slice::from_raw_parts((*args).Ng_z, (*args).N as usize * 3), + std::slice::from_raw_parts_mut((*args).P_x, (*args).N as usize * 3), + std::slice::from_raw_parts_mut((*args).P_y, (*args).N as usize * 3), + std::slice::from_raw_parts_mut((*args).P_z, (*args).N as usize * 3), + ); + } + } + + Some(inner::) } diff --git a/src/geometry/user_geom.rs b/src/geometry/user_geom.rs index d0bca91cf..71d11380f 100644 --- a/src/geometry/user_geom.rs +++ b/src/geometry/user_geom.rs @@ -1,9 +1,9 @@ use crate::{ - sys, - sys::{RTCRayHitN, RTCRayN}, - Bounds, Device, Error, Geometry, GeometryKind, IntersectContext, UserGeometryData, + geometry::GeometryData, sys::*, Bounds, Device, Error, Geometry, GeometryKind, + IntersectContext, RayHitN, RayN, UserGeometryData, }; use std::{ + any::TypeId, ops::{Deref, DerefMut}, ptr, }; @@ -30,8 +30,9 @@ impl UserGeometry { /// /// Only a single callback function can be registered per geometry, and /// further invocations overwrite the previously set callback function. - /// Passing `None` as function pointer disables the registered callback - /// function. + /// + /// Unregister the callback function by calling + /// [`UserGeometry::unset_bounds_function`]. /// /// The registered bounding box callback function is invoked to calculate /// axis- aligned bounding boxes of the primitives of the user-defined @@ -59,29 +60,33 @@ impl UserGeometry { D: UserGeometryData, F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), { - match self.kind { - GeometryKind::USER => unsafe { - let mut state = self.state.lock().unwrap(); - let mut closure = bounds; - state.data.user_fns.as_mut().unwrap().bounds_fn = - &mut closure as *mut _ as *mut std::os::raw::c_void; - sys::rtcSetGeometryBoundsFunction( - self.handle, - crate::callback::user_bounds_function_helper(&mut closure), - ptr::null_mut(), - ); - }, - _ => panic!("Only user-defined geometries can have a bounds function"), + unsafe { + let mut state = self.state.lock().unwrap(); + let mut closure = bounds; + state.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(), + ); + } + } + + /// Unsets the callback to calculate the bounding box of user-defined + /// geometry. + pub fn unset_bounds_function(&mut self) { + unsafe { + rtcSetGeometryBoundsFunction(self.handle, None, ptr::null_mut()); } } - // TODO(yang): deal with RTCRayHitN, then we can make this function safe /// Sets the callback function to intersect a user geometry. /// /// Only a single callback function can be registered per geometry and /// further invocations overwrite the previously set callback function. - /// Passing `None` as function pointer disables the registered callback - /// function. + /// Unregister the callback function by calling + /// [`UserGeometry::unset_intersect_function`]. /// /// The registered callback function is invoked by intersect-type ray /// queries to calculate the intersection of a ray packet of variable @@ -136,22 +141,22 @@ impl UserGeometry { pub fn set_intersect_function(&mut self, intersect: F) where D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayHitN, u32), + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayHitN), { - // TODO: deal with RTCRayHitN let mut state = self.state.lock().unwrap(); let mut closure = intersect; state.data.user_fns.as_mut().unwrap().intersect_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; + unsafe { rtcSetGeometryIntersectFunction(self.handle, intersect_function(&mut closure)) }; + } + + /// Unsets the callback to intersect user-defined geometry. + pub fn unset_intersect_function(&mut self) { unsafe { - sys::rtcSetGeometryIntersectFunction( - self.handle, - crate::callback::user_intersect_function_helper(&mut closure), - ) - }; + rtcSetGeometryIntersectFunction(self.handle, None); + } } - // TODO(yang): deal with RTCRayN, then we can make this function safe /// Sets the callback function to occlude a user geometry. /// /// Similar to [`Geometry::set_intersect_function`], but for occlusion @@ -159,26 +164,174 @@ impl UserGeometry { pub fn set_occluded_function(&mut self, occluded: F) where D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, &mut RTCRayN, u32), + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayN), { - // TODO: deal with RTCRayN let mut state = self.state.lock().unwrap(); let mut closure = occluded; state.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)) }; + } + + /// Unsets the callback to occlude user-defined geometry. + pub fn unset_occluded_function(&mut self) { unsafe { - sys::rtcSetGeometryOccludedFunction( - self.handle, - crate::callback::user_occluded_function_helper(&mut closure), - ) - }; + rtcSetGeometryOccludedFunction(self.handle, None); + } } /// Sets the number of primitives of a user-defined geometry. pub fn set_primitive_count(&mut self, count: u32) { // Update the primitive count. unsafe { - sys::rtcSetGeometryUserPrimitiveCount(self.handle, count); + rtcSetGeometryUserPrimitiveCount(self.handle, count); + } + } +} + +/// Helper function to convert a Rust closure to `RTCBoundsFunction` callback. +fn bounds_function(_f: &mut F) -> RTCBoundsFunction +where + D: UserGeometryData, + F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), +{ + unsafe extern "C" fn inner(args: *const RTCBoundsFunctionArguments) + where + D: UserGeometryData, + F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), + { + let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) + .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 (*((*args).geometryUserPtr as *mut GeometryData)).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( + user_data, + (*args).primID, + (*args).timeStep, + &mut *(*args).bounds_o, + ); + } + } + + Some(inner::) +} + +/// Helper function to convert a Rust closure to `RTCIntersectFunctionN` +/// callback. +fn intersect_function(_f: &mut F) -> RTCIntersectFunctionN +where + D: UserGeometryData, + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayHitN), +{ + unsafe extern "C" fn inner(args: *const RTCIntersectFunctionNArguments) + where + D: UserGeometryData, + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayHitN), + { + let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) + .user_fns + .as_ref() + .expect( + "User payloads not set! Make sure the geometry was created with kind \ + GeometryKind::USER", + ) + .intersect_fn as *mut F; + if !cb_ptr.is_null() { + let cb = &mut *cb_ptr; + let user_data = { + match (*((*args).geometryUserPtr as *mut GeometryData)).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( + std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), + user_data, + (*args).geomID, + (*args).primID, + &mut *(*args).context, + RayHitN { + ptr: (*args).rayhit, + len: (*args).N as usize, + }, + ); } } + + Some(inner::) +} + +/// Helper function to convert a Rust closure to `RTCOccludedFunctionN` +/// callback. +fn occluded_function(_f: &mut F) -> RTCOccludedFunctionN +where + D: UserGeometryData, + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayN), +{ + unsafe extern "C" fn inner(args: *const RTCOccludedFunctionNArguments) + where + D: UserGeometryData, + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayN), + { + let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) + .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 (*((*args).geometryUserPtr as *mut GeometryData)).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( + std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), + user_data, + (*args).geomID, + (*args).primID, + &mut *(*args).context, + RayN { + ptr: (*args).ray, + len: (*args).N as usize, + }, + ) + } + } + + Some(inner::) } diff --git a/src/lib.rs b/src/lib.rs index a1ea37f01..42bedb00c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,11 +12,10 @@ //! See the [examples/](https://github.com/Twinklebear/embree-rs/tree/master/examples) //! for some example applications using the bindings. -use std::{alloc, mem, mem::MaybeUninit}; +use std::{alloc, mem}; mod buffer; mod bvh; -mod callback; mod context; mod device; mod error; @@ -232,6 +231,9 @@ impl Bounds { /// 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 vectors pub fn aligned_vector(len: usize, align: usize) -> Vec { let t_size = mem::size_of::(); diff --git a/src/ray/packet.rs b/src/ray/packet.rs index 1d14d17ef..1ce9c066a 100644 --- a/src/ray/packet.rs +++ b/src/ray/packet.rs @@ -1,7 +1,6 @@ use crate::{ sys, Hit, Ray, RayHit, SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut, }; -use std::{marker::PhantomData, ops::Add}; mod sealed { pub trait Sealed {} diff --git a/src/scene.rs b/src/scene.rs index f935c0c8c..b1760c5bf 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,19 +1,19 @@ use crate::{ - sys, Bounds, Error, Ray16, Ray8, RayHit16, RayHit8, RayHitPacket, RayHitStream, RayPacket, - SceneFlags, SoARay, + Bounds, Error, PointQuery, PointQueryContext, Ray16, Ray8, RayHit16, RayHit8, RayHitPacket, + RayHitStream, RayPacket, SceneFlags, }; use std::{ + any::TypeId, collections::HashMap, - mem, + mem, ptr, sync::{Arc, Mutex}, }; use crate::{ - callback, context::IntersectContext, device::Device, geometry::Geometry, - ray::{Ray, Ray4, RayHit, RayHit4, RayHitN, RayStream}, + ray::{Ray, Ray4, RayHit, RayHit4, RayStream}, sys::*, }; @@ -23,6 +23,7 @@ pub struct Scene { pub(crate) handle: RTCScene, pub(crate) device: Device, geometries: Arc>>>, + point_query_user_data: Arc>, } impl Clone for Scene { @@ -32,6 +33,7 @@ impl Clone for Scene { handle: self.handle, device: self.device.clone(), geometries: self.geometries.clone(), + point_query_user_data: self.point_query_user_data.clone(), } } } @@ -55,6 +57,7 @@ impl Scene { handle, device, geometries: Default::default(), + point_query_user_data: Arc::new(Mutex::new(PointQueryUserData::default())), }) } } @@ -231,6 +234,111 @@ impl Scene { /// ``` pub fn get_flags(&self) -> RTCSceneFlags { 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 (see [`GeometryKind::POINT`]), curves (see + /// [`GeometryKind::CURVE`]) and subdivision surfaces (see + /// [`GeometryKind::SUBDIVISION]). + /// + /// 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. /// @@ -276,23 +384,22 @@ impl Scene { /// # 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, - 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()); } @@ -755,4 +862,83 @@ impl Scene { } } -// TODO: implement rtcIntersect1Mp \ No newline at end of file +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 From 024993a916f0808eb75b250277d83923e16ecb9b Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 23 Feb 2023 18:01:46 +0100 Subject: [PATCH 42/65] remove portable simd --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 42bedb00c..ee4fd15cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(portable_simd)] //! [![Crates.io](https://img.shields.io/crates/v/embree.svg)](https://crates.io/crates/embree) //! [![Build Status](https://travis-ci.org/Twinklebear/embree-rs.svg?branch=master)](https://travis-ci.org/Twinklebear/embree-rs) //! From 2b36bc1f59f03f158de0122ff882de791b7f3f16 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 23 Feb 2023 18:31:28 +0100 Subject: [PATCH 43/65] remove user geometry --- examples/triangle/src/main.rs | 3 +- src/device.rs | 8 +- src/geometry.rs | 345 +++++++++++++++++++++++++++++++++- src/geometry/subdivision.rs | 28 ++- src/geometry/user_geom.rs | 337 --------------------------------- src/lib.rs | 4 +- 6 files changed, 360 insertions(+), 365 deletions(-) delete mode 100644 src/geometry/user_geom.rs diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index 70c00a419..04278a141 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -4,7 +4,8 @@ extern crate embree; extern crate support; use embree::{ - BufferUsage, Device, Format, IntersectContext, RayHitStream, RayStream, TriangleMesh, + BufferUsage, Device, Format, GeometryKind, IntersectContext, RayHitStream, RayStream, + SubdivisionGeometry, TriangleMesh, }; fn main() { diff --git a/src/device.rs b/src/device.rs index 5079128ee..4f59f6ca9 100644 --- a/src/device.rs +++ b/src/device.rs @@ -66,9 +66,9 @@ impl Device { /// ```no_run /// use embree::Device; /// let device = Device::new().unwrap(); - /// device.set_error_function(Some(|error, msg| { + /// device.set_error_function(|error, msg| { /// println!("Error: {:?} {}", error, msg); - /// })); + /// }); /// ``` pub fn set_error_function(&self, error_fn: F) where @@ -131,14 +131,14 @@ impl Device { /// ```no_run /// use embree::Device; /// let device = Device::new().unwrap(); - /// device.set_memory_monitor_function(Some(|bytes, post| { + /// device.set_memory_monitor_function(|bytes, post| { /// if bytes > 0 { /// println!("allocated {} bytes", bytes); /// } else { /// println!("deallocated {} bytes", -bytes); /// }; /// true - /// })); + /// }); /// ``` pub fn set_memory_monitor_function(&self, monitor_fn: F) where diff --git a/src/geometry.rs b/src/geometry.rs index c9e14a4c1..0e9b58f1b 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -3,8 +3,8 @@ use std::{ }; use crate::{ - aligned_vector, sys::*, BufferSlice, BufferUsage, BuildQuality, Device, Error, Format, - GeometryKind, HitN, IntersectContext, RayN, + aligned_vector, sys::*, Bounds, BufferSlice, BufferUsage, BuildQuality, Device, Error, Format, + GeometryKind, HitN, IntersectContext, RayHitN, RayN, }; use std::{ @@ -15,11 +15,9 @@ use std::{ mod instance; mod subdivision; -mod user_geom; pub use instance::*; pub use subdivision::*; -pub use user_geom::*; // TODO(yang): maybe enforce format and stride when get the view? /// Information about how a (part of) buffer is bound to a geometry. @@ -1123,6 +1121,194 @@ impl<'buf> Geometry<'buf> { 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(Option<&mut D>, u32, u32, &mut Bounds), + { + match self.kind { + GeometryKind::USER => unsafe { + let mut state = self.state.lock().unwrap(); + let mut closure = bounds; + state.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. + /// + /// 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`]. + /// + /// 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. The callback function of type + /// [`RTCIntersectFunctionN`] gets passed a number of arguments through + /// the [`RTCIntersectFunctionNArguments`] structure. The value N + /// specifies the ray packet size, valid points to an array of + /// integers that specify whether the corresponding ray is valid (-1) or + /// invalid (0), the `geometryUserPtr` member points to the geometry + /// user data previously set through [`Geometry::set_user_data`], the + /// context member points to the intersection context passed to the ray + /// query, the rayhit member points to a ray and hit packet of variable + /// size N, and the geomID and primID member identifies the geometry ID + /// and primitive ID of the primitive to intersect. The ray component of + /// the rayhit structure contains valid data, in particular + /// the tfar value is the current closest hit distance found. All data + /// inside the hit component of the rayhit structure are undefined and + /// should not be read by the function. + /// 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 was found in + /// the valid range (from tnear to tfar), it should update the hit distance + /// of the ray (tfar member) and the hit (u, v, Ng, 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. + /// + /// 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 rtcFilterIntersection call. Within the user geometry intersect + /// function, it is safe to trace new rays and create new scenes and + /// geometries. When performing ray queries using rtcIntersect1, it is + /// guaranteed that the packet size is 1 when the callback is invoked. + /// When performing ray queries using the rtcIntersect4/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 rtcGetDevice- Property. When performing ray queries + /// using the stream API such as rtcIntersect1M, rtcIntersect1Mp, + /// rtcIntersectNM, or rtcIntersectNp the or- der 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, + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayHitN), + { + match self.kind { + GeometryKind::USER => unsafe { + let mut state = self.state.lock().unwrap(); + let mut closure = intersect; + state.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. + pub fn set_occluded_function(&mut self, occluded: F) + where + D: UserGeometryData, + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayN), + { + match self.kind { + GeometryKind::USER => { + let mut state = self.state.lock().unwrap(); + let mut closure = occluded; + state.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!"), + } + } } /// The arguments for the `Geometry::interpolate` function. @@ -1294,6 +1480,10 @@ impl_geometry_type!(QuadMesh, GeometryKind::QUAD, /// u ); +impl_geometry_type!(UserGeometry, GeometryKind::USER, + /// A user geometry. +); + /// Helper function to convert a Rust closure to `RTCFilterFunctionN` callback /// for intersect. fn intersect_filter_function(_f: &mut F) -> RTCFilterFunctionN @@ -1386,3 +1576,150 @@ where Some(inner::) } + +/// Helper function to convert a Rust closure to `RTCBoundsFunction` callback. +fn bounds_function(_f: &mut F) -> RTCBoundsFunction +where + D: UserGeometryData, + F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), +{ + unsafe extern "C" fn inner(args: *const RTCBoundsFunctionArguments) + where + D: UserGeometryData, + F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), + { + let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) + .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 (*((*args).geometryUserPtr as *mut GeometryData)).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( + user_data, + (*args).primID, + (*args).timeStep, + &mut *(*args).bounds_o, + ); + } + } + + Some(inner::) +} + +/// Helper function to convert a Rust closure to `RTCIntersectFunctionN` +/// callback. +fn intersect_function(_f: &mut F) -> RTCIntersectFunctionN +where + D: UserGeometryData, + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayHitN), +{ + unsafe extern "C" fn inner(args: *const RTCIntersectFunctionNArguments) + where + D: UserGeometryData, + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayHitN), + { + let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) + .user_fns + .as_ref() + .expect( + "User payloads not set! Make sure the geometry was created with kind \ + GeometryKind::USER", + ) + .intersect_fn as *mut F; + if !cb_ptr.is_null() { + let cb = &mut *cb_ptr; + let user_data = { + match (*((*args).geometryUserPtr as *mut GeometryData)).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( + std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), + user_data, + (*args).geomID, + (*args).primID, + &mut *(*args).context, + RayHitN { + ptr: (*args).rayhit, + len: (*args).N as usize, + }, + ); + } + } + + Some(inner::) +} + +/// Helper function to convert a Rust closure to `RTCOccludedFunctionN` +/// callback. +fn occluded_function(_f: &mut F) -> RTCOccludedFunctionN +where + D: UserGeometryData, + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayN), +{ + unsafe extern "C" fn inner(args: *const RTCOccludedFunctionNArguments) + where + D: UserGeometryData, + F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayN), + { + let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) + .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 (*((*args).geometryUserPtr as *mut GeometryData)).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( + std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), + user_data, + (*args).geomID, + (*args).primID, + &mut *(*args).context, + RayN { + ptr: (*args).ray, + len: (*args).N as usize, + }, + ) + } + } + + Some(inner::) +} diff --git a/src/geometry/subdivision.rs b/src/geometry/subdivision.rs index 6d2feb83d..7b86c2ddc 100644 --- a/src/geometry/subdivision.rs +++ b/src/geometry/subdivision.rs @@ -147,7 +147,6 @@ impl SubdivisionGeometry { D: UserGeometryData, F: FnMut( Option<&mut D>, - sys::RTCGeometry, u32, u32, &[f32], @@ -164,17 +163,14 @@ impl SubdivisionGeometry { unsafe { let mut closure = displacement; state.data.intersect_filter_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; - sys::rtcSetGeometryDisplacementFunction( - self.handle, - displacement_function(&mut closure), - ) + rtcSetGeometryDisplacementFunction(self.handle, displacement_function(&mut closure)) } } /// Removes the displacement function for a subdivision geometry. pub fn unset_displacement_function(&mut self) { unsafe { - sys::rtcSetGeometryDisplacementFunction(self.handle, None); + rtcSetGeometryDisplacementFunction(self.handle, None); } } } @@ -186,7 +182,6 @@ where D: UserGeometryData, F: FnMut( Option<&mut D>, - RTCGeometry, u32, u32, &[f32], @@ -204,7 +199,6 @@ where D: UserGeometryData, F: FnMut( Option<&mut D>, - RTCGeometry, u32, u32, &[f32], @@ -239,19 +233,19 @@ where None => None, } }; + let len = (*args).N as usize; cb( user_data, - (*args).geometry, (*args).primID, (*args).timeStep, - std::slice::from_raw_parts((*args).u, (*args).N as usize), - std::slice::from_raw_parts((*args).v, (*args).N as usize), - std::slice::from_raw_parts((*args).Ng_x, (*args).N as usize * 3), - std::slice::from_raw_parts((*args).Ng_y, (*args).N as usize * 3), - std::slice::from_raw_parts((*args).Ng_z, (*args).N as usize * 3), - std::slice::from_raw_parts_mut((*args).P_x, (*args).N as usize * 3), - std::slice::from_raw_parts_mut((*args).P_y, (*args).N as usize * 3), - std::slice::from_raw_parts_mut((*args).P_z, (*args).N as usize * 3), + std::slice::from_raw_parts((*args).u, len), + std::slice::from_raw_parts((*args).v, len), + std::slice::from_raw_parts((*args).Ng_x, len), + std::slice::from_raw_parts((*args).Ng_y, len), + std::slice::from_raw_parts((*args).Ng_z, len), + std::slice::from_raw_parts_mut((*args).P_x, len), + std::slice::from_raw_parts_mut((*args).P_y, len), + std::slice::from_raw_parts_mut((*args).P_z, len), ); } } diff --git a/src/geometry/user_geom.rs b/src/geometry/user_geom.rs deleted file mode 100644 index 71d11380f..000000000 --- a/src/geometry/user_geom.rs +++ /dev/null @@ -1,337 +0,0 @@ -use crate::{ - geometry::GeometryData, sys::*, Bounds, Device, Error, Geometry, GeometryKind, - IntersectContext, RayHitN, RayN, UserGeometryData, -}; -use std::{ - any::TypeId, - ops::{Deref, DerefMut}, - ptr, -}; - -#[derive(Debug)] -pub struct UserGeometry(Geometry<'static>); - -impl Deref for UserGeometry { - type Target = Geometry<'static>; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -impl DerefMut for UserGeometry { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } -} - -impl UserGeometry { - pub fn new(device: &Device) -> Result { - Ok(Self(Geometry::new(device, GeometryKind::USER)?)) - } - - /// 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 - /// [`UserGeometry::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(Option<&mut D>, u32, u32, &mut Bounds), - { - unsafe { - let mut state = self.state.lock().unwrap(); - let mut closure = bounds; - state.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(), - ); - } - } - - /// Unsets the callback to calculate the bounding box of user-defined - /// geometry. - pub fn unset_bounds_function(&mut self) { - unsafe { - rtcSetGeometryBoundsFunction(self.handle, None, ptr::null_mut()); - } - } - - /// Sets the callback function to intersect a user 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 - /// [`UserGeometry::unset_intersect_function`]. - /// - /// 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. The callback function of type - /// [`RTCIntersectFunctionN`] gets passed a number of arguments through - /// the [`RTCIntersectFunctionNArguments`] structure. The value N - /// specifies the ray packet size, valid points to an array of - /// integers that specify whether the corresponding ray is valid (-1) or - /// invalid (0), the `geometryUserPtr` member points to the geometry - /// user data previously set through [`Geometry::set_user_data`], the - /// context member points to the intersection context passed to the ray - /// query, the rayhit member points to a ray and hit packet of variable - /// size N, and the geomID and primID member identifies the geometry ID - /// and primitive ID of the primitive to intersect. The ray component of - /// the rayhit structure contains valid data, in particular - /// the tfar value is the current closest hit distance found. All data - /// inside the hit component of the rayhit structure are undefined and - /// should not be read by the function. - /// 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 was found in - /// the valid range (from tnear to tfar), it should update the hit distance - /// of the ray (tfar member) and the hit (u, v, Ng, 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. - /// - /// 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 rtcFilterIntersection call. Within the user geometry intersect - /// function, it is safe to trace new rays and create new scenes and - /// geometries. When performing ray queries using rtcIntersect1, it is - /// guaranteed that the packet size is 1 when the callback is invoked. - /// When performing ray queries using the rtcIntersect4/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 rtcGetDevice- Property. When performing ray queries - /// using the stream API such as rtcIntersect1M, rtcIntersect1Mp, - /// rtcIntersectNM, or rtcIntersectNp the or- der 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, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayHitN), - { - let mut state = self.state.lock().unwrap(); - let mut closure = intersect; - state.data.user_fns.as_mut().unwrap().intersect_fn = - &mut closure as *mut _ as *mut std::os::raw::c_void; - unsafe { rtcSetGeometryIntersectFunction(self.handle, intersect_function(&mut closure)) }; - } - - /// Unsets the callback to intersect user-defined geometry. - pub fn unset_intersect_function(&mut self) { - unsafe { - rtcSetGeometryIntersectFunction(self.handle, None); - } - } - - /// Sets the callback function to occlude a user geometry. - /// - /// Similar to [`Geometry::set_intersect_function`], but for occlusion - /// queries. - pub fn set_occluded_function(&mut self, occluded: F) - where - D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayN), - { - let mut state = self.state.lock().unwrap(); - let mut closure = occluded; - state.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)) }; - } - - /// Unsets the callback to occlude user-defined geometry. - pub fn unset_occluded_function(&mut self) { - unsafe { - rtcSetGeometryOccludedFunction(self.handle, None); - } - } - - /// Sets the number of primitives of a user-defined geometry. - pub fn set_primitive_count(&mut self, count: u32) { - // Update the primitive count. - unsafe { - rtcSetGeometryUserPrimitiveCount(self.handle, count); - } - } -} - -/// Helper function to convert a Rust closure to `RTCBoundsFunction` callback. -fn bounds_function(_f: &mut F) -> RTCBoundsFunction -where - D: UserGeometryData, - F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), -{ - unsafe extern "C" fn inner(args: *const RTCBoundsFunctionArguments) - where - D: UserGeometryData, - F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), - { - let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) - .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 (*((*args).geometryUserPtr as *mut GeometryData)).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( - user_data, - (*args).primID, - (*args).timeStep, - &mut *(*args).bounds_o, - ); - } - } - - Some(inner::) -} - -/// Helper function to convert a Rust closure to `RTCIntersectFunctionN` -/// callback. -fn intersect_function(_f: &mut F) -> RTCIntersectFunctionN -where - D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayHitN), -{ - unsafe extern "C" fn inner(args: *const RTCIntersectFunctionNArguments) - where - D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayHitN), - { - let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) - .user_fns - .as_ref() - .expect( - "User payloads not set! Make sure the geometry was created with kind \ - GeometryKind::USER", - ) - .intersect_fn as *mut F; - if !cb_ptr.is_null() { - let cb = &mut *cb_ptr; - let user_data = { - match (*((*args).geometryUserPtr as *mut GeometryData)).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( - std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), - user_data, - (*args).geomID, - (*args).primID, - &mut *(*args).context, - RayHitN { - ptr: (*args).rayhit, - len: (*args).N as usize, - }, - ); - } - } - - Some(inner::) -} - -/// Helper function to convert a Rust closure to `RTCOccludedFunctionN` -/// callback. -fn occluded_function(_f: &mut F) -> RTCOccludedFunctionN -where - D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayN), -{ - unsafe extern "C" fn inner(args: *const RTCOccludedFunctionNArguments) - where - D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayN), - { - let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) - .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 (*((*args).geometryUserPtr as *mut GeometryData)).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( - std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), - user_data, - (*args).geomID, - (*args).primID, - &mut *(*args).context, - RayN { - ptr: (*args).ray, - len: (*args).N as usize, - }, - ) - } - } - - Some(inner::) -} diff --git a/src/lib.rs b/src/lib.rs index ee4fd15cf..f1a0df7a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,7 +86,7 @@ pub type GeometryKind = sys::RTCGeometryType; /// /// 1. A upper triangular scaling/skew/shift matrix /// -/// ``` +/// ```ignore /// | scale_x skew_xy skew_xz shift_x | /// | 0 scale_y skew_yz shift_y | /// | 0 0 scale_z shitf_z | @@ -94,7 +94,7 @@ pub type GeometryKind = sys::RTCGeometryType; /// ``` /// /// 2. A translation matrix -/// ``` +/// ```ignore /// | 1 0 0 translation_x | /// | 0 1 0 translation_y | /// | 0 0 1 translation_z | From c603dd9b35dc370d2201448168926decce740c2b Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 23 Feb 2023 22:44:02 +0100 Subject: [PATCH 44/65] clean displacement callback --- src/geometry.rs | 343 ++++++++++++++++++++++++++++++++++-- src/geometry/instance.rs | 75 -------- src/geometry/subdivision.rs | 254 -------------------------- 3 files changed, 329 insertions(+), 343 deletions(-) delete mode 100644 src/geometry/instance.rs delete mode 100644 src/geometry/subdivision.rs diff --git a/src/geometry.rs b/src/geometry.rs index 0e9b58f1b..713c5efff 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -4,21 +4,17 @@ use std::{ use crate::{ aligned_vector, sys::*, Bounds, BufferSlice, BufferUsage, BuildQuality, Device, Error, Format, - GeometryKind, HitN, IntersectContext, RayHitN, RayN, + GeometryKind, HitN, IntersectContext, QuaternionDecomposition, RayHitN, RayN, Scene, + SubdivisionMode, }; use std::{ borrow::Cow, ops::{Deref, DerefMut}, rc::Rc, + slice::Iter, }; -mod instance; -mod subdivision; - -pub use instance::*; -pub use subdivision::*; - // 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)] @@ -1309,6 +1305,253 @@ impl<'buf> Geometry<'buf> { _ => 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: + /// + /// * [`RTCSubdivisionMode::NO_BOUNDARY`]: Boundary patches are ignored. + /// This way each rendered patch has a full set of control vertices. + /// + /// * [`RTCSubdivisionMode::SMOOTH_BOUNDARY`]: The sequence of boundary + /// control points are used to generate a smooth B-spline boundary curve + /// (default mode). + /// + /// * [`RTCSubdivisionMode::PIN_CORNERS`]: Corner vertices are pinned to + /// their location during subdivision. + /// + /// * [`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. + /// + /// * [`RTCSubdivisionMode::PIN_ALL`]: All vertices at the border are pinned + /// to their location during subdivision. This way all patches are linearly + /// interpolated. + 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!"), + } + } + + /// Sets the number of topologies of a subdivision geometry. + /// + /// The number of topologies of a subdivision geometry must be greater + /// or equal to 1. + /// + /// 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. + /// Passing `None` will 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. + /// + /// The displacement function is called for each vertex of the subdivision + /// geometry. The function is called with the following parameters: + /// + /// * `geometry_user_data`: The user data pointer that was specified when + /// the geometry was created. + /// * `geometry`: The geometry handle. + /// * `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. + /// * `us`: The u coordinates of points to displace. + /// * `vs`: The v coordinates of points to displace. + /// * `ng_xs`: The x components of normal of vertices to displace + /// (normalized). + /// * `ng_ys`: The y component of normal of vertices to displace + /// (normalized). + /// * `ng_zs`: The z component of normal of vertices to displace + /// (normalized). + /// * `pxs`: The x components of points to displace. + /// * `pys`: The y components of points to displace. + /// * `pzs`: The z components of points to displace. + /// + /// # 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: FnMut(RTCGeometry, Option<&mut D>, u32, u32, SoAVertices), + { + match self.kind { + GeometryKind::SUBDIVISION => { + let mut state = self.state.lock().unwrap(); + unsafe { + let mut closure = displacement; + state.data.intersect_filter_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. @@ -1386,25 +1629,23 @@ impl InterpolateOutput { } } -// TODO(yang): rtcInterpolate, rtcInterpolateN - macro_rules! impl_geometry_type { ($name:ident, $kind:path, $(#[$meta:meta])*) => { #[derive(Debug)] - pub struct $name(Geometry<'static>); + pub struct $name<'a>(Geometry<'a>); - impl Deref for $name { - type Target = Geometry<'static>; + impl<'a> Deref for $name<'a> { + type Target = Geometry<'a>; fn deref(&self) -> &Self::Target { &self.0 } } - impl DerefMut for $name { + impl<'a> DerefMut for $name<'a> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } $(#[$meta])* - impl $name { + impl<'a> $name<'a> { pub fn new(device: &Device) -> Result { Ok(Self(Geometry::new(device, $kind)?)) } @@ -1484,6 +1725,10 @@ 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 @@ -1723,3 +1968,73 @@ where Some(inner::) } + +/// Struct holding data for a set of vertices in SoA layout. +pub struct SoAVertices<'a> { + pub u: &'a [f32], + pub v: &'a [f32], + pub ng_x: &'a [f32], + pub ng_y: &'a [f32], + pub ng_z: &'a [f32], + pub p_x: &'a mut [f32], + pub p_y: &'a mut [f32], + pub p_z: &'a mut [f32], +} + +/// Helper function to convert a Rust closure to `RTCDisplacementFunctionN` +/// callback. +fn displacement_function(_f: &mut F) -> RTCDisplacementFunctionN +where + D: UserGeometryData, + F: FnMut(RTCGeometry, Option<&mut D>, u32, u32, SoAVertices), +{ + unsafe extern "C" fn inner(args: *const RTCDisplacementFunctionNArguments) + where + D: UserGeometryData, + F: FnMut(RTCGeometry, Option<&mut D>, u32, u32, SoAVertices), + { + let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) + .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 (*((*args).geometryUserPtr as *mut GeometryData)).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 = SoAVertices { + u: std::slice::from_raw_parts((*args).u, len), + v: std::slice::from_raw_parts((*args).v, len), + ng_x: std::slice::from_raw_parts((*args).Ng_x, len), + ng_y: std::slice::from_raw_parts((*args).Ng_y, len), + ng_z: std::slice::from_raw_parts((*args).Ng_z, len), + p_x: std::slice::from_raw_parts_mut((*args).P_x, len), + p_y: std::slice::from_raw_parts_mut((*args).P_y, len), + p_z: std::slice::from_raw_parts_mut((*args).P_z, len), + }; + cb( + (*args).geometry, + user_data, + (*args).primID, + (*args).timeStep, + vertices, + ); + } + } + + Some(inner::) +} diff --git a/src/geometry/instance.rs b/src/geometry/instance.rs deleted file mode 100644 index bbbc9025c..000000000 --- a/src/geometry/instance.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::{sys, Device, Error, Format, Geometry, GeometryKind, QuaternionDecomposition, Scene}; -use std::ops::{Deref, DerefMut}; - -#[derive(Debug)] -pub struct Instance(Geometry<'static>); - -impl Deref for Instance { - type Target = Geometry<'static>; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -impl DerefMut for Instance { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } -} - -impl Instance { - pub fn new(device: &Device) -> Result { - Ok(Self(Geometry::new(device, GeometryKind::INSTANCE)?)) - } - - /// Sets the instanced scene of an instance geometry. - pub fn set_instanced_scene(&mut self, scene: &Scene) { - unsafe { sys::rtcSetGeometryInstancedScene(self.handle, scene.handle) } - } - - /// 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] { - unsafe { - let mut transform = [0.0; 16]; - sys::rtcGetGeometryTransform( - self.handle, - time, - Format::FLOAT4X4_COLUMN_MAJOR, - transform.as_mut_ptr() as *mut _, - ); - transform - } - } - - /// 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]) { - unsafe { - sys::rtcSetGeometryTransform( - self.handle, - time_step, - Format::FLOAT4X4_COLUMN_MAJOR, - transform.as_ptr() as *const _, - ); - } - } - - /// 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, - ) { - unsafe { - sys::rtcSetGeometryTransformQuaternion( - self.handle, - time_step, - transform as &QuaternionDecomposition as *const _, - ); - } - } -} diff --git a/src/geometry/subdivision.rs b/src/geometry/subdivision.rs deleted file mode 100644 index 7b86c2ddc..000000000 --- a/src/geometry/subdivision.rs +++ /dev/null @@ -1,254 +0,0 @@ -use crate::{ - geometry::GeometryData, sys, Device, Error, Geometry, GeometryKind, SubdivisionMode, - UserGeometryData, -}; -use std::{ - any::TypeId, - ops::{Deref, DerefMut}, -}; -use sys::*; - -#[derive(Debug)] -pub struct SubdivisionGeometry(Geometry<'static>); - -impl Deref for SubdivisionGeometry { - type Target = Geometry<'static>; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -impl DerefMut for SubdivisionGeometry { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } -} - -impl SubdivisionGeometry { - pub fn new(device: &Device) -> Result { - Ok(Self(Geometry::new(device, GeometryKind::SUBDIVISION)?)) - } - - /// 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: - /// - /// * [`RTCSubdivisionMode::NO_BOUNDARY`]: Boundary patches are ignored. - /// This way each rendered patch has a full set of control vertices. - /// - /// * [`RTCSubdivisionMode::SMOOTH_BOUNDARY`]: The sequence of boundary - /// control points are used to generate a smooth B-spline boundary curve - /// (default mode). - /// - /// * [`RTCSubdivisionMode::PIN_CORNERS`]: Corner vertices are pinned to - /// their location during subdivision. - /// - /// * [`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. - /// - /// * [`RTCSubdivisionMode::PIN_ALL`]: All vertices at the border are pinned - /// to their location during subdivision. This way all patches are linearly - /// interpolated. - pub fn set_subdivision_mode(&self, topology_id: u32, mode: SubdivisionMode) { - unsafe { rtcSetGeometrySubdivisionMode(self.handle, topology_id, mode) } - } - - /// Sets the number of topologies of a subdivision geometry. - /// - /// The number of topologies of a subdivision geometry must be greater - /// or equal to 1. - /// - /// 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) { - unsafe { rtcSetGeometryTopologyCount(self.handle, count) } - } - - /// 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 { - unsafe { rtcGetGeometryFirstHalfEdge(self.handle, face_id) } - } - - /// 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 { - unsafe { rtcGetGeometryFace(self.handle, half_edge_id) } - } - - /// 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 { - unsafe { rtcGetGeometryNextHalfEdge(self.handle, half_edge_id) } - } - - /// Returns the previous half edge of some half edge. - pub fn get_previous_half_edge(&self, half_edge_id: u32) -> u32 { - unsafe { rtcGetGeometryPreviousHalfEdge(self.handle, half_edge_id) } - } - - /// Returns the opposite half edge of some half edge. - pub fn get_opposite_half_edge(&self, topology_id: u32, edge_id: u32) -> u32 { - unsafe { rtcGetGeometryOppositeHalfEdge(self.handle, topology_id, edge_id) } - } - - // TODO(yang): Better way to deal with RTCGeometry, maybe we need a lookup table - // to get the geometry from the handle. - /// 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. - /// Passing `None` will 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. - /// - /// The displacement function is called for each vertex of the subdivision - /// geometry. The function is called with the following parameters: - /// - /// * `geometry_user_data`: The user data pointer that was specified when - /// the geometry was created. - /// * `geometry`: The geometry handle. - /// * `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. - /// * `us`: The u coordinates of points to displace. - /// * `vs`: The v coordinates of points to displace. - /// * `ng_xs`: The x components of normal of vertices to displace - /// (normalized). - /// * `ng_ys`: The y component of normal of vertices to displace - /// (normalized). - /// * `ng_ys`: The z component of normal of vertices to displace - /// (normalized). - /// * `pxs`: The x components of points to displace. - /// * `pys`: The y components of points to displace. - /// * `pzs`: The z components of points to displace. - /// - /// # 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: FnMut( - Option<&mut D>, - u32, - u32, - &[f32], - &[f32], - &[f32], - &[f32], - &[f32], - &mut [f32], - &mut [f32], - &mut [f32], - ), - { - let mut state = self.state.lock().unwrap(); - unsafe { - let mut closure = displacement; - state.data.intersect_filter_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; - rtcSetGeometryDisplacementFunction(self.handle, displacement_function(&mut closure)) - } - } - - /// Removes the displacement function for a subdivision geometry. - pub fn unset_displacement_function(&mut self) { - unsafe { - rtcSetGeometryDisplacementFunction(self.handle, None); - } - } -} - -/// Helper function to convert a Rust closure to `RTCDisplacementFunctionN` -/// callback. -fn displacement_function(_f: &mut F) -> RTCDisplacementFunctionN -where - D: UserGeometryData, - F: FnMut( - Option<&mut D>, - u32, - u32, - &[f32], - &[f32], - &[f32], - &[f32], - &[f32], - &mut [f32], - &mut [f32], - &mut [f32], - ), -{ - unsafe extern "C" fn inner(args: *const RTCDisplacementFunctionNArguments) - where - D: UserGeometryData, - F: FnMut( - Option<&mut D>, - u32, - u32, - &[f32], - &[f32], - &[f32], - &[f32], - &[f32], - &mut [f32], - &mut [f32], - &mut [f32], - ), - { - let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) - .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 (*((*args).geometryUserPtr as *mut GeometryData)).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( - user_data, - (*args).primID, - (*args).timeStep, - std::slice::from_raw_parts((*args).u, len), - std::slice::from_raw_parts((*args).v, len), - std::slice::from_raw_parts((*args).Ng_x, len), - std::slice::from_raw_parts((*args).Ng_y, len), - std::slice::from_raw_parts((*args).Ng_z, len), - std::slice::from_raw_parts_mut((*args).P_x, len), - std::slice::from_raw_parts_mut((*args).P_y, len), - std::slice::from_raw_parts_mut((*args).P_z, len), - ); - } - } - - Some(inner::) -} From 1212e30de194d8015c845b9673d804d47735c2e4 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Fri, 24 Feb 2023 18:15:18 +0100 Subject: [PATCH 45/65] displacement geometry example --- examples/displacement_geometry/Cargo.toml | 16 + examples/displacement_geometry/src/main.rs | 221 ++++++++ examples/support/src/camera.rs | 2 +- examples/support/src/common.rs | 601 +++++++++++++++++++++ examples/support/src/display.rs | 2 +- examples/support/src/lib.rs | 3 + examples/triangle/src/main.rs | 3 +- examples/triangle_geometry/src/main.rs | 4 +- src/device.rs | 2 +- src/geometry.rs | 460 +++++++++++----- src/scene.rs | 23 +- 11 files changed, 1182 insertions(+), 155 deletions(-) create mode 100644 examples/displacement_geometry/Cargo.toml create mode 100644 examples/displacement_geometry/src/main.rs create mode 100644 examples/support/src/common.rs diff --git a/examples/displacement_geometry/Cargo.toml b/examples/displacement_geometry/Cargo.toml new file mode 100644 index 000000000..501a51951 --- /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.22.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..877f97eb9 --- /dev/null +++ b/examples/displacement_geometry/src/main.rs @@ -0,0 +1,221 @@ +use embree::{ + BufferSlice, BufferUsage, Device, Format, Geometry, GeometryKind, InterpolateInput, + InterpolateOutput, IntersectContext, Ray, Scene, SceneFlags, +}; +use glam::vec3; +use support::{noise, Align16Array, Camera}; + +const EDGE_LEVEL: f32 = 256.0; +const NUM_INDICES: usize = 24; +const NUM_FACES: usize = 6; +const FACE_SIZE: usize = 4; + +const CUBE_VERTICES: Align16Array = Align16Array([ + -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]; + +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 +} + +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, user_data: Option<&mut ()>, prim_id, _, vertices| { + for (uv, 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(512, 512, "Dynamic Scene"); + let light_dir = vec3(1.0, 1.0, 1.0).normalize(); + support::display::run(display, move |image, camera_pose, time| { + 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, + ); + + // 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 intersection_ctx = IntersectContext::coherent(); + let ray_hit = scene.intersect( + &mut intersection_ctx, + Ray::new(camera.pos.into(), dir.into()), + ); + + let mut color = vec3(0.0, 0.0, 0.0); + if ray_hit.is_valid() { + let pixel = image.get_pixel_mut(i, j); + 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 mut normal = glam::Vec3::from(ray_hit.hit.normal_normalized()); + + #[cfg(feature = "smooth_normals")] + { + let hit_point: glam::Vec3 = ray_hit.hit_point().into(); + if ray_hit.hit.geomID != ground_id { + let mut output = InterpolateOutput::new(3, true, true, false); + 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 = + glam::Vec3::from_slice(output.dp_du().as_ref().unwrap()); + let mut dp_dv = + glam::Vec3::from_slice(output.dp_dv().as_ref().unwrap()); + let ng = dp_du.cross(dp_dv).normalize(); + dp_du += ng * displacement([hit_point.x, hit_point.y, hit_point.z]); + dp_dv += ng * displacement([hit_point.x, hit_point.y, hit_point.z]); + normal = dp_du.cross(dp_dv).normalize(); + } + } + + let mut shadow_ray = + Ray::segment(ray_hit.hit_point(), light_dir.into(), 0.001, f32::INFINITY); + + // Check if the shadow ray is occluded. + if !scene.occluded(&mut intersection_ctx, &mut shadow_ray) { + color += diffuse * light_dir.dot(normal).clamp(0.0, 1.0); + } + + // Write the color to the image. + pixel[0] = (color.x * 255.0) as u8; + pixel[1] = (color.y * 255.0) as u8; + pixel[2] = (color.z * 255.0) as u8; + } + } + } + }); +} diff --git a/examples/support/src/camera.rs b/examples/support/src/camera.rs index 7042e0999..026587f28 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 { diff --git a/examples/support/src/common.rs b/examples/support/src/common.rs new file mode 100644 index 000000000..4bf96faca --- /dev/null +++ b/examples/support/src/common.rs @@ -0,0 +1,601 @@ +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, +]; + +#[repr(align(16))] +pub struct Align16Array(pub [T; N]); + +#[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] as f32 + + y * G3[h as usize * 4 + 1] as f32 + + z * G3[h as usize * 4 + 2] as f32 +} + +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 ac98b28a3..819732bd9 100644 --- a/examples/support/src/display.rs +++ b/examples/support/src/display.rs @@ -2,7 +2,7 @@ use core::num::NonZeroU32; use std::borrow::Cow; use arcball::ArcballCamera; -use cgmath::{Matrix4, SquareMatrix, Vector2, Vector3, Vector4}; +use cgmath::{Vector2, Vector3}; use clock_ticks; use futures; use image::RgbaImage; diff --git a/examples/support/src/lib.rs b/examples/support/src/lib.rs index 8bfc54c4a..96ebb1a9a 100644 --- a/examples/support/src/lib.rs +++ b/examples/support/src/lib.rs @@ -12,8 +12,11 @@ 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; diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index 04278a141..70c00a419 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -4,8 +4,7 @@ extern crate embree; extern crate support; use embree::{ - BufferUsage, Device, Format, GeometryKind, IntersectContext, RayHitStream, RayStream, - SubdivisionGeometry, TriangleMesh, + BufferUsage, Device, Format, IntersectContext, RayHitStream, RayStream, TriangleMesh, }; fn main() { diff --git a/examples/triangle_geometry/src/main.rs b/examples/triangle_geometry/src/main.rs index 32c1c243a..dc1ad4df7 100644 --- a/examples/triangle_geometry/src/main.rs +++ b/examples/triangle_geometry/src/main.rs @@ -8,7 +8,7 @@ use embree::{ }; use support::Camera; -fn make_cube(device: &Device, vertex_colors: &[[f32; 3]]) -> TriangleMesh { +fn make_cube(device: &Device, vertex_colors: &[[f32; 3]]) -> TriangleMesh<'static> { let mut mesh = TriangleMesh::new(device).unwrap(); { mesh.set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 12, 8) @@ -65,7 +65,7 @@ fn make_cube(device: &Device, vertex_colors: &[[f32; 3]]) -> TriangleMesh { mesh } -fn make_ground_plane(device: &Device) -> QuadMesh { +fn make_ground_plane(device: &Device) -> QuadMesh<'static> { let mut mesh = QuadMesh::new(device).unwrap(); { mesh.set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, 4) diff --git a/src/device.rs b/src/device.rs index 4f59f6ca9..071c3a274 100644 --- a/src/device.rs +++ b/src/device.rs @@ -195,7 +195,7 @@ impl Device { pub fn get_error(&self) -> RTCError { unsafe { rtcGetDeviceError(self.handle) } } /// Creates a new scene bound to the device. - pub fn create_scene(&self) -> Result { Scene::new(self.clone()) } + 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 diff --git a/src/geometry.rs b/src/geometry.rs index 713c5efff..6d4e39861 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -3,16 +3,14 @@ use std::{ }; use crate::{ - aligned_vector, sys::*, Bounds, BufferSlice, BufferUsage, BuildQuality, Device, Error, Format, - GeometryKind, HitN, IntersectContext, QuaternionDecomposition, RayHitN, RayN, Scene, - SubdivisionMode, + sys::*, Bounds, BufferSlice, BufferUsage, BuildQuality, Device, Error, Format, GeometryKind, + HitN, IntersectContext, QuaternionDecomposition, RayHitN, RayN, Scene, SubdivisionMode, }; use std::{ borrow::Cow, ops::{Deref, DerefMut}, - rc::Rc, - slice::Iter, + sync::Arc, }; // TODO(yang): maybe enforce format and stride when get the view? @@ -100,11 +98,16 @@ pub(crate) struct GeometryData { pub subdivision_fns: Option, } -/// Extra data for a geometry. -#[derive(Debug, Clone)] -struct GeometryState<'buf> { - attachments: HashMap>>, - data: GeometryData, +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. @@ -155,7 +158,10 @@ pub struct Geometry<'buf> { pub(crate) device: Device, pub(crate) handle: RTCGeometry, kind: GeometryKind, - state: Rc>>, + /// Buffers that are attached to this geometry. + attachments: Arc>>>>, + /// Data associated with this geometry. + data: Arc>, } impl<'buf> Clone for Geometry<'buf> { @@ -167,7 +173,8 @@ impl<'buf> Clone for Geometry<'buf> { device: self.device.clone(), handle: self.handle, kind: self.kind, - state: self.state.clone(), + attachments: self.attachments.clone(), + data: self.data.clone(), } } } @@ -205,29 +212,39 @@ impl<'buf> Geometry<'buf> { if handle.is_null() { Err(device.get_error()) } else { - let state = GeometryState { - attachments: Default::default(), - data: GeometryData { - user_data: None, - intersect_filter_fn: ptr::null_mut(), - occluded_filter_fn: ptr::null_mut(), - user_fns: if kind == GeometryKind::USER { - Some(UserGeometryPayloads::default()) - } else { - None - }, - subdivision_fns: if kind == GeometryKind::SUBDIVISION { - Some(SubdivisionGeometryPayloads::default()) - } else { - None - }, + 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, - state: Rc::new(Mutex::new(state)), + attachments: Arc::new(Mutex::new(HashMap::default())), + data, }) } } @@ -337,8 +354,8 @@ impl<'buf> Geometry<'buf> { stride, count ); - let mut state = self.state.lock().unwrap(); - let bindings = state.attachments.entry(usage).or_insert_with(Vec::new); + 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. @@ -403,8 +420,8 @@ impl<'buf> Geometry<'buf> { BufferSlice::User { ptr, offset, size, .. } => { - let mut state = self.state.lock().unwrap(); - let bindings = state.attachments.entry(usage).or_insert_with(Vec::new); + 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. @@ -506,8 +523,8 @@ impl<'buf> Geometry<'buf> { self.check_vertex_attribute()?; } { - let mut state = self.state.lock().unwrap(); - let bindings = state.attachments.entry(usage).or_insert_with(Vec::new); + 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) @@ -537,9 +554,8 @@ impl<'buf> Geometry<'buf> { /// Returns the buffer bound to the given slot and usage. pub fn get_buffer(&self, usage: BufferUsage, slot: u32) -> Option { - let state = self.state.lock().unwrap(); - state - .attachments + let attachments = self.attachments.lock().unwrap(); + attachments .get(&usage) .and_then(|v| v.iter().find(|a| a.slot == slot)) .map(|a| a.source) @@ -663,10 +679,10 @@ impl<'buf> Geometry<'buf> { D: UserGeometryData, F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), { - let mut state = self.state.lock().unwrap(); + let mut geom_data = self.data.lock().unwrap(); unsafe { let mut closure = filter; - state.data.intersect_filter_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; + geom_data.intersect_filter_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; rtcSetGeometryIntersectFilterFunction( self.handle, intersect_filter_function(&mut closure), @@ -703,10 +719,10 @@ impl<'buf> Geometry<'buf> { D: UserGeometryData, F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), { - let mut state = self.state.lock().unwrap(); + let mut geom_data = self.data.lock().unwrap(); unsafe { let mut closure = filter; - state.data.occluded_filter_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; + geom_data.occluded_filter_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; rtcSetGeometryOccludedFilterFunction( self.handle, occluded_filter_function(&mut closure), @@ -906,13 +922,16 @@ impl<'buf> Geometry<'buf> { where D: UserGeometryData, { - let mut state = self.state.lock().unwrap(); - state.data.user_data = Some(GeometryUserData { + 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, &mut state.data as *mut GeometryData as *mut _); + rtcSetGeometryUserData( + self.handle, + geom_data.deref_mut() as *mut GeometryData as *mut _, + ); } } @@ -1014,36 +1033,30 @@ impl<'buf> Geometry<'buf> { bufferType: input.usage, bufferSlot: input.slot, P: output - .p - .as_mut() + .p_mut() .map(|p| p.as_mut_ptr()) .unwrap_or(ptr::null_mut()), dPdu: output - .dp_du - .as_mut() + .dp_du_mut() .map(|p| p.as_mut_ptr()) .unwrap_or(ptr::null_mut()), dPdv: output - .dp_dv - .as_mut() + .dp_dv_mut() .map(|p| p.as_mut_ptr()) .unwrap_or(ptr::null_mut()), ddPdudu: output - .ddp_du_du - .as_mut() + .ddp_du_du_mut() .map(|p| p.as_mut_ptr()) .unwrap_or(ptr::null_mut()), ddPdvdv: output - .ddp_dv_dv - .as_mut() + .ddp_dv_dv_mut() .map(|p| p.as_mut_ptr()) .unwrap_or(ptr::null_mut()), ddPdudv: output - .ddp_du_dv - .as_mut() + .ddp_du_dv_mut() .map(|p| p.as_mut_ptr()) .unwrap_or(ptr::null_mut()), - valueCount: output.count, + valueCount: output.value_count(), }; unsafe { rtcInterpolate(&args as _); @@ -1082,36 +1095,30 @@ impl<'buf> Geometry<'buf> { bufferType: input.usage, bufferSlot: input.slot, P: output - .p - .as_mut() + .p_mut() .map(|p| p.as_mut_ptr()) .unwrap_or(ptr::null_mut()), dPdu: output - .dp_du - .as_mut() + .dp_du_mut() .map(|p| p.as_mut_ptr()) .unwrap_or(ptr::null_mut()), dPdv: output - .dp_dv - .as_mut() + .dp_dv_mut() .map(|p| p.as_mut_ptr()) .unwrap_or(ptr::null_mut()), ddPdudu: output - .ddp_du_du - .as_mut() + .ddp_du_du_mut() .map(|p| p.as_mut_ptr()) .unwrap_or(ptr::null_mut()), ddPdvdv: output - .ddp_dv_dv - .as_mut() + .ddp_dv_dv_mut() .map(|p| p.as_mut_ptr()) .unwrap_or(ptr::null_mut()), ddPdudv: output - .ddp_du_dv - .as_mut() + .ddp_du_dv_mut() .map(|p| p.as_mut_ptr()) .unwrap_or(ptr::null_mut()), - valueCount: output.count, + valueCount: output.value_count(), }; unsafe { rtcInterpolateN(&args as _); @@ -1154,9 +1161,9 @@ impl<'buf> Geometry<'buf> { { match self.kind { GeometryKind::USER => unsafe { - let mut state = self.state.lock().unwrap(); + let mut geom_data = self.data.lock().unwrap(); let mut closure = bounds; - state.data.user_fns.as_mut().unwrap().bounds_fn = + geom_data.user_fns.as_mut().unwrap().bounds_fn = &mut closure as *mut _ as *mut std::os::raw::c_void; rtcSetGeometryBoundsFunction( self.handle, @@ -1243,9 +1250,9 @@ impl<'buf> Geometry<'buf> { { match self.kind { GeometryKind::USER => unsafe { - let mut state = self.state.lock().unwrap(); + let mut geom_data = self.data.lock().unwrap(); let mut closure = intersect; - state.data.user_fns.as_mut().unwrap().intersect_fn = + 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)); }, @@ -1274,9 +1281,9 @@ impl<'buf> Geometry<'buf> { { match self.kind { GeometryKind::USER => { - let mut state = self.state.lock().unwrap(); + let mut geom_data = self.data.lock().unwrap(); let mut closure = occluded; - state.data.user_fns.as_mut().unwrap().occluded_fn = + 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)) @@ -1456,15 +1463,18 @@ impl<'buf> Geometry<'buf> { pub unsafe fn set_displacement_function(&mut self, displacement: F) where D: UserGeometryData, - F: FnMut(RTCGeometry, Option<&mut D>, u32, u32, SoAVertices), + F: FnMut(RTCGeometry, Option<&mut D>, u32, u32, Vertices), { match self.kind { GeometryKind::SUBDIVISION => { - let mut state = self.state.lock().unwrap(); + let mut geom_data = self.data.lock().unwrap(); unsafe { let mut closure = displacement; - state.data.intersect_filter_fn = - &mut closure as *mut _ as *mut std::os::raw::c_void; + 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), @@ -1577,56 +1587,165 @@ pub struct InterpolateNInput<'a> { /// The output of the `Geometry::interpolate` and `Geometry::interpolate_n` /// functions in structure of array (SOA) layout. pub struct InterpolateOutput { - pub count: u32, - pub p: Option>, - pub dp_du: Option>, - pub dp_dv: Option>, - pub ddp_du_du: Option>, - pub ddp_dv_dv: Option>, - pub ddp_du_dv: Option>, + /// 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, - origin_value: bool, - first_order_derivative: bool, - second_order_derivative: bool, - ) -> Self { + 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 { - count, - p: if origin_value { - Some(aligned_vector::(count as usize, 16)) - } else { - None - }, - dp_du: if first_order_derivative { - Some(aligned_vector::(count as usize, 16)) - } else { - None - }, - dp_dv: if first_order_derivative { - Some(aligned_vector::(count as usize, 16)) - } else { - None - }, - ddp_du_du: if second_order_derivative { - Some(aligned_vector::(count as usize, 16)) - } else { - None - }, - ddp_dv_dv: if second_order_derivative { - Some(aligned_vector::(count as usize, 16)) - } else { - None - }, - ddp_du_dv: if second_order_derivative { - Some(aligned_vector::(count as usize, 16)) - } else { - None - }, + 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 { @@ -1970,15 +2089,79 @@ where } /// Struct holding data for a set of vertices in SoA layout. -pub struct SoAVertices<'a> { - pub u: &'a [f32], - pub v: &'a [f32], - pub ng_x: &'a [f32], - pub ng_y: &'a [f32], - pub ng_z: &'a [f32], - pub p_x: &'a mut [f32], - pub p_y: &'a mut [f32], - pub p_z: &'a mut [f32], +pub struct Vertices<'src> { + pub len: usize, + pub u: &'src [f32], + pub v: &'src [f32], + pub ng_x: &'src [f32], + pub ng_y: &'src [f32], + pub ng_z: &'src [f32], + pub p_x: &'src mut [f32], + pub p_y: &'src mut [f32], + pub p_z: &'src mut [f32], +} + +impl<'src> Vertices<'src> { + pub fn into_iter_mut(self) -> VerticesIterMut<'src> { + VerticesIterMut { + u: self.u.as_ptr(), + v: self.v.as_ptr(), + ng_x: self.ng_x.as_ptr(), + ng_y: self.ng_y.as_ptr(), + ng_z: self.ng_z.as_ptr(), + p_x: self.p_x.as_mut_ptr(), + p_y: self.p_y.as_mut_ptr(), + p_z: self.p_z.as_mut_ptr(), + cur: 0, + len: self.len, + marker: Default::default(), + } + } +} + +pub struct VerticesIterMut<'src> { + u: *const f32, + v: *const f32, + ng_x: *const f32, + ng_y: *const f32, + ng_z: *const f32, + p_x: *mut f32, + p_y: *mut f32, + p_z: *mut f32, + cur: usize, + len: usize, + marker: PhantomData>, +} + +impl<'src> Iterator for VerticesIterMut<'src> { + type Item = ([f32; 2], [f32; 3], [&'src mut f32; 3]); + + fn next(&mut self) -> Option { + if self.cur < self.len { + unsafe { + let u = *self.u.add(self.cur); + let v = *self.v.add(self.cur); + let ng_x = *self.ng_x.add(self.cur); + let ng_y = *self.ng_y.add(self.cur); + let ng_z = *self.ng_z.add(self.cur); + let p_x = self.p_x.add(self.cur); + let p_y = self.p_y.add(self.cur); + let p_z = self.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<'src> ExactSizeIterator for VerticesIterMut<'src> { + fn len(&self) -> usize { self.len - self.cur } } /// Helper function to convert a Rust closure to `RTCDisplacementFunctionN` @@ -1986,12 +2169,12 @@ pub struct SoAVertices<'a> { fn displacement_function(_f: &mut F) -> RTCDisplacementFunctionN where D: UserGeometryData, - F: FnMut(RTCGeometry, Option<&mut D>, u32, u32, SoAVertices), + F: FnMut(RTCGeometry, Option<&mut D>, u32, u32, Vertices), { unsafe extern "C" fn inner(args: *const RTCDisplacementFunctionNArguments) where D: UserGeometryData, - F: FnMut(RTCGeometry, Option<&mut D>, u32, u32, SoAVertices), + F: FnMut(RTCGeometry, Option<&mut D>, u32, u32, Vertices), { let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) .subdivision_fns @@ -2016,7 +2199,8 @@ where } }; let len = (*args).N as usize; - let vertices = SoAVertices { + let vertices = Vertices { + len, u: std::slice::from_raw_parts((*args).u, len), v: std::slice::from_raw_parts((*args).v, len), ng_x: std::slice::from_raw_parts((*args).Ng_x, len), diff --git a/src/scene.rs b/src/scene.rs index b1760c5bf..b206b7319 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -19,14 +19,14 @@ use crate::{ /// A scene containing various geometries. #[derive(Debug)] -pub struct Scene { +pub struct Scene<'a> { pub(crate) handle: RTCScene, pub(crate) device: Device, - geometries: Arc>>>, + geometries: Arc>>>, point_query_user_data: Arc>, } -impl Clone for Scene { +impl<'a> Clone for Scene<'a> { fn clone(&self) -> Self { unsafe { rtcRetainScene(self.handle) } Self { @@ -38,7 +38,7 @@ impl Clone for Scene { } } -impl Drop for Scene { +impl<'a> Drop for Scene<'a> { fn drop(&mut self) { unsafe { rtcReleaseScene(self.handle); @@ -46,9 +46,12 @@ impl Drop for Scene { } } -impl Scene { +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 { + pub(crate) fn new(device: Device) -> Result { let handle = unsafe { rtcNewScene(device.handle) }; if handle.is_null() { Err(device.get_error()) @@ -63,7 +66,7 @@ impl Scene { } /// Creates a new scene with the given device and flags. - pub(crate) fn new_with_flags(device: Device, flags: SceneFlags) -> Result { + pub(crate) fn new_with_flags(device: Device, flags: SceneFlags) -> Result { let scene = Self::new(device)?; scene.set_flags(flags); Ok(scene) @@ -85,7 +88,7 @@ impl Scene { /// 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<'a>(&'a mut self, geometry: &'a Geometry<'static>) -> u32 { + 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 @@ -103,7 +106,7 @@ impl Scene { /// /// This function is thread-safe, thus multiple threads can attach /// geometries to a scene at the same time. - pub fn attach_geometry_by_id<'a>(&'a mut self, geometry: &'a Geometry<'static>, id: u32) { + 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()); } @@ -127,7 +130,7 @@ impl Scene { /// 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> { + pub fn get_geometry_unchecked(&self, id: u32) -> Option> { let raw = unsafe { rtcGetGeometry(self.handle, id) }; if raw.is_null() { None From 64c88023d66d00c8b1691c009275136c2dbf4dd3 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Fri, 24 Feb 2023 18:54:03 +0100 Subject: [PATCH 46/65] temp update --- src/geometry.rs | 121 +++++++++++++++++++++--------------------------- 1 file changed, 54 insertions(+), 67 deletions(-) diff --git a/src/geometry.rs b/src/geometry.rs index 6d4e39861..37eb21415 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -737,6 +737,13 @@ impl<'buf> Geometry<'buf> { } } + // 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 @@ -1437,24 +1444,14 @@ impl<'buf> Geometry<'buf> { /// The displacement function is called for each vertex of the subdivision /// geometry. The function is called with the following parameters: /// - /// * `geometry_user_data`: The user data pointer that was specified when - /// the geometry was created. /// * `geometry`: The geometry handle. + /// * `geometry_user_data`: The user data. /// * `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. - /// * `us`: The u coordinates of points to displace. - /// * `vs`: The v coordinates of points to displace. - /// * `ng_xs`: The x components of normal of vertices to displace - /// (normalized). - /// * `ng_ys`: The y component of normal of vertices to displace - /// (normalized). - /// * `ng_zs`: The z component of normal of vertices to displace - /// (normalized). - /// * `pxs`: The x components of points to displace. - /// * `pys`: The y components of points to displace. - /// * `pzs`: The z components of points to displace. + /// * `vertices`: The information about the vertices to displace. See + /// [`Vertices`]. /// /// # Safety /// @@ -2089,64 +2086,54 @@ where } /// Struct holding data for a set of vertices in SoA layout. -pub struct Vertices<'src> { - pub len: usize, - pub u: &'src [f32], - pub v: &'src [f32], - pub ng_x: &'src [f32], - pub ng_y: &'src [f32], - pub ng_z: &'src [f32], - pub p_x: &'src mut [f32], - pub p_y: &'src mut [f32], - pub p_z: &'src mut [f32], -} - -impl<'src> Vertices<'src> { - pub fn into_iter_mut(self) -> VerticesIterMut<'src> { - VerticesIterMut { - u: self.u.as_ptr(), - v: self.v.as_ptr(), - ng_x: self.ng_x.as_ptr(), - ng_y: self.ng_y.as_ptr(), - ng_z: self.ng_z.as_ptr(), - p_x: self.p_x.as_mut_ptr(), - p_y: self.p_y.as_mut_ptr(), - p_z: self.p_z.as_mut_ptr(), - cur: 0, - len: self.len, - marker: Default::default(), - } - } -} - -pub struct VerticesIterMut<'src> { +pub struct 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, +} + +impl Vertices { + pub fn into_iter_mut(self) -> VerticesIterMut { + VerticesIterMut { + inner: self, + cur: 0, + } + } +} + +pub struct VerticesIterMut { + inner: Vertices, cur: usize, - len: usize, - marker: PhantomData>, } -impl<'src> Iterator for VerticesIterMut<'src> { - type Item = ([f32; 2], [f32; 3], [&'src mut f32; 3]); +impl Iterator for VerticesIterMut { + type Item = ([f32; 2], [f32; 3], [&mut f32; 3]); fn next(&mut self) -> Option { - if self.cur < self.len { + if self.cur < self.inner.len { unsafe { - let u = *self.u.add(self.cur); - let v = *self.v.add(self.cur); - let ng_x = *self.ng_x.add(self.cur); - let ng_y = *self.ng_y.add(self.cur); - let ng_z = *self.ng_z.add(self.cur); - let p_x = self.p_x.add(self.cur); - let p_y = self.p_y.add(self.cur); - let p_z = self.p_z.add(self.cur); + 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], @@ -2160,8 +2147,8 @@ impl<'src> Iterator for VerticesIterMut<'src> { } } -impl<'src> ExactSizeIterator for VerticesIterMut<'src> { - fn len(&self) -> usize { self.len - self.cur } +impl ExactSizeIterator for VerticesIterMut { + fn len(&self) -> usize { self.inner.len - self.cur } } /// Helper function to convert a Rust closure to `RTCDisplacementFunctionN` @@ -2201,14 +2188,14 @@ where let len = (*args).N as usize; let vertices = Vertices { len, - u: std::slice::from_raw_parts((*args).u, len), - v: std::slice::from_raw_parts((*args).v, len), - ng_x: std::slice::from_raw_parts((*args).Ng_x, len), - ng_y: std::slice::from_raw_parts((*args).Ng_y, len), - ng_z: std::slice::from_raw_parts((*args).Ng_z, len), - p_x: std::slice::from_raw_parts_mut((*args).P_x, len), - p_y: std::slice::from_raw_parts_mut((*args).P_y, len), - p_z: std::slice::from_raw_parts_mut((*args).P_z, 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, }; cb( (*args).geometry, From 4a74e10019baac6019591ac853ff2ec7ee66b817 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Mon, 27 Feb 2023 15:17:29 +0100 Subject: [PATCH 47/65] reduce code duplication --- examples/triangle/src/main.rs | 8 +- src/bvh.rs | 65 ++++- src/geometry.rs | 66 +++-- src/lib.rs | 2 + src/ray/packet.rs | 501 ++++++++++++++++------------------ src/ray/soa.rs | 1 - src/ray/stream.rs | 76 ++++-- src/scene.rs | 10 +- 8 files changed, 394 insertions(+), 335 deletions(-) diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index 70c00a419..cb00be930 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -3,9 +3,7 @@ extern crate embree; extern crate support; -use embree::{ - BufferUsage, Device, Format, IntersectContext, RayHitStream, RayStream, TriangleMesh, -}; +use embree::{BufferUsage, Device, Format, IntersectContext, RayHitNp, RayNp, TriangleMesh}; fn main() { let display = support::Display::new(512, 512, "triangle"); @@ -49,7 +47,7 @@ fn main() { let y = -(j as f32 + 0.5) / img_dims.1 as f32 + 0.5; // Try out streams of scanlines across x - let mut rays = RayStream::new(img_dims.0 as usize); + 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); @@ -57,7 +55,7 @@ fn main() { ray.set_dir([x / dir_len, y / dir_len, -1.0 / dir_len]); } - let mut ray_hit = RayHitStream::new(rays); + let mut ray_hit = RayHitNp::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); diff --git a/src/bvh.rs b/src/bvh.rs index b837b2258..66c70dbfd 100644 --- a/src/bvh.rs +++ b/src/bvh.rs @@ -1,5 +1,8 @@ use crate::{sys::*, BuildFlags, BuildPrimitive, BuildQuality, Device, Error}; +#[derive(Debug, Clone, Copy)] +pub struct ThreadLocalAllocator(RTCThreadLocalAllocator); + pub struct Bvh { handle: RTCBVH, } @@ -28,17 +31,23 @@ impl Bvh { } } -pub struct BvhBuilderUserData { - create_node_fn: *mut std::os::raw::c_void, +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> { - bvh: &'a Bvh, +pub struct BvhBuilder<'a, T> { quality: Option, flags: Option, max_branching_factor: Option, @@ -46,16 +55,17 @@ pub struct BvhBuilder<'a> { sah_block_size: Option, min_leaf_size: Option, max_leaf_size: Option, - traversal_cost: Option, - intersection_cost: Option, + traversal_cost: Option, + intersection_cost: Option, primitives: Option>, + // create_node_fn: Option>, + user_data: Option<&'a mut T>, ready: u32, } -impl<'a> BvhBuilder<'a> { - pub fn new(bvh: &'a Bvh) -> Self { +impl<'a, T> BvhBuilder<'a, T> { + pub fn new() -> Self { Self { - bvh, quality: None, flags: None, max_branching_factor: None, @@ -66,6 +76,8 @@ impl<'a> BvhBuilder<'a> { traversal_cost: None, intersection_cost: None, primitives: None, + // create_node_fn: None, + user_data: None, ready: 0, } } @@ -112,13 +124,13 @@ impl<'a> BvhBuilder<'a> { self } - pub fn traversal_cost(mut self, traversal_cost: u32) -> 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: u32) -> Self { + pub fn intersection_cost(mut self, intersection_cost: f32) -> Self { self.intersection_cost = Some(intersection_cost); self.ready |= 1 << 8; self @@ -130,5 +142,34 @@ impl<'a> BvhBuilder<'a> { self } - // TODO: build + // 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/geometry.rs b/src/geometry.rs index 37eb21415..12b258bee 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -677,7 +677,7 @@ impl<'buf> Geometry<'buf> { pub fn set_intersect_filter_function(&mut self, filter: F) where D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), + F: for<'a> FnMut(&'a mut [i32], Option<&mut D>, &mut IntersectContext, RayN<'a>, HitN<'a>), { let mut geom_data = self.data.lock().unwrap(); unsafe { @@ -815,7 +815,7 @@ impl<'buf> Geometry<'buf> { /// 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) { + pub unsafe fn set_point_query_function(&mut self, query_fn: RTCPointQueryFunction) { rtcSetGeometryPointQueryFunction(self.handle, query_fn); } @@ -1253,7 +1253,14 @@ impl<'buf> Geometry<'buf> { pub fn set_intersect_function(&mut self, intersect: F) where D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayHitN), + F: for<'a> FnMut( + &'a mut [i32], + Option<&mut D>, + u32, + u32, + &mut IntersectContext, + RayHitN<'a>, + ), { match self.kind { GeometryKind::USER => unsafe { @@ -1284,7 +1291,7 @@ impl<'buf> Geometry<'buf> { pub fn set_occluded_function(&mut self, occluded: F) where D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayN), + F: for<'a> FnMut(&'a mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayN<'a>), { match self.kind { GeometryKind::USER => { @@ -1460,7 +1467,7 @@ impl<'buf> Geometry<'buf> { pub unsafe fn set_displacement_function(&mut self, displacement: F) where D: UserGeometryData, - F: FnMut(RTCGeometry, Option<&mut D>, u32, u32, Vertices), + F: for<'a> FnMut(RTCGeometry, Option<&mut D>, u32, u32, Vertices<'a>), { match self.kind { GeometryKind::SUBDIVISION => { @@ -1850,12 +1857,12 @@ impl_geometry_type!(Instance, GeometryKind::INSTANCE, fn intersect_filter_function(_f: &mut F) -> RTCFilterFunctionN where D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), + F: for<'a> FnMut(&'a mut [i32], Option<&mut D>, &mut IntersectContext, RayN<'a>, HitN<'a>), { unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) where D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), + F: for<'a> FnMut(&'a mut [i32], Option<&mut D>, &mut IntersectContext, RayN<'a>, HitN<'a>), { let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)).intersect_filter_fn as *mut F; @@ -1880,10 +1887,12 @@ where RayN { ptr: &mut *(*args).ray, len: (*args).N as usize, + marker: PhantomData, }, HitN { ptr: &mut *(*args).hit, len: (*args).N as usize, + marker: PhantomData, }, ); } @@ -1926,10 +1935,12 @@ where RayN { ptr: &mut *(*args).ray, len, + marker: PhantomData, }, HitN { ptr: &mut *(*args).hit, len, + marker: PhantomData, }, ); } @@ -1988,12 +1999,19 @@ where fn intersect_function(_f: &mut F) -> RTCIntersectFunctionN where D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayHitN), + F: for<'a> FnMut(&'a mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayHitN<'a>), { unsafe extern "C" fn inner(args: *const RTCIntersectFunctionNArguments) where D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayHitN), + F: for<'a> FnMut( + &'a mut [i32], + Option<&mut D>, + u32, + u32, + &mut IntersectContext, + RayHitN<'a>, + ), { let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) .user_fns @@ -2026,6 +2044,7 @@ where RayHitN { ptr: (*args).rayhit, len: (*args).N as usize, + marker: PhantomData, }, ); } @@ -2039,12 +2058,12 @@ where fn occluded_function(_f: &mut F) -> RTCOccludedFunctionN where D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayN), + F: for<'a> FnMut(&'a mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayN<'a>), { unsafe extern "C" fn inner(args: *const RTCOccludedFunctionNArguments) where D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayN), + F: for<'a> FnMut(&'a mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayN<'a>), { let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) .user_fns @@ -2077,6 +2096,7 @@ where RayN { ptr: (*args).ray, len: (*args).N as usize, + marker: PhantomData, }, ) } @@ -2086,7 +2106,10 @@ where } /// Struct holding data for a set of vertices in SoA layout. -pub struct Vertices { +/// 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, @@ -2104,10 +2127,12 @@ pub struct Vertices { 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 Vertices { - pub fn into_iter_mut(self) -> VerticesIterMut { +impl<'a> Vertices<'a> { + pub fn into_iter_mut(self) -> VerticesIterMut<'a> { VerticesIterMut { inner: self, cur: 0, @@ -2115,13 +2140,13 @@ impl Vertices { } } -pub struct VerticesIterMut { - inner: Vertices, +pub struct VerticesIterMut<'a> { + inner: Vertices<'a>, cur: usize, } -impl Iterator for VerticesIterMut { - type Item = ([f32; 2], [f32; 3], [&mut f32; 3]); +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 { @@ -2147,7 +2172,7 @@ impl Iterator for VerticesIterMut { } } -impl ExactSizeIterator for VerticesIterMut { +impl<'a> ExactSizeIterator for VerticesIterMut<'a> { fn len(&self) -> usize { self.inner.len - self.cur } } @@ -2156,7 +2181,7 @@ impl ExactSizeIterator for VerticesIterMut { fn displacement_function(_f: &mut F) -> RTCDisplacementFunctionN where D: UserGeometryData, - F: FnMut(RTCGeometry, Option<&mut D>, u32, u32, Vertices), + F: for<'a> FnMut(RTCGeometry, Option<&mut D>, u32, u32, Vertices<'a>), { unsafe extern "C" fn inner(args: *const RTCDisplacementFunctionNArguments) where @@ -2196,6 +2221,7 @@ where p_x: (*args).P_x, p_y: (*args).P_y, p_z: (*args).P_z, + marker: PhantomData, }; cb( (*args).geometry, diff --git a/src/lib.rs b/src/lib.rs index f1a0df7a9..c2cce9253 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,8 @@ //! See the [examples/](https://github.com/Twinklebear/embree-rs/tree/master/examples) //! for some example applications using the bindings. +extern crate core; + use std::{alloc, mem}; mod buffer; diff --git a/src/ray/packet.rs b/src/ray/packet.rs index 1ce9c066a..bdd0a2b69 100644 --- a/src/ray/packet.rs +++ b/src/ray/packet.rs @@ -1,6 +1,8 @@ use crate::{ sys, Hit, Ray, RayHit, SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut, + INVALID_ID, }; +use std::marker::PhantomData; mod sealed { pub trait Sealed {} @@ -84,11 +86,42 @@ macro_rules! impl_ray_packets { ($($t:ident, $n:expr);*) => { $( impl $t { - pub fn new(origin: [[f32; 3]; $n], dir: [[f32; 3]; $n]) -> $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 fn empty() -> $t { + 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], @@ -101,262 +134,107 @@ macro_rules! impl_ray_packets { pub fn iter_mut(&mut self) -> SoARayIterMut<$t> { SoARayIterMut::new(self, $n) } } - )* - }; -} - -impl Ray4 { - 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, - tfar, - time: [0.0; 4], - mask: [u32::MAX; 4], - id: [0; 4], - flags: [0; 4], - } - } -} -impl Ray8 { - pub fn segment( - origin: [[f32; 3]; 8], - dir: [[f32; 3]; 8], - tnear: [f32; 8], - tfar: [f32; 8], - ) -> Ray8 { - Ray8 { - org_x: [ - origin[0][0], - origin[1][0], - origin[2][0], - origin[3][0], - origin[4][0], - origin[5][0], - origin[6][0], - origin[7][0], - ], - org_y: [ - origin[0][1], - origin[1][1], - origin[2][1], - origin[3][1], - origin[4][1], - origin[5][1], - origin[6][1], - origin[7][1], - ], - org_z: [ - origin[0][2], - origin[1][2], - origin[2][2], - origin[3][2], - origin[4][2], - origin[5][2], - origin[6][2], - origin[7][2], - ], - dir_x: [ - dir[0][0], dir[1][0], dir[2][0], dir[3][0], dir[4][0], dir[5][0], dir[6][0], - dir[7][0], - ], - dir_y: [ - dir[0][1], dir[1][1], dir[2][1], dir[3][1], dir[4][1], dir[5][1], dir[6][1], - dir[7][1], - ], - dir_z: [ - dir[0][2], dir[1][2], dir[2][2], dir[3][2], dir[4][2], dir[5][2], dir[6][2], - dir[7][2], - ], - tnear, - tfar, - time: [0.0; 8], - mask: [u32::MAX; 8], - id: [0; 8], - flags: [0; 8], - } - } -} + impl Default for $t { + fn default() -> Self { Self::empty() } + } -impl Ray16 { - pub fn segment( - origin: [[f32; 3]; 16], - dir: [[f32; 3]; 16], - tnear: [f32; 16], - tfar: [f32; 16], - ) -> Ray16 { - Ray16 { - org_x: [ - origin[0][0], - origin[1][0], - origin[2][0], - origin[3][0], - origin[4][0], - origin[5][0], - origin[6][0], - origin[7][0], - origin[8][0], - origin[9][0], - origin[10][0], - origin[11][0], - origin[12][0], - origin[13][0], - origin[14][0], - origin[15][0], - ], - org_y: [ - origin[0][1], - origin[1][1], - origin[2][1], - origin[3][1], - origin[4][1], - origin[5][1], - origin[6][1], - origin[7][1], - origin[8][1], - origin[9][1], - origin[10][1], - origin[11][1], - origin[12][1], - origin[13][1], - origin[14][1], - origin[15][1], - ], - org_z: [ - origin[0][2], - origin[1][2], - origin[2][2], - origin[3][2], - origin[4][2], - origin[5][2], - origin[6][2], - origin[7][2], - origin[8][2], - origin[9][2], - origin[10][2], - origin[11][2], - origin[12][2], - origin[13][2], - origin[14][2], - origin[15][2], - ], - dir_x: [ - dir[0][0], dir[1][0], dir[2][0], dir[3][0], dir[4][0], dir[5][0], dir[6][0], - dir[7][0], dir[8][0], dir[9][0], dir[10][0], dir[11][0], dir[12][0], dir[13][0], - dir[14][0], dir[15][0], - ], - dir_y: [ - dir[0][1], dir[1][1], dir[2][1], dir[3][1], dir[4][1], dir[5][1], dir[6][1], - dir[7][1], dir[8][1], dir[9][1], dir[10][1], dir[11][1], dir[12][1], dir[13][1], - dir[14][1], dir[15][1], - ], - dir_z: [ - dir[0][2], dir[1][2], dir[2][2], dir[3][2], dir[4][2], dir[5][2], dir[6][2], - dir[7][2], dir[8][2], dir[9][2], dir[10][2], dir[11][2], dir[12][2], dir[13][2], - dir[14][2], dir[15][2], - ], - tnear, - tfar, - time: [0.0; 16], - mask: [u32::MAX; 16], - id: [0; 16], - flags: [0; 16], - } - } -} + 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]; + } -impl_ray_packets!(Ray4, 4); + 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]; + } -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 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 tfar(&self, i: usize) -> f32 { self.tfar[i] } + fn set_tfar(&mut self, i: usize, t: f32) { self.tfar[i] = t} - fn tnear(&self, i: usize) -> f32 { self.tnear[i] } - fn set_tnear(&mut self, i: usize, near: f32) { self.tnear[i] = near; } + 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, far: f32) { self.tfar[i] = far; } + fn mask(&self, i: usize) -> u32 { self.mask[i] } + fn set_mask(&mut self, i: usize, m: u32) { self.mask[i] = m } - fn time(&self, i: usize) -> f32 { self.time[i] } - fn set_time(&mut self, i: usize, time: f32) { self.time[i] = time; } + fn id(&self, i: usize) -> u32 { self.id[i] } + fn set_id(&mut self, i: usize, id: u32) { self.id[i] = id } - fn mask(&self, i: usize) -> u32 { self.mask[i] } - fn set_mask(&mut self, i: usize, mask: u32) { self.mask[i] = mask; } + fn flags(&self, i: usize) -> u32 { self.flags[i] } + fn set_flags(&mut self, i: usize, f: u32) { self.flags[i] = f } + } + )* + }; +} - fn id(&self, i: usize) -> u32 { self.id[i] } - fn set_id(&mut self, i: usize, id: u32) { self.id[i] = id; } +impl_ray_packets!(Ray4, 4; Ray8, 8; Ray16, 16); - fn flags(&self, i: usize) -> u32 { self.flags[i] } - fn set_flags(&mut self, i: usize, flags: u32) { self.flags[i] = flags; } -} +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.hits().any(|h| h) } + pub fn hits(&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.hit()) + } + } -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().any(|h| h) } - 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 Default for $t { + fn default() -> Self { Self::new() } + } -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]; - } + impl SoAHit for $t { + 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 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 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 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; } + 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 { @@ -374,71 +252,156 @@ impl RayHit4 { /// 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. -pub struct RayN { +pub struct RayN<'a> { pub(crate) ptr: *mut sys::RTCRayN, pub(crate) len: usize, + pub(crate) marker: PhantomData<&'a mut sys::RTCRayN>, } -impl RayN { - pub fn org_x(&self, i: usize) -> f32 { +impl<'a> RayN<'a> { + pub const fn org_x(&self, i: usize) -> f32 { debug_assert!(i < self.len, "index out of bounds"); unsafe { *(self.ptr as *const f32).add(i) } } - pub fn org_y(&self, i: usize) -> f32 { + pub fn set_org_x(&mut self, i: usize, x: f32) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + *(self.ptr as *mut f32).add(i) = x; + } + } + + pub const fn org_y(&self, i: usize) -> f32 { debug_assert!(i < self.len, "index out of bounds"); unsafe { *(self.ptr as *const f32).add(self.len + i) } } - pub fn org_z(&self, i: usize) -> f32 { + pub fn set_org_y(&mut self, i: usize, y: f32) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + *(self.ptr as *mut f32).add(self.len + i) = y; + } + } + + pub const fn org_z(&self, i: usize) -> f32 { debug_assert!(i < self.len, "index out of bounds"); unsafe { *(self.ptr as *const f32).add(2 * self.len + i) } } - pub fn tnear(&self, i: usize) -> f32 { + pub fn set_org_z(&mut self, i: usize, z: f32) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + *(self.ptr as *mut f32).add(2 * self.len + i) = z; + } + } + + pub const 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) } } - pub fn dir_x(&self, i: usize) -> f32 { + pub 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; + } + } + + pub const fn dir_x(&self, i: usize) -> f32 { debug_assert!(i < self.len, "index out of bounds"); unsafe { *(self.ptr as *const f32).add(4 * self.len + i) } } - pub fn dir_y(&self, i: usize) -> f32 { + pub fn set_dir_x(&mut self, i: usize, x: f32) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + *(self.ptr as *mut f32).add(4 * self.len + i) = x; + } + } + + pub const fn dir_y(&self, i: usize) -> f32 { debug_assert!(i < self.len, "index out of bounds"); unsafe { *(self.ptr as *const f32).add(5 * self.len + i) } } - pub fn dir_z(&self, i: usize) -> f32 { + pub fn set_dir_y(&mut self, i: usize, y: f32) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + *(self.ptr as *mut f32).add(5 * self.len + i) = y; + } + } + + pub const fn dir_z(&self, i: usize) -> f32 { debug_assert!(i < self.len, "index out of bounds"); unsafe { *(self.ptr as *const f32).add(6 * self.len + i) } } - pub fn time(&self, i: usize) -> f32 { + pub fn set_dir_z(&mut self, i: usize, z: f32) { + debug_assert!(i < self.len, "index out of bounds"); + unsafe { + *(self.ptr as *mut f32).add(6 * self.len + i) = z; + } + } + + pub const 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) } } - pub fn tfar(&self, i: usize) -> f32 { + pub 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; + } + } + + pub const 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) } } - pub fn mask(&self, i: usize) -> u32 { + pub 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; + } + } + + pub const 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) } } - pub fn id(&self, i: usize) -> u32 { + pub 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; + } + } + + pub const 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) } } - pub fn flags(&self, i: usize) -> u32 { + pub 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; + } + } + + pub const 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) } } + + pub 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. @@ -446,48 +409,49 @@ impl RayN { /// 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 { +pub struct HitN<'a> { pub(crate) ptr: *mut sys::RTCHitN, pub(crate) len: usize, + pub(crate) marker: PhantomData<&'a mut sys::RTCHitN>, } -impl HitN { - pub fn ng_x(&self, i: usize) -> f32 { +impl<'a> HitN<'a> { + pub const fn ng_x(&self, i: usize) -> f32 { debug_assert!(i < self.len, "index out of bounds"); unsafe { *(self.ptr as *const f32).add(i) } } - pub fn ng_y(&self, i: usize) -> f32 { + pub const fn ng_y(&self, i: usize) -> f32 { debug_assert!(i < self.len, "index out of bounds"); unsafe { *(self.ptr as *const f32).add(self.len + i) } } - pub fn ng_z(&self, i: usize) -> f32 { + pub const fn ng_z(&self, i: usize) -> f32 { debug_assert!(i < self.len, "index out of bounds"); unsafe { *(self.ptr as *const f32).add(2 * self.len + i) } } - pub fn u(&self, i: usize) -> f32 { + pub const 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) } } - pub fn v(&self, i: usize) -> f32 { + pub const 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) } } - pub fn prim_id(&self, i: usize) -> u32 { + pub const 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) } } - pub fn geom_id(&self, i: usize) -> u32 { + pub const 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) } } - pub fn inst_id(&self, i: usize) -> u32 { + pub const 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) } } @@ -496,25 +460,28 @@ impl HitN { /// 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 { +pub struct RayHitN<'a> { pub(crate) ptr: *mut sys::RTCRayHitN, pub(crate) len: usize, + pub(crate) marker: PhantomData<&'a mut sys::RTCRayHitN>, } -impl RayHitN { +impl<'a> RayHitN<'a> { /// Returns the ray packet. - pub fn ray_n(&self) -> RayN { + 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(&self) -> HitN { + 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/ray/soa.rs b/src/ray/soa.rs index 10d597a48..dfc859421 100644 --- a/src/ray/soa.rs +++ b/src/ray/soa.rs @@ -1,7 +1,6 @@ use std::{ iter::{ExactSizeIterator, Iterator}, marker::PhantomData, - u32, }; pub trait SoARay { diff --git a/src/ray/stream.rs b/src/ray/stream.rs index 21d7ee732..24c863253 100644 --- a/src/ray/stream.rs +++ b/src/ray/stream.rs @@ -1,12 +1,36 @@ -use std::{f32, iter::Iterator, u32}; +use core::alloc; +use std::{iter::Iterator, marker::PhantomData, ptr::NonNull}; use crate::{ aligned_vector, aligned_vector_init, sys, SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut, }; +// struct RayNp2 { +// ptr: NonNull, +// len: usize, +// marker: PhantomData, +// } +// +// impl RayNp2 { +// pub fn new(n: usize) -> Self { +// unsafe { +// let layout = alloc::Layout::from_size_align(48 * n, 16).unwrap(); +// let ptr = match NonNull::new(alloc::alloc_zeroed(layout) as *mut +// u8) { Some(ptr) => ptr, +// None => alloc::handle_alloc_error(layout), +// }; +// RayNp2 { +// ptr, +// len: n, +// marker: PhantomData, +// } +// } +// } +// } + /// A ray stream stored in SoA format -pub struct RayStream { +pub struct RayNp { org_x: Vec, org_y: Vec, org_z: Vec, @@ -21,10 +45,10 @@ pub struct RayStream { flags: Vec<::std::os::raw::c_uint>, } -impl RayStream { +impl RayNp { /// Allocate a new Ray stream with room for `n` rays - pub fn new(n: usize) -> RayStream { - RayStream { + pub fn new(n: usize) -> RayNp { + RayNp { org_x: aligned_vector::(n, 16), org_y: aligned_vector::(n, 16), org_z: aligned_vector::(n, 16), @@ -39,8 +63,8 @@ impl RayStream { flags: aligned_vector_init::(n, 16, 0), } } - pub fn iter(&self) -> SoARayIter { SoARayIter::new(self, self.len()) } - pub fn iter_mut(&mut self) -> SoARayIterMut { + 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) } @@ -64,7 +88,7 @@ impl RayStream { } } -impl SoARay for RayStream { +impl SoARay for RayNp { 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]; @@ -98,7 +122,7 @@ impl SoARay for RayStream { fn set_flags(&mut self, i: usize, flags: u32) { self.flags[i] = flags; } } -pub struct HitStream { +pub struct HitNp { ng_x: Vec, ng_y: Vec, ng_z: Vec, @@ -109,9 +133,9 @@ pub struct HitStream { inst_id: Vec<::std::os::raw::c_uint>, } -impl HitStream { - pub fn new(n: usize) -> HitStream { - HitStream { +impl HitNp { + pub fn new(n: usize) -> HitNp { + HitNp { ng_x: aligned_vector::(n, 16), ng_y: aligned_vector::(n, 16), ng_z: aligned_vector::(n, 16), @@ -122,15 +146,16 @@ impl HitStream { 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 { + pub fn any_hit(&self) -> bool { self.hits().any(|g| g) } + pub fn hits(&self) -> impl Iterator + '_ { 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 { + 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.hit()) } pub fn len(&self) -> usize { self.ng_x.len() } + pub fn empty(&self) -> bool { self.len() == 0 } pub unsafe fn as_hitnp(&mut self) -> sys::RTCHitNp { sys::RTCHitNp { Ng_x: self.ng_x.as_mut_ptr(), @@ -145,7 +170,7 @@ impl HitStream { } } -impl SoAHit for HitStream { +impl SoAHit for HitNp { 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]; @@ -167,23 +192,24 @@ impl SoAHit for HitStream { fn set_inst_id(&mut self, i: usize, id: u32) { self.inst_id[i] = id; } } -pub struct RayHitStream { - pub ray: RayStream, - pub hit: HitStream, +pub struct RayHitNp { + pub ray: RayNp, + pub hit: HitNp, } -impl RayHitStream { - pub fn new(ray: RayStream) -> RayHitStream { +impl RayHitNp { + pub fn new(ray: RayNp) -> RayHitNp { let n = ray.len(); - RayHitStream { + RayHitNp { ray, - hit: HitStream::new(n), + hit: HitNp::new(n), } } - pub fn iter(&self) -> std::iter::Zip, SoAHitIter> { + 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 empty(&self) -> bool { self.len() == 0 } pub unsafe fn as_rayhitnp(&mut self) -> sys::RTCRayHitNp { sys::RTCRayHitNp { ray: self.ray.as_raynp(), diff --git a/src/scene.rs b/src/scene.rs index b206b7319..f40777f90 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,6 +1,6 @@ use crate::{ - Bounds, Error, PointQuery, PointQueryContext, Ray16, Ray8, RayHit16, RayHit8, RayHitPacket, - RayHitStream, RayPacket, SceneFlags, + Bounds, Error, PointQuery, PointQueryContext, Ray16, Ray8, RayHit16, RayHit8, RayHitNp, + RayHitPacket, RayPacket, SceneFlags, }; use std::{ any::TypeId, @@ -13,7 +13,7 @@ use crate::{ context::IntersectContext, device::Device, geometry::Geometry, - ray::{Ray, Ray4, RayHit, RayHit4, RayStream}, + ray::{Ray, Ray4, RayHit, RayHit4, RayNp}, sys::*, }; @@ -808,7 +808,7 @@ impl<'a> Scene<'a> { /// /// 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 IntersectContext, rays: &mut RayHitStream) { + pub fn intersect_stream_soa(&self, ctx: &mut IntersectContext, rays: &mut RayHitNp) { let n = rays.len(); unsafe { let mut rayhit = rays.as_rayhitnp(); @@ -833,7 +833,7 @@ impl<'a> Scene<'a> { /// /// 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 IntersectContext, rays: &mut RayStream) { + pub fn occluded_stream_soa(&self, ctx: &mut IntersectContext, rays: &mut RayNp) { let n = rays.len(); unsafe { let mut r = rays.as_raynp(); From 405390c628681dda43708a603cf2e3a9d0cd0a5f Mon Sep 17 00:00:00 2001 From: matthiascy Date: Mon, 27 Feb 2023 15:33:16 +0100 Subject: [PATCH 48/65] remove geomtry directory & update workflows to use nightly toolchain --- .github/workflows/main.yml | 15 ++- src/geometry/bezier_curve.rs | 145 ------------------------ src/geometry/bspline_curve.rs | 145 ------------------------ src/geometry/catmull_rom_curve.rs | 149 ------------------------- src/geometry/curve.rs | 6 - src/geometry/hermite_curve.rs | 178 ------------------------------ src/geometry/linear_curve.rs | 160 --------------------------- 7 files changed, 11 insertions(+), 787 deletions(-) delete mode 100644 src/geometry/bezier_curve.rs delete mode 100644 src/geometry/bspline_curve.rs delete mode 100644 src/geometry/catmull_rom_curve.rs delete mode 100644 src/geometry/curve.rs delete mode 100644 src/geometry/hermite_curve.rs delete mode 100644 src/geometry/linear_curve.rs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0e83edc8c..e1af8a3d6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,6 +4,17 @@ env: CARGO_TERM_COLOR: always EMBREE_VERSION: 3.13.5 jobs: + format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + - name: Format Core + run: cargo fmt -- --check + - name: Format Examples + run: scripts/check-examples-formatting.sh build_linux: runs-on: ubuntu-latest steps: @@ -19,10 +30,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: diff --git a/src/geometry/bezier_curve.rs b/src/geometry/bezier_curve.rs deleted file mode 100644 index df7e4931d..000000000 --- a/src/geometry/bezier_curve.rs +++ /dev/null @@ -1,145 +0,0 @@ -use std::sync::Arc; - -use crate::buffer::Buffer; -use crate::device::Device; -use crate::sys::*; -use crate::{BufferType, CurveType, Format, GeometryType}; - -pub struct BezierCurve { - device: Device, - pub(crate) handle: RTCGeometry, - pub vertex_buffer: Buffer<[f32; 4]>, - pub index_buffer: Buffer, - pub normal_buffer: Option>, -} - -impl BezierCurve { - pub fn flat( - device: Device, - 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: Device, - 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: Device, - num_segments: usize, - num_verts: usize, - ) -> Arc { - BezierCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::NormalOriented, - true, - ) - } - - fn unanimated( - device: Device, - 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 GeometryTrait for BezierCurve { - fn handle(&self) -> RTCGeometry { - self.handle - } -} - -impl Drop for BezierCurve { - fn drop(&mut self) { - unsafe { - rtcReleaseGeometry(self.handle); - } - } -} diff --git a/src/geometry/bspline_curve.rs b/src/geometry/bspline_curve.rs deleted file mode 100644 index b048d97c2..000000000 --- a/src/geometry/bspline_curve.rs +++ /dev/null @@ -1,145 +0,0 @@ -use std::sync::Arc; - -use crate::buffer::Buffer; -use crate::device::Device; -use crate::sys::*; -use crate::{BufferType, CurveType, Format, GeometryType}; - -pub struct BsplineCurve { - device: Device, - pub(crate) handle: RTCGeometry, - pub vertex_buffer: Buffer<[f32; 4]>, - pub index_buffer: Buffer, - pub normal_buffer: Option>, -} - -impl BsplineCurve { - pub fn flat( - device: Device, - 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: Device, - 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: Device, - num_segments: usize, - num_verts: usize, - ) -> Arc { - BsplineCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::NormalOriented, - true, - ) - } - - fn unanimated( - device: Device, - 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 GeometryTrait for BsplineCurve { - fn handle(&self) -> RTCGeometry { - self.handle - } -} - -impl Drop for BsplineCurve { - fn drop(&mut self) { - unsafe { - rtcReleaseGeometry(self.handle); - } - } -} diff --git a/src/geometry/catmull_rom_curve.rs b/src/geometry/catmull_rom_curve.rs deleted file mode 100644 index 9d136a095..000000000 --- a/src/geometry/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::GeometryTrait; -use crate::sys::*; -use crate::{BufferType, CurveType, Format, GeometryType}; - -pub struct CatmullRomCurve { - device: Device, - pub(crate) handle: RTCGeometry, - pub vertex_buffer: Buffer<[f32; 4]>, - pub index_buffer: Buffer, - pub normal_buffer: Option>, -} - -impl CatmullRomCurve { - pub fn flat( - device: Device, - 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: Device, - 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: Device, - num_segments: usize, - num_verts: usize, - ) -> Arc { - CatmullRomCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::NormalOriented, - true, - ) - } - - fn unanimated( - device: Device, - 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 GeometryTrait for CatmullRomCurve { - fn handle(&self) -> RTCGeometry { - self.handle - } -} - -impl Drop for CatmullRomCurve { - fn drop(&mut self) { - unsafe { - rtcReleaseGeometry(self.handle); - } - } -} diff --git a/src/geometry/curve.rs b/src/geometry/curve.rs deleted file mode 100644 index f55237965..000000000 --- a/src/geometry/curve.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub enum CurveType { - Flat, - NormalOriented, - Round, - Cone, -} diff --git a/src/geometry/hermite_curve.rs b/src/geometry/hermite_curve.rs deleted file mode 100644 index e335554e5..000000000 --- a/src/geometry/hermite_curve.rs +++ /dev/null @@ -1,178 +0,0 @@ -use std::sync::Arc; - -use crate::buffer::Buffer; -use crate::device::Device; -use crate::geometry::GeometryTrait; -use crate::sys::*; -use crate::{BufferType, CurveType, Format, GeometryType}; - -pub struct HermiteCurve { - device: Device, - 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: Device, - 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: Device, - 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: Device, - num_segments: usize, - num_verts: usize, - ) -> Arc { - HermiteCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::NormalOriented, - true, - ) - } - - fn unanimated( - device: Device, - 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 GeometryTrait for HermiteCurve { - fn handle(&self) -> RTCGeometry { - self.handle - } -} - -impl Drop for HermiteCurve { - fn drop(&mut self) { - unsafe { - rtcReleaseGeometry(self.handle); - } - } -} diff --git a/src/geometry/linear_curve.rs b/src/geometry/linear_curve.rs deleted file mode 100644 index 5e60d2b21..000000000 --- a/src/geometry/linear_curve.rs +++ /dev/null @@ -1,160 +0,0 @@ -use std::sync::Arc; - -use crate::buffer::Buffer; -use crate::device::Device; -use crate::geometry::GeometryTrait; -use crate::sys::*; -use crate::{BufferType, CurveType, Format, GeometryType}; - -pub struct LinearCurve { - device: Device, - 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: Device, - 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: Device, - 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: Device, - num_segments: usize, - num_verts: usize, - use_normals: bool, - ) -> Arc { - LinearCurve::unanimated( - device, - num_segments, - num_verts, - CurveType::Cone, - use_normals, - ) - } - fn unanimated( - device: Device, - 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 GeometryTrait for LinearCurve { - fn handle(&self) -> RTCGeometry { - self.handle - } -} - -impl Drop for LinearCurve { - fn drop(&mut self) { - unsafe { - rtcReleaseGeometry(self.handle); - } - } -} From 7d94a36197b8cb0c96e3472c41857f3f38cbdcec Mon Sep 17 00:00:00 2001 From: matthiascy Date: Mon, 27 Feb 2023 15:37:33 +0100 Subject: [PATCH 49/65] update code formatting task --- .github/workflows/main.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e1af8a3d6..589818869 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,13 +4,15 @@ env: CARGO_TERM_COLOR: always EMBREE_VERSION: 3.13.5 jobs: - format: + 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 From 83da23d46b14c109db897a8b45dc141054a215af Mon Sep 17 00:00:00 2001 From: matthiascy Date: Tue, 28 Feb 2023 11:00:37 +0100 Subject: [PATCH 50/65] update examples --- examples/displacement_geometry/Cargo.toml | 2 +- examples/intersection_filter/Cargo.toml | 10 ++ examples/intersection_filter/src/main.rs | 34 ++++ examples/support/src/camera.rs | 10 +- examples/support/src/common.rs | 9 ++ examples/support/src/display.rs | 14 +- examples/support/src/lib.rs | 2 + examples/triangle_geometry/Cargo.toml | 1 + examples/triangle_geometry/src/main.rs | 183 +++++++++++++++------- src/ray/packet.rs | 2 + 10 files changed, 200 insertions(+), 67 deletions(-) create mode 100644 examples/intersection_filter/Cargo.toml create mode 100644 examples/intersection_filter/src/main.rs diff --git a/examples/displacement_geometry/Cargo.toml b/examples/displacement_geometry/Cargo.toml index 501a51951..1418d7faf 100644 --- a/examples/displacement_geometry/Cargo.toml +++ b/examples/displacement_geometry/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" edition = "2021" [dependencies] -glam = "0.22.0" +glam = "0.23.0" embree = { path = "../.." } support = { path = "../support" } rayon = { version = "1.6.1", optional = true } 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..e5a161a7b --- /dev/null +++ b/examples/intersection_filter/src/main.rs @@ -0,0 +1,34 @@ +//! 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::Ray; +use glam::Vec3; + +const HIT_LIST_LEN: usize = 16; + +// Extended ray structure that includes total transparency along the ray. +struct Ray2 { + ray: Ray, + 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], +} + +fn transparency_function(h: Vec3) -> f32 { + let v = ((4.0 * h.x).sin() * (4.0 * h.y).cos() * (4.0 * h.z).sin()).abs(); + ((v - 0.1) * 3.0).clamp(0.0, 1.0) +} + +fn main() { + println!("Hello, world!"); +} diff --git a/examples/support/src/camera.rs b/examples/support/src/camera.rs index 026587f28..debf94dcc 100644 --- a/examples/support/src/camera.rs +++ b/examples/support/src/camera.rs @@ -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 index 4bf96faca..3d6dd4caa 100644 --- a/examples/support/src/common.rs +++ b/examples/support/src/common.rs @@ -1,3 +1,12 @@ +/// 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, diff --git a/examples/support/src/display.rs b/examples/support/src/display.rs index 819732bd9..48b8f27ee 100644 --- a/examples/support/src/display.rs +++ b/examples/support/src/display.rs @@ -104,13 +104,13 @@ 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, } } } diff --git a/examples/support/src/lib.rs b/examples/support/src/lib.rs index 96ebb1a9a..4c824f43f 100644 --- a/examples/support/src/lib.rs +++ b/examples/support/src/lib.rs @@ -20,6 +20,8 @@ pub use common::*; pub use camera::Camera; pub use display::Display; +pub use image::{Rgba, RgbaImage}; + /// Clamp `x` to be between `min` and `max` pub fn clamp(x: T, min: T, max: T) -> T { if x < min { diff --git a/examples/triangle_geometry/Cargo.toml b/examples/triangle_geometry/Cargo.toml index 53589204a..417887e01 100644 --- a/examples/triangle_geometry/Cargo.toml +++ b/examples/triangle_geometry/Cargo.toml @@ -8,4 +8,5 @@ edition = "2021" glam = "0.22.0" embree = { path = "../.." } support = { path = "../support" } +rayon = "1.5" diff --git a/examples/triangle_geometry/src/main.rs b/examples/triangle_geometry/src/main.rs index dc1ad4df7..fc6bb7e23 100644 --- a/examples/triangle_geometry/src/main.rs +++ b/examples/triangle_geometry/src/main.rs @@ -4,9 +4,11 @@ extern crate embree; extern crate support; use embree::{ - BufferSlice, BufferUsage, Device, Format, IntersectContext, QuadMesh, Ray, TriangleMesh, + BufferSlice, BufferUsage, Device, Format, IntersectContext, QuadMesh, Ray, Scene, TriangleMesh, + INVALID_ID, }; -use support::Camera; +use glam::Vec3; +use support::{Camera, Rgba, RgbaImage, TILE_SIZE, TILE_SIZE_X, TILE_SIZE_Y}; fn make_cube(device: &Device, vertex_colors: &[[f32; 3]]) -> TriangleMesh<'static> { let mut mesh = TriangleMesh::new(device).unwrap(); @@ -88,6 +90,14 @@ fn make_ground_plane(device: &Device) -> QuadMesh<'static> { mesh } +struct State { + ground_id: u32, + cube_id: u32, + face_colors: Vec<[f32; 3]>, + light_dir: Vec3, + scene: Scene<'static>, +} + fn main() { let display = support::Display::new(512, 512, "triangle geometry"); let device = Device::new().unwrap(); @@ -105,37 +115,39 @@ fn main() { [1.0, 1.0, 1.0], ]; - let 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], - ]; + let mut state = State { + 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(), + scene: device.create_scene().unwrap(), + }; let cube = make_cube(&device, &vertex_colors); let ground = make_ground_plane(&device); + state.cube_id = state.scene.attach_geometry(&cube); + state.ground_id = state.scene.attach_geometry(&ground); + state.scene.commit(); - let mut scene = device.create_scene().unwrap(); - let _ = scene.attach_geometry(&cube); - let ground_id = scene.attach_geometry(&ground); - scene.commit(); - - let mut intersection_ctx = IntersectContext::coherent(); - - let light_dir = glam::vec3(1.0, 1.0, 1.0).normalize(); - - support::display::run(display, move |image, camera_pose, _| { + let mut last_time = 0.0; + support::display::run(display, move |image, camera_pose, time| { for p in image.iter_mut() { *p = 0; } + let img_dims = image.dimensions(); let camera = Camera::look_dir( camera_pose.pos, @@ -144,38 +156,101 @@ fn main() { 75.0, img_dims, ); + + //render_frame(image, time, &camera, &state); + // 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_hit = scene.intersect( - &mut intersection_ctx, - Ray::new(camera.pos.into(), dir.into()), - ); - if ray_hit.is_valid() { - let p = image.get_pixel_mut(i, j); - let diffuse = if ray_hit.hit.geomID == ground_id { - glam::vec3(0.6, 0.6, 0.6) - } else { - glam::Vec3::from(face_colors[ray_hit.hit.primID as usize]) - }; - - let mut shadow_ray = - Ray::segment(ray_hit.hit_point(), light_dir.into(), 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 - }; - - // Write the color to the image. - p[0] = (color.x * 255.0) as u8; - p[1] = (color.y * 255.0) as u8; - p[2] = (color.z * 255.0) as u8; - } + render_pixel(i, j, &mut image.get_pixel_mut(i, j), time, &camera, &state); } } + + let elapsed = time - last_time; + last_time = time; + let fps = 1.0 / elapsed; + eprint!("\r{} fps", fps); + }); +} + +// Task that renders a single pixel. +fn render_pixel(x: u32, y: u32, pixel: &mut Rgba, _time: f32, camera: &Camera, state: &State) { + let mut ctx = IntersectContext::coherent(); + let dir = camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)); + let ray_hit = state + .scene + .intersect(&mut ctx, Ray::new(camera.pos.into(), dir.into())); + if ray_hit.is_valid() { + let diffuse = if ray_hit.hit.geomID == state.ground_id { + glam::vec3(0.6, 0.6, 0.6) + } else { + glam::Vec3::from(state.face_colors[ray_hit.hit.primID as usize]) + }; + + let mut shadow_ray = Ray::segment( + ray_hit.hit_point(), + state.light_dir.into(), + 0.001, + f32::INFINITY, + ); + + // Check if the shadow ray is occluded. + let color = if !state.scene.occluded(&mut ctx, &mut shadow_ray) { + diffuse + } else { + diffuse * 0.5 + }; + + // Write the color to the image. + pixel[0] = (color.x * 255.0) as u8; + pixel[1] = (color.y * 255.0) as u8; + pixel[2] = (color.z * 255.0) as u8; + } +} + +fn render_tile( + tile_idx: u32, + num_tiles_x: u32, + num_tiles_y: u32, + width: u32, + height: u32, + time: f32, + camera: &Camera, + state: &State, + image: &mut RgbaImage, +) { + let title_y = tile_idx / num_tiles_x; + let tile_x = tile_idx % num_tiles_x; + let x0 = tile_x * TILE_SIZE_X; + let y0 = title_y * TILE_SIZE_Y; + let x1 = (x0 + TILE_SIZE_X).min(width); + let y1 = (y0 + TILE_SIZE_Y).min(height); + + for y in y0..y1 { + for x in x0..x1 { + render_pixel(x, y, &mut image.get_pixel_mut(x, y), time, &camera, &state); + } + } +} + +fn render_frame(image: &mut RgbaImage, time: f32, camera: &Camera, state: &State) { + use rayon::prelude::*; + let img_dims = image.dimensions(); + let num_tiles_x = (img_dims.0 + TILE_SIZE_X - 1) / TILE_SIZE_X; + let num_tiles_y = (img_dims.1 + TILE_SIZE_Y - 1) / TILE_SIZE_Y; + let num_tiles = num_tiles_x * num_tiles_y; + + (0..num_tiles).into_par_iter().for_each(|tile_idx| { + render_tile( + tile_idx, + num_tiles_x, + num_tiles_y, + img_dims.0, + img_dims.1, + time, + camera, + state, + image, + ); }); } diff --git a/src/ray/packet.rs b/src/ray/packet.rs index bdd0a2b69..f0ce6d01b 100644 --- a/src/ray/packet.rs +++ b/src/ray/packet.rs @@ -252,6 +252,8 @@ impl RayHit4 { /// 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, From a7822cdfa8d073cdda687a92b0cab2918665b57c Mon Sep 17 00:00:00 2001 From: matthiascy Date: Tue, 28 Feb 2023 18:17:10 +0100 Subject: [PATCH 51/65] tiled image --- examples/dynamic_scene/Cargo.toml | 5 - examples/dynamic_scene/src/main.rs | 149 +++++++++++++++---------- examples/support/Cargo.toml | 1 + examples/support/src/common.rs | 7 +- examples/support/src/lib.rs | 127 ++++++++++++++++++--- examples/triangle_geometry/Cargo.toml | 5 +- examples/triangle_geometry/src/main.rs | 86 +++++--------- src/ray/stream.rs | 3 +- 8 files changed, 236 insertions(+), 147 deletions(-) diff --git a/examples/dynamic_scene/Cargo.toml b/examples/dynamic_scene/Cargo.toml index e7168ebce..6742709cb 100644 --- a/examples/dynamic_scene/Cargo.toml +++ b/examples/dynamic_scene/Cargo.toml @@ -8,9 +8,4 @@ edition = "2021" glam = "0.22.0" embree = { path = "../.." } support = { path = "../support" } -rayon = { version = "1.6.1", optional = true } - -[features] -default = ["rayon"] -rayon = ["dep:rayon"] diff --git a/examples/dynamic_scene/src/main.rs b/examples/dynamic_scene/src/main.rs index 9b1ca2e4f..4dbfe1024 100644 --- a/examples/dynamic_scene/src/main.rs +++ b/examples/dynamic_scene/src/main.rs @@ -3,19 +3,22 @@ use embree::{ BufferUsage, BuildQuality, Device, Format, Geometry, IntersectContext, Ray, Scene, SceneFlags, }; -use glam::{vec3, Vec3}; -use support::Camera; +use glam::Vec3; +use support::{ + rgba_to_u32, Camera, IndexedParallelIterator, ParallelIterator, ParallelSliceMut, RgbaImage, + TiledImage, DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH, TILE_SIZE_X, TILE_SIZE_Y, +}; const NUM_SPHERES: usize = 20; const NUM_PHI: usize = 120; const NUM_THETA: usize = 2 * NUM_PHI; -fn create_sphere<'a, 'b>( - device: &'a Device, +fn create_sphere<'a>( + device: &Device, quality: BuildQuality, pos: Vec3, radius: f32, -) -> Geometry<'b> { +) -> Geometry<'a> { // Create a triangulated sphere let mut geometry = device .create_geometry(embree::GeometryKind::TRIANGLE) @@ -85,7 +88,7 @@ fn create_sphere<'a, 'b>( geometry } -fn create_ground_plane<'a, 'b>(device: &'a Device) -> Geometry<'b> { +fn create_ground_plane<'a>(device: &Device) -> Geometry<'a> { let mut geometry = Geometry::new(device, embree::GeometryKind::TRIANGLE).unwrap(); { geometry @@ -121,39 +124,24 @@ fn animate_sphere(scene: &Scene, id: u32, pos: Vec3, radius: f32, time: f32) { let num_phi_rcp = 1.0 / NUM_PHI as f32; let f = 2.0 * (1.0 + 0.5 * time.sin()); - #[cfg(feature = "rayon")] - { - use rayon::prelude::*; - 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(); - } - }); - } - #[cfg(not(feature = "rayon"))] - { - for phi_idx in 0..NUM_PHI { - for theta_idx in 0..NUM_THETA { - let phi = phi_idx as f32 * num_phi_rcp * std::f32::consts::PI; + 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; - let mut v = vertices[phi_idx * NUM_THETA + theta_idx]; 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| { @@ -191,8 +179,14 @@ fn main() { colors[id as usize] = Vec3::new(1.0, 1.0, 1.0); scene.commit(); + let mut last_time = 0.0; let display = support::Display::new(512, 512, "Dynamic Scene"); - let light_dir = vec3(1.0, 1.0, 1.0).normalize(); + let mut tiled = TiledImage::new( + DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT, + TILE_SIZE_X, + TILE_SIZE_Y, + ); support::display::run(display, move |image, camera_pose, time| { for p in image.iter_mut() { *p = 0; @@ -211,36 +205,67 @@ fn main() { } scene.commit(); - // 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 intersection_ctx = IntersectContext::coherent(); - let ray_hit = scene.intersect( - &mut intersection_ctx, - Ray::new(camera.pos.into(), dir.into()), - ); - - if ray_hit.is_valid() { - let p = image.get_pixel_mut(i, j); - let diffuse = colors[ray_hit.hit.geomID as usize]; - - let mut shadow_ray = - Ray::segment(ray_hit.hit_point(), light_dir.into(), 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 - }; - - // Write the color to the image. - p[0] = (color.x * 255.0) as u8; - p[1] = (color.y * 255.0) as u8; - p[2] = (color.z * 255.0) as u8; - } - } - } + render_frame(&mut tiled, image, time, &scene, &camera, &colors); + + let elapsed = time - last_time; + last_time = time; + let fps = 1.0 / elapsed; + eprint!("\r{} fps", fps); + }); +} + +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 ray_hit = scene.intersect( + &mut intersection_ctx, + Ray::new(camera.pos.into(), dir.into()), + ); + + if ray_hit.is_valid() { + let diffuse = colors[ray_hit.hit.geomID as usize]; + + let mut shadow_ray = Ray::segment(ray_hit.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( + tiled: &mut TiledImage, + frame: &mut RgbaImage, + time: f32, + scene: &Scene, + camera: &Camera, + colors: &[Vec3], +) { + tiled.reset_pixels(); + tiled.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, scene, camera, colors); + }); }); + tiled.write_to_image(frame); } diff --git a/examples/support/Cargo.toml b/examples/support/Cargo.toml index 74fc1ac68..cc7f4a4b3 100644 --- a/examples/support/Cargo.toml +++ b/examples/support/Cargo.toml @@ -12,4 +12,5 @@ clock_ticks = "0.1.1" wgpu = "0.12" winit = "0.26" futures = {version = "0.3", features = ["executor"]} +rayon = "1.5" diff --git a/examples/support/src/common.rs b/examples/support/src/common.rs index 3d6dd4caa..f4fab296e 100644 --- a/examples/support/src/common.rs +++ b/examples/support/src/common.rs @@ -1,3 +1,6 @@ +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; @@ -562,9 +565,7 @@ 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] as f32 - + y * G3[h as usize * 4 + 1] as f32 - + z * G3[h as usize * 4 + 2] as f32 + 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 { diff --git a/examples/support/src/lib.rs b/examples/support/src/lib.rs index 4c824f43f..fa5142f88 100644 --- a/examples/support/src/lib.rs +++ b/examples/support/src/lib.rs @@ -1,16 +1,3 @@ -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; @@ -21,6 +8,120 @@ pub use camera::Camera; pub use display::Display; pub use image::{Rgba, RgbaImage}; +pub use rayon::{iter::*, prelude::*, slice::*, vec::*}; +pub mod math { + pub use cgmath::*; +} + +/// An image that is tiled into smaller tiles for parallel rendering. +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, +} + +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], + } + } + + /// Write the tiled image to a flat image. + pub fn write_to_image(&self, image: &mut RgbaImage) { + 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(u32_to_rgba(pixel))); + } + } + } + + /// Iterate over the tiles of the tiled image. + pub fn par_tiles_mut(&mut self) -> impl IndexedParallelIterator> { + 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; + Tile { + 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. + 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. + pub pixels: &'a mut [u32], +} + +/// Convert a u32 to a RGBA color. +#[inline(always)] +pub const fn u32_to_rgba(val: u32) -> [u8; 4] { + let r = (val >> 24) as u8; + let g = (val >> 16) as u8; + let b = (val >> 8) as u8; + let a = val as u8; + [r, g, b, a] +} + +/// Convert a RGBA color to a u32. +#[inline(always)] +pub const fn rgba_to_u32(r: u8, g: u8, b: u8, a: u8) -> u32 { + ((r as u32) << 24) | ((g as u32) << 16) | ((b as u32) << 8) | (a as u32) +} /// Clamp `x` to be between `min` and `max` pub fn clamp(x: T, min: T, max: T) -> T { diff --git a/examples/triangle_geometry/Cargo.toml b/examples/triangle_geometry/Cargo.toml index 417887e01..a31dd594c 100644 --- a/examples/triangle_geometry/Cargo.toml +++ b/examples/triangle_geometry/Cargo.toml @@ -5,8 +5,9 @@ authors = ["Will Usher "] edition = "2021" [dependencies] -glam = "0.22.0" +glam = "0.23.0" embree = { path = "../.." } support = { path = "../support" } -rayon = "1.5" +[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 fc6bb7e23..1e4a4f07c 100644 --- a/examples/triangle_geometry/src/main.rs +++ b/examples/triangle_geometry/src/main.rs @@ -8,7 +8,10 @@ use embree::{ INVALID_ID, }; use glam::Vec3; -use support::{Camera, Rgba, RgbaImage, TILE_SIZE, TILE_SIZE_X, TILE_SIZE_Y}; +use support::*; + +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(); @@ -99,7 +102,7 @@ struct State { } fn main() { - let display = support::Display::new(512, 512, "triangle geometry"); + let display = Display::new(DISPLAY_WIDTH, DISPLAY_HEIGHT, "triangle geometry"); let device = Device::new().unwrap(); device.set_error_function(|err, msg| { println!("{}: {}", err, msg); @@ -142,12 +145,9 @@ fn main() { state.ground_id = state.scene.attach_geometry(&ground); state.scene.commit(); + let mut tiled = TiledImage::new(DISPLAY_WIDTH, DISPLAY_HEIGHT, TILE_SIZE_X, TILE_SIZE_Y); let mut last_time = 0.0; - support::display::run(display, move |image, camera_pose, time| { - for p in image.iter_mut() { - *p = 0; - } - + display::run(display, move |image, camera_pose, time| { let img_dims = image.dimensions(); let camera = Camera::look_dir( camera_pose.pos, @@ -157,14 +157,7 @@ fn main() { img_dims, ); - //render_frame(image, time, &camera, &state); - - // Render the scene - for j in 0..img_dims.1 { - for i in 0..img_dims.0 { - render_pixel(i, j, &mut image.get_pixel_mut(i, j), time, &camera, &state); - } - } + render_frame(&mut tiled, image, time, &camera, &state); let elapsed = time - last_time; last_time = time; @@ -174,7 +167,7 @@ fn main() { } // Task that renders a single pixel. -fn render_pixel(x: u32, y: u32, pixel: &mut Rgba, _time: f32, camera: &Camera, state: &State) { +fn render_pixel(x: u32, y: u32, pixel: &mut u32, _time: f32, camera: &Camera, state: &State) { let mut ctx = IntersectContext::coherent(); let dir = camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)); let ray_hit = state @@ -201,56 +194,29 @@ fn render_pixel(x: u32, y: u32, pixel: &mut Rgba, _time: f32, camera: &Camer diffuse * 0.5 }; - // Write the color to the image. - pixel[0] = (color.x * 255.0) as u8; - pixel[1] = (color.y * 255.0) as u8; - pixel[2] = (color.z * 255.0) as u8; + *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_tile( - tile_idx: u32, - num_tiles_x: u32, - num_tiles_y: u32, - width: u32, - height: u32, +fn render_frame( + tiled: &mut TiledImage, + frame: &mut RgbaImage, time: f32, camera: &Camera, state: &State, - image: &mut RgbaImage, ) { - let title_y = tile_idx / num_tiles_x; - let tile_x = tile_idx % num_tiles_x; - let x0 = tile_x * TILE_SIZE_X; - let y0 = title_y * TILE_SIZE_Y; - let x1 = (x0 + TILE_SIZE_X).min(width); - let y1 = (y0 + TILE_SIZE_Y).min(height); - - for y in y0..y1 { - for x in x0..x1 { - render_pixel(x, y, &mut image.get_pixel_mut(x, y), time, &camera, &state); - } - } -} - -fn render_frame(image: &mut RgbaImage, time: f32, camera: &Camera, state: &State) { - use rayon::prelude::*; - let img_dims = image.dimensions(); - let num_tiles_x = (img_dims.0 + TILE_SIZE_X - 1) / TILE_SIZE_X; - let num_tiles_y = (img_dims.1 + TILE_SIZE_Y - 1) / TILE_SIZE_Y; - let num_tiles = num_tiles_x * num_tiles_y; - - (0..num_tiles).into_par_iter().for_each(|tile_idx| { - render_tile( - tile_idx, - num_tiles_x, - num_tiles_y, - img_dims.0, - img_dims.1, - time, - camera, - state, - image, - ); + tiled.reset_pixels(); + tiled.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); + }); }); + tiled.write_to_image(frame); } diff --git a/src/ray/stream.rs b/src/ray/stream.rs index 24c863253..379448bc8 100644 --- a/src/ray/stream.rs +++ b/src/ray/stream.rs @@ -1,5 +1,4 @@ -use core::alloc; -use std::{iter::Iterator, marker::PhantomData, ptr::NonNull}; +use std::iter::Iterator; use crate::{ aligned_vector, aligned_vector_init, sys, SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, From 1489607e1291a3e95bd3513d0a4c9d3cd1dbd44f Mon Sep 17 00:00:00 2001 From: matthiascy Date: Tue, 28 Feb 2023 23:23:17 +0100 Subject: [PATCH 52/65] update tiled image for displacement geometry --- examples/displacement_geometry/src/main.rs | 167 +++++++++++++-------- examples/triangle_geometry/src/main.rs | 11 +- 2 files changed, 110 insertions(+), 68 deletions(-) diff --git a/examples/displacement_geometry/src/main.rs b/examples/displacement_geometry/src/main.rs index 877f97eb9..80f1ccd65 100644 --- a/examples/displacement_geometry/src/main.rs +++ b/examples/displacement_geometry/src/main.rs @@ -3,7 +3,10 @@ use embree::{ InterpolateOutput, IntersectContext, Ray, Scene, SceneFlags, }; use glam::vec3; -use support::{noise, Align16Array, Camera}; +use support::{ + noise, rgba_to_u32, Align16Array, Camera, 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; @@ -32,6 +35,8 @@ const CUBE_INDICES: [u32; NUM_INDICES] = [ 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; @@ -139,8 +144,18 @@ fn main() { let cube_id = scene.attach_geometry(&cube); scene.commit(); - let display = support::Display::new(512, 512, "Dynamic Scene"); - let light_dir = vec3(1.0, 1.0, 1.0).normalize(); + let display = support::Display::new( + DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT, + "Dynamic Scene", + ); + let mut last_time = 0.0; + let mut tiled = TiledImage::new( + DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT, + TILE_SIZE_X, + TILE_SIZE_Y, + ); support::display::run(display, move |image, camera_pose, time| { for p in image.iter_mut() { *p = 0; @@ -154,68 +169,94 @@ fn main() { img_dims, ); - // 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 intersection_ctx = IntersectContext::coherent(); - let ray_hit = scene.intersect( - &mut intersection_ctx, - Ray::new(camera.pos.into(), dir.into()), - ); + render_frame(&mut tiled, image, &camera, &scene, cube_id, ground_id); - let mut color = vec3(0.0, 0.0, 0.0); - if ray_hit.is_valid() { - let pixel = image.get_pixel_mut(i, j); - 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 mut normal = glam::Vec3::from(ray_hit.hit.normal_normalized()); - - #[cfg(feature = "smooth_normals")] - { - let hit_point: glam::Vec3 = ray_hit.hit_point().into(); - if ray_hit.hit.geomID != ground_id { - let mut output = InterpolateOutput::new(3, true, true, false); - 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 = - glam::Vec3::from_slice(output.dp_du().as_ref().unwrap()); - let mut dp_dv = - glam::Vec3::from_slice(output.dp_dv().as_ref().unwrap()); - let ng = dp_du.cross(dp_dv).normalize(); - dp_du += ng * displacement([hit_point.x, hit_point.y, hit_point.z]); - dp_dv += ng * displacement([hit_point.x, hit_point.y, hit_point.z]); - normal = dp_du.cross(dp_dv).normalize(); - } - } - - let mut shadow_ray = - Ray::segment(ray_hit.hit_point(), light_dir.into(), 0.001, f32::INFINITY); - - // Check if the shadow ray is occluded. - if !scene.occluded(&mut intersection_ctx, &mut shadow_ray) { - color += diffuse * light_dir.dot(normal).clamp(0.0, 1.0); - } - - // Write the color to the image. - pixel[0] = (color.x * 255.0) as u8; - pixel[1] = (color.y * 255.0) as u8; - pixel[2] = (color.z * 255.0) as u8; - } + let elapsed = time - last_time; + last_time = time; + let fps = 1.0 / elapsed; + eprint!("\r{} fps", fps); + }); +} + +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 ray_hit = scene.intersect(&mut ctx, Ray::new(camera.pos.into(), dir.into())); + + let mut color = vec3(0.0, 0.0, 0.0); + if ray_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 mut normal = glam::Vec3::from(ray_hit.hit.normal_normalized()); + + #[cfg(feature = "smooth_normals")] + { + let hit_point: glam::Vec3 = ray_hit.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 = glam::Vec3::from_slice(output.dp_du().as_ref().unwrap()); + let mut dp_dv = glam::Vec3::from_slice(output.dp_dv().as_ref().unwrap()); + let ng = dp_du.cross(dp_dv).normalize(); + dp_du += ng * displacement([hit_point.x, hit_point.y, hit_point.z]); + dp_dv += ng * displacement([hit_point.x, hit_point.y, hit_point.z]); + normal = dp_du.cross(dp_dv).normalize(); } } + + let mut shadow_ray = Ray::segment(ray_hit.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( + tiled: &mut TiledImage, + frame: &mut RgbaImage, + camera: &Camera, + scene: &Scene, + cube_id: u32, + ground_id: u32, +) { + tiled.reset_pixels(); + tiled.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); + }); }); + tiled.write_to_image(frame); } diff --git a/examples/triangle_geometry/src/main.rs b/examples/triangle_geometry/src/main.rs index 1e4a4f07c..83933dd33 100644 --- a/examples/triangle_geometry/src/main.rs +++ b/examples/triangle_geometry/src/main.rs @@ -148,13 +148,12 @@ fn main() { let mut tiled = TiledImage::new(DISPLAY_WIDTH, DISPLAY_HEIGHT, TILE_SIZE_X, TILE_SIZE_Y); let mut last_time = 0.0; display::run(display, move |image, camera_pose, time| { - let img_dims = image.dimensions(); let camera = Camera::look_dir( camera_pose.pos, camera_pose.dir, camera_pose.up, 75.0, - img_dims, + image.dimensions(), ); render_frame(&mut tiled, image, time, &camera, &state); @@ -167,12 +166,13 @@ fn main() { } // Task that renders a single pixel. -fn render_pixel(x: u32, y: u32, pixel: &mut u32, _time: f32, camera: &Camera, state: &State) { +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 ray_hit = state .scene .intersect(&mut ctx, Ray::new(camera.pos.into(), dir.into())); + let mut pixel = 0; if ray_hit.is_valid() { let diffuse = if ray_hit.hit.geomID == state.ground_id { glam::vec3(0.6, 0.6, 0.6) @@ -194,13 +194,14 @@ fn render_pixel(x: u32, y: u32, pixel: &mut u32, _time: f32, camera: &Camera, st diffuse * 0.5 }; - *pixel = rgba_to_u32( + pixel = rgba_to_u32( (color.x * 255.0) as u8, (color.y * 255.0) as u8, (color.z * 255.0) as u8, 255, ); } + pixel } fn render_frame( @@ -215,7 +216,7 @@ fn render_frame( 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); + *pixel = render_pixel(x, y, time, camera, state); }); }); tiled.write_to_image(frame); From b3d852e72f1209c3d92e96a91cb9742a822868c6 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 2 Mar 2023 01:01:47 +0100 Subject: [PATCH 53/65] trait for extending ray --- examples/displacement_geometry/src/main.rs | 18 +- examples/dynamic_scene/src/main.rs | 9 +- examples/intersection_filter/src/main.rs | 366 ++++++++++++++++++++- examples/support/src/common.rs | 12 + examples/support/src/lib.rs | 8 + examples/triangle_geometry/src/main.rs | 9 +- src/geometry.rs | 123 ++++++- src/ray.rs | 104 ++++++ src/ray/packet.rs | 28 ++ src/scene.rs | 15 +- 10 files changed, 649 insertions(+), 43 deletions(-) diff --git a/examples/displacement_geometry/src/main.rs b/examples/displacement_geometry/src/main.rs index 80f1ccd65..a73809514 100644 --- a/examples/displacement_geometry/src/main.rs +++ b/examples/displacement_geometry/src/main.rs @@ -1,8 +1,8 @@ use embree::{ BufferSlice, BufferUsage, Device, Format, Geometry, GeometryKind, InterpolateInput, - InterpolateOutput, IntersectContext, Ray, Scene, SceneFlags, + InterpolateOutput, IntersectContext, Ray, RayHit, Scene, SceneFlags, }; -use glam::vec3; +use glam::{vec3, Vec3}; use support::{ noise, rgba_to_u32, Align16Array, Camera, ParallelIterator, RgbaImage, TiledImage, DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH, TILE_SIZE_X, TILE_SIZE_Y, @@ -48,6 +48,11 @@ fn displacement(p: [f32; 3]) -> f32 { dn } +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( @@ -92,7 +97,7 @@ fn create_cube(device: &Device) -> Geometry<'static> { unsafe { geom.set_displacement_function( |raw_geom, user_data: Option<&mut ()>, prim_id, _, vertices| { - for (uv, ng, p) in vertices.into_iter_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]; @@ -188,7 +193,8 @@ fn render_pixel( ) -> u32 { let dir = camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)); let mut ctx = IntersectContext::coherent(); - let ray_hit = scene.intersect(&mut ctx, Ray::new(camera.pos.into(), dir.into())); + let mut ray_hit = RayHit::new(Ray::new(camera.pos.into(), dir.into())); + scene.intersect(&mut ctx, &mut ray_hit); let mut color = vec3(0.0, 0.0, 0.0); if ray_hit.is_valid() { @@ -220,8 +226,8 @@ fn render_pixel( let mut dp_du = glam::Vec3::from_slice(output.dp_du().as_ref().unwrap()); let mut dp_dv = glam::Vec3::from_slice(output.dp_dv().as_ref().unwrap()); let ng = dp_du.cross(dp_dv).normalize(); - dp_du += ng * displacement([hit_point.x, hit_point.y, hit_point.z]); - dp_dv += ng * displacement([hit_point.x, hit_point.y, hit_point.z]); + dp_du += ng * displacement_du_or_dv(hit_point, dp_du); + dp_dv += ng * displacement_du_or_dv(hit_point, dp_dv); normal = dp_du.cross(dp_dv).normalize(); } } diff --git a/examples/dynamic_scene/src/main.rs b/examples/dynamic_scene/src/main.rs index 4dbfe1024..3f55f6450 100644 --- a/examples/dynamic_scene/src/main.rs +++ b/examples/dynamic_scene/src/main.rs @@ -1,7 +1,8 @@ //! This example show how to create a dynamic scene. use embree::{ - BufferUsage, BuildQuality, Device, Format, Geometry, IntersectContext, Ray, Scene, SceneFlags, + BufferUsage, BuildQuality, Device, Format, Geometry, IntersectContext, Ray, RayHit, Scene, + SceneFlags, }; use glam::Vec3; use support::{ @@ -225,10 +226,8 @@ fn render_pixel( ) { let dir = camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)); let mut intersection_ctx = IntersectContext::coherent(); - let ray_hit = scene.intersect( - &mut intersection_ctx, - Ray::new(camera.pos.into(), dir.into()), - ); + let mut ray_hit = RayHit::new(Ray::new(camera.pos.into(), dir.into())); + scene.intersect(&mut intersection_ctx, &mut ray_hit); if ray_hit.is_valid() { let diffuse = colors[ray_hit.hit.geomID as usize]; diff --git a/examples/intersection_filter/src/main.rs b/examples/intersection_filter/src/main.rs index e5a161a7b..ca592a5f7 100644 --- a/examples/intersection_filter/src/main.rs +++ b/examples/intersection_filter/src/main.rs @@ -9,14 +9,79 @@ //! all surfaces along the ray, and terminates traversal if an opaque surface //! occluder is hit. -use embree::Ray; -use glam::Vec3; +use embree::{ + BufferSlice, BufferUsage, BuildQuality, Device, Format, Geometry, GeometryKind, HitN, + IntersectContext, Ray, RayHit, RayN, Scene, ValidMasks, +}; +use glam::{vec3, Mat3, Vec3}; +use support::{ + rgba_to_u32, Align16Array, Camera, Mode, ParallelIterator, RgbaImage, Tile, TiledImage, + DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH, TILE_SIZE_X, TILE_SIZE_Y, +}; + +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 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 MODE: Mode = Mode::Normal; + +const CUBE_VERTICES: Align16Array<[f32; 4], CUBE_NUM_VERTICES> = Align16Array([ + [-1.0, -1.0, -1.0, 0.0], + [1.0, -1.0, -1.0, 0.0], + [1.0, -1.0, 1.0, 0.0], + [-1.0, -1.0, 1.0, 0.0], + [-1.0, 1.0, -1.0, 0.0], + [1.0, 1.0, -1.0, 0.0], + [1.0, 1.0, 1.0, 0.0], + [-1.0, 1.0, 1.0, 0.0], +]); + +const CUBE_QUAD_INDICES: Align16Array = Align16Array([ + 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: Align16Array = Align16Array([ + 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, // +]); + +const CUBE_QUAD_FACES: [u32; CUBE_NUM_QUAD_FACES] = [4; 6]; // Extended ray structure that includes total transparency along the ray. struct Ray2 { - ray: Ray, + ray_hit: RayHit, transparency: f32, // accumulated transparency first_hit: u32, // index of first hit last_hit: u32, // index of last hit @@ -24,11 +89,304 @@ struct Ray2 { hit_prim_ids: [u32; HIT_LIST_LEN], } +impl Ray2 { + fn new(ray: Ray) -> Self { + Self { + ray_hit: RayHit::new(ray), + 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: Vec3) -> f32 { let v = ((4.0 * h.x).sin() * (4.0 * h.y).cos() * (4.0 * h.z).sin()).abs(); ((v - 0.1) * 3.0).clamp(0.0, 1.0) } +struct IntersectContextExt { + context: IntersectContext, + ray_ext: Ray2, +} + +fn render_pixel(x: u32, y: u32, camera: &Camera, scene: &Scene) -> u32 { + let mut weight = 1.0; + let mut color = Vec3::ZERO; + let mut ctx = IntersectContextExt { + context: IntersectContext::coherent(), + ray_ext: Ray2 { + ray_hit: RayHit::new(Ray::new_with_id( + camera.pos.into(), + camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)).into(), + 0, // needs to encode rayID for filter function + )), + transparency: 1.0, + first_hit: 0, + last_hit: 0, + hit_geom_ids: [0; HIT_LIST_LEN], + hit_prim_ids: [0; HIT_LIST_LEN], + }, + }; + + loop { + scene.intersect(&mut ctx.context, &mut ctx.ray_ext.ray_hit); + if !ctx.ray_ext.ray_hit.is_valid() { + break; + } + + let opacity = 1.0 - ctx.ray_ext.transparency; + let diffuse = Vec3::from(COLORS[ctx.ray_ext.ray_hit.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 = Ray2::new(Ray::segment( + ctx.ray_ext.ray_hit.hit_point(), + light_dir.into(), + 0.001, + f32::INFINITY, + )); + ctx.ray_ext = shadow_ray; + + // if !scene.occluded(&mut ctx.context, &mut ctx.ray_ext.ray_hit.ray) { + // let ll = diffuse + // * ctx.ray_ext.transparency + // * light_dir .dot(ctx.ray_ext.ray_hit.hit.normal_normalized().into()) + // .clamp(0.0, 1.0); + // color += weight * opacity * ll; + // } + + weight *= ctx.ray_ext.transparency; + ctx.ray_ext.ray_hit.ray.tnear = 1.001 * ctx.ray_ext.ray_hit.ray.tfar; + ctx.ray_ext.ray_hit.ray.tfar = f32::INFINITY; + ctx.ray_ext.ray_hit.hit.geomID = embree::INVALID_ID; + ctx.ray_ext.ray_hit.hit.primID = embree::INVALID_ID; + ctx.ray_ext.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 Tile, 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 Tile, camera: &Camera, scene: &Scene) { todo!() } + +fn render_frame(tiled: &mut TiledImage, frame: &mut RgbaImage, camera: &Camera, scene: &Scene) { + tiled.reset_pixels(); + match MODE { + Mode::Normal => { + tiled + .par_tiles_mut() + .for_each(|mut tile| render_tile(&mut tile, camera, scene)); + } + Mode::Stream => { + tiled + .par_tiles_mut() + .for_each(|mut tile| render_tile_stream(&mut tile, camera, scene)); + } + } + tiled.write_to_image(frame); +} + +fn intersect_filter<'a>( + rays: RayN<'a>, + hits: HitN<'a>, + mut valid: ValidMasks<'a>, + ctx: &mut IntersectContext, + _user_data: Option<&mut ()>, +) { + assert_eq!(rays.len(), 1); + + let context = unsafe { + let ctx = ctx as *mut IntersectContext as *mut IntersectContextExt; + assert!(!ctx.is_null()); + &mut *ctx + }; + + // ignore invalid rays + if valid[0] != -1 { + return; + } + + // calculate transparency + let h = Vec3::from(rays.org(0)) + Vec3::from(rays.dir(0)) * rays.tfar(0); + let t = transparency_function(h); + + // ignore hit if completely transparent + if t >= 1.0 { + valid[0] = 0; + } else { + // otherwise accept hit and remember transparency + context.ray_ext.transparency = t; + } +} + +fn occlusion_filter<'a>( + rays: RayN<'a>, + hits: HitN<'a>, + mut valid: ValidMasks<'a>, + context: &mut IntersectContext, + _user_data: Option<&mut ()>, +) { + assert_eq!(rays.len(), 1); + let context = unsafe { + let ctx = context as *mut IntersectContext as *mut IntersectContextExt; + assert!(!ctx.is_null()); + &mut *ctx + }; + + if valid[0] != -1 { + return; + } + + for i in context.ray_ext.first_hit..context.ray_ext.last_hit { + let slot = i as usize % HIT_LIST_LEN; + if context.ray_ext.hit_geom_ids[slot] == hits.geom_id(0) + && context.ray_ext.hit_prim_ids[slot] == hits.prim_id(0) + { + valid[0] = 0; // ignore duplicate intersections + return; + } + } + + // store hit in hit list + let slot = context.ray_ext.last_hit % HIT_LIST_LEN as u32; + context.ray_ext.hit_geom_ids[slot as usize] = hits.geom_id(0); + context.ray_ext.hit_prim_ids[slot as usize] = hits.prim_id(0); + context.ray_ext.last_hit += 1; + + eprintln!("{} {}", context.ray_ext.first_hit, context.ray_ext.last_hit); + + if context.ray_ext.last_hit - context.ray_ext.first_hit > HIT_LIST_LEN as u32 { + context.ray_ext.first_hit += 1; + } + + let h = Vec3::from(rays.org(0)) + Vec3::from(rays.dir(0)) * rays.tfar(0); + + let t = transparency_function(h); + context.ray_ext.transparency *= t; + if t != 0.0 { + valid[0] = 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| { + let vtx = Vec3::new(v[0], v[1], v[2]); + let vtx = offset + Mat3::from_axis_angle(Vec3::Y, rotation) * scale * vtx; + [vtx.x, vtx.y, vtx.z, 0.0] + }); + geom.set_new_buffer(BufferUsage::VERTEX, 0, Format::FLOAT3, 16, 8) + .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(occlusion_filter); + } + Mode::Stream => { + // geom.set_intersect_filter_function(|_, _, _, _| {}); + // geom.set_occluded_filter_function(|_, _, _| {}); + todo!() + } + } + geom.commit(); + geom +} + fn main() { - println!("Hello, world!"); + 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); + 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, + "Intersection Filter", + ); + let mut last_time = 0.0; + let mut tiled = TiledImage::new( + DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT, + TILE_SIZE_X, + TILE_SIZE_Y, + ); + support::display::run(display, move |image, camera_pose, time| { + 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, + ); + + render_frame(&mut tiled, image, &camera, &scene); + + let elapsed = time - last_time; + last_time = time; + let fps = 1.0 / elapsed; + eprint!("\r{} fps", fps); + }); } diff --git a/examples/support/src/common.rs b/examples/support/src/common.rs index f4fab296e..574e81f55 100644 --- a/examples/support/src/common.rs +++ b/examples/support/src/common.rs @@ -1,3 +1,5 @@ +use std::ops::{Deref, DerefMut}; + pub const DEFAULT_DISPLAY_WIDTH: u32 = 512; pub const DEFAULT_DISPLAY_HEIGHT: u32 = 512; @@ -556,6 +558,16 @@ pub const G3: [f32; 128 * 4] = [ #[repr(align(16))] pub struct Align16Array(pub [T; N]); +impl Deref for Align16Array { + type Target = [T; N]; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl DerefMut for Align16Array { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } +} + #[inline(always)] pub fn lerp(a: f32, b: f32, t: f32) -> f32 { a + (b - a) * t } diff --git a/examples/support/src/lib.rs b/examples/support/src/lib.rs index fa5142f88..dadb40f98 100644 --- a/examples/support/src/lib.rs +++ b/examples/support/src/lib.rs @@ -13,6 +13,14 @@ pub mod math { pub use cgmath::*; } +/// The type of ray used for rendering with Embree. +pub enum Mode { + /// A single ray. + Normal, + /// A stream of rays. + Stream, +} + /// An image that is tiled into smaller tiles for parallel rendering. pub struct TiledImage { pub width: u32, diff --git a/examples/triangle_geometry/src/main.rs b/examples/triangle_geometry/src/main.rs index 83933dd33..d32c6180f 100644 --- a/examples/triangle_geometry/src/main.rs +++ b/examples/triangle_geometry/src/main.rs @@ -4,8 +4,8 @@ extern crate embree; extern crate support; use embree::{ - BufferSlice, BufferUsage, Device, Format, IntersectContext, QuadMesh, Ray, Scene, TriangleMesh, - INVALID_ID, + BufferSlice, BufferUsage, Device, Format, IntersectContext, QuadMesh, Ray, RayHit, Scene, + TriangleMesh, INVALID_ID, }; use glam::Vec3; use support::*; @@ -169,9 +169,8 @@ fn main() { 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 ray_hit = state - .scene - .intersect(&mut ctx, Ray::new(camera.pos.into(), dir.into())); + let mut ray_hit = RayHit::new(Ray::new(camera.pos.into(), dir.into())); + state.scene.intersect(&mut ctx, &mut ray_hit); let mut pixel = 0; if ray_hit.is_valid() { let diffuse = if ray_hit.hit.geomID == state.ground_id { diff --git a/src/geometry.rs b/src/geometry.rs index 12b258bee..5c4755ddd 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -9,7 +9,7 @@ use crate::{ use std::{ borrow::Cow, - ops::{Deref, DerefMut}, + ops::{Deref, DerefMut, Index, IndexMut}, sync::Arc, }; @@ -677,7 +677,7 @@ impl<'buf> Geometry<'buf> { pub fn set_intersect_filter_function(&mut self, filter: F) where D: UserGeometryData, - F: for<'a> FnMut(&'a mut [i32], Option<&mut D>, &mut IntersectContext, RayN<'a>, HitN<'a>), + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut IntersectContext, Option<&mut D>), { let mut geom_data = self.data.lock().unwrap(); unsafe { @@ -717,7 +717,7 @@ impl<'buf> Geometry<'buf> { pub fn set_occluded_filter_function(&mut self, filter: F) where D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut IntersectContext, Option<&mut D>), { let mut geom_data = self.data.lock().unwrap(); unsafe { @@ -1857,12 +1857,12 @@ impl_geometry_type!(Instance, GeometryKind::INSTANCE, fn intersect_filter_function(_f: &mut F) -> RTCFilterFunctionN where D: UserGeometryData, - F: for<'a> FnMut(&'a mut [i32], Option<&mut D>, &mut IntersectContext, RayN<'a>, HitN<'a>), + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut IntersectContext, Option<&mut D>), { unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) where D: UserGeometryData, - F: for<'a> FnMut(&'a mut [i32], Option<&mut D>, &mut IntersectContext, RayN<'a>, HitN<'a>), + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut IntersectContext, Option<&mut D>), { let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)).intersect_filter_fn as *mut F; @@ -1880,20 +1880,25 @@ where None => None, } }; + let len = (*args).N as usize; cb( - std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), - user_data, - &mut *(*args).context, RayN { ptr: &mut *(*args).ray, - len: (*args).N as usize, + len, marker: PhantomData, }, HitN { ptr: &mut *(*args).hit, - len: (*args).N as usize, + len, + marker: PhantomData, + }, + ValidMasks { + ptr: &mut *(*args).valid, + len, marker: PhantomData, }, + &mut *(*args).context, + user_data, ); } } @@ -1905,12 +1910,12 @@ where fn occluded_filter_function(_f: &mut F) -> RTCFilterFunctionN where D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut IntersectContext, Option<&mut D>), { unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) where D: UserGeometryData, - F: FnMut(&mut [i32], Option<&mut D>, &mut IntersectContext, RayN, HitN), + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut IntersectContext, Option<&mut D>), { let len = (*args).N as usize; let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)).occluded_filter_fn as *mut F; @@ -1929,9 +1934,6 @@ where } }; cb( - std::slice::from_raw_parts_mut((*args).valid, len), - user_data, - &mut *(*args).context, RayN { ptr: &mut *(*args).ray, len, @@ -1942,6 +1944,13 @@ where len, marker: PhantomData, }, + ValidMasks { + ptr: &mut *(*args).valid, + len, + marker: PhantomData, + }, + &mut *(*args).context, + user_data, ); } } @@ -2235,3 +2244,87 @@ where Some(inner::) } + +/// Struct holding data for valid masks used in the callback function set by +/// [`Geometry::set_intersection_filter_function`], +/// [`Geometry::set_occlusion_filter_function`]. +pub struct ValidMasks<'a> { + ptr: *const i32, + len: usize, + marker: PhantomData<&'a [i32]>, +} + +pub struct ValidMasksIter<'a, 'b> { + inner: &'b ValidMasks<'a>, + cur: usize, +} + +impl<'a> ValidMasks<'a> { + pub fn iter<'b>(&'b self) -> ValidMasksIter<'a, 'b> { + ValidMasksIter { + inner: self, + cur: 0, + } + } + + pub fn iter_mut<'b>(&'b mut self) -> ValidMasksIterMut<'a, 'b> { + ValidMasksIterMut { + inner: self, + cur: 0, + } + } + + pub const fn len(&self) -> usize { self.len } +} + +impl<'a> Index for ValidMasks<'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 ValidMasks<'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 ValidMasksIter<'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 ValidMasksIterMut<'a, 'b> { + inner: &'b mut ValidMasks<'a>, + cur: usize, +} + +impl<'a, 'b> Iterator for ValidMasksIterMut<'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/ray.rs b/src/ray.rs index d1fd7a1a7..41b7574ed 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -8,6 +8,42 @@ pub use packet::*; pub use soa::*; pub use stream::*; +/// Trait for types that can be converted to a [`Ray`]. +/// +/// Structs that implement this trait must guarantee that they are +/// layout-compatible with [`Ray`] (i.e. pointer casts between the two types are +/// valid). Make the [`Ray`] type the first field of the struct. +pub unsafe trait AsRay: Sized { + type RayExt: Sized; + + fn as_ray(&self) -> &Ray; + fn as_ray_mut(&mut self) -> &mut Ray; + fn as_ext(&self) -> Option<&Self::RayExt>; + fn as_ext_mut(&mut self) -> Option<&mut Self::RayExt>; + + fn as_ray_mut_ptr(&mut self) -> *mut Ray { self.as_ray_mut() as *mut Ray } + + fn as_ray_ptr(&self) -> *const Ray { self.as_ray() as *const Ray } +} + +/// Trait for types that can be converted to a [`Ray`]. +/// +/// Structs that implement this trait must guarantee that +/// they are layout-compatible with [`Ray`] (i.e. pointer +/// casts between the two types are valid). +pub unsafe trait AsRayHit: AsRay { + type RayHitExt: Sized; + + fn as_ray_hit(&self) -> &RayHit; + fn as_ray_hit_mut(&mut self) -> &mut RayHit; + fn as_ext(&self) -> Option<&Self::RayHitExt>; + fn as_ext_mut(&mut self) -> Option<&mut Self::RayHitExt>; + + fn as_ray_hit_mut_ptr(&mut self) -> *mut RayHit { self.as_ray_hit_mut() as *mut RayHit } + + fn as_ray_hit_ptr(&self) -> *const RayHit { self.as_ray_hit() as *const RayHit } +} + /// New type alias for [`sys::RTCRay`] that provides some convenience /// methods. /// @@ -43,6 +79,23 @@ impl Ray { Ray::segment(origin, direction, 0.0, f32::INFINITY) } + pub fn new_with_id(origin: [f32; 3], direction: [f32; 3], id: u32) -> Ray { + Ray { + org_x: origin[0], + org_y: origin[1], + org_z: origin[2], + tnear: 0.0, + dir_x: direction[0], + dir_y: direction[1], + dir_z: direction[2], + tfar: f32::INFINITY, + time: 0.0, + mask: u32::MAX, + id, + flags: 0, + } + } + /// Creates a new 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 { @@ -80,6 +133,33 @@ impl Ray { } } +unsafe impl AsRay for Ray { + type RayExt = (); + + fn as_ray(&self) -> &Ray { self } + fn as_ray_mut(&mut self) -> &mut Ray { self } + fn as_ext(&self) -> Option<&()> { None } + fn as_ext_mut(&mut self) -> Option<&mut ()> { None } +} + +/// Extended ray type that contains an additional data field. +/// +/// For the reason that the ray passed to the filter callback functions +/// and user geometry callback functions is guaranteed to be the same +/// ray pointer initially provided to the ray query function by the user, +/// it is SAFE to extend the ray by additional data and access this data +/// inside the filter callback functions (e.g. to accumulate opacity) and +/// user geometry callback functions (e.g. to accumulate color). +pub struct RayExt { + pub ray: Ray, + pub ext: E, +} + +pub struct RayHitExt { + pub ray: RayHit, + pub ext: E, +} + /// New type alias for [`sys::RTCHit`] that provides some convenience /// methods. /// @@ -173,3 +253,27 @@ impl RayHit { ] } } + +unsafe impl AsRay for RayHit { + type RayExt = Hit; + + fn as_ray(&self) -> &Ray { &self.ray } + + fn as_ray_mut(&mut self) -> &mut Ray { &mut self.ray } + + fn as_ext(&self) -> Option<&Self::RayExt> { Some(&self.hit) } + + fn as_ext_mut(&mut self) -> Option<&mut Self::RayExt> { Some(&mut self.hit) } +} + +unsafe impl AsRayHit for RayHit { + type RayHitExt = (); + + fn as_ray_hit(&self) -> &RayHit { self } + + fn as_ray_hit_mut(&mut self) -> &mut RayHit { self } + + fn as_ext(&self) -> Option<&Self::RayHitExt> { None } + + fn as_ext_mut(&mut self) -> Option<&mut Self::RayHitExt> { None } +} diff --git a/src/ray/packet.rs b/src/ray/packet.rs index f0ce6d01b..cec9403f3 100644 --- a/src/ray/packet.rs +++ b/src/ray/packet.rs @@ -261,6 +261,18 @@ pub struct RayN<'a> { } impl<'a> RayN<'a> { + pub const 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), + ] + } + } + pub const fn org_x(&self, i: usize) -> f32 { debug_assert!(i < self.len, "index out of bounds"); unsafe { *(self.ptr as *const f32).add(i) } @@ -309,6 +321,18 @@ impl<'a> RayN<'a> { } } + pub const 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), + ] + } + } + pub const fn dir_x(&self, i: usize) -> f32 { debug_assert!(i < self.len, "index out of bounds"); unsafe { *(self.ptr as *const f32).add(4 * self.len + i) } @@ -404,6 +428,8 @@ impl<'a> RayN<'a> { *(self.ptr as *mut u32).add(11 * self.len + i) = f; } } + + pub const fn len(&self) -> usize { self.len } } /// Hit packet of runtime size. @@ -457,6 +483,8 @@ impl<'a> HitN<'a> { debug_assert!(i < self.len, "index out of bounds"); unsafe { *(self.ptr as *const u32).add(7 * self.len + i) } } + + pub const fn len(&self) -> usize { self.len } } /// Combined ray and hit packet of runtime size. diff --git a/src/scene.rs b/src/scene.rs index f40777f90..1c9e19b9f 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,6 +1,6 @@ use crate::{ - Bounds, Error, PointQuery, PointQueryContext, Ray16, Ray8, RayHit16, RayHit8, RayHitNp, - RayHitPacket, RayPacket, SceneFlags, + AsRay, AsRayHit, Bounds, Error, PointQuery, PointQueryContext, Ray16, Ray8, RayHit16, RayHit8, + RayHitNp, RayHitPacket, RayPacket, SceneFlags, }; use std::{ any::TypeId, @@ -444,16 +444,14 @@ impl<'a> Scene<'a> { /// 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 IntersectContext, ray: Ray) -> RayHit { - let mut ray_hit = RayHit::new(ray); + pub fn intersect(&self, ctx: &mut IntersectContext, ray: &mut R) { unsafe { rtcIntersect1( self.handle, ctx as *mut RTCIntersectContext, - &mut ray_hit as *mut RTCRayHit, + ray.as_ray_hit_mut_ptr(), ); } - ray_hit } /// Finds the closest hits for a ray packet of size 4 with the scene. @@ -573,12 +571,13 @@ impl<'a> Scene<'a> { /// additional data. See [`IntersectContext`] for more information. /// /// * `ray` - The ray to intersect with the scene. - pub fn occluded(&self, ctx: &mut IntersectContext, ray: &mut Ray) -> bool { + pub fn occluded(&self, ctx: &mut IntersectContext, ray: &mut R) -> bool { + let mut ray = ray.as_ray_mut(); unsafe { rtcOccluded1( self.handle, ctx as *mut RTCIntersectContext, - <&mut Ray as Into<&mut RTCRay>>::into(ray) as *mut RTCRay, + ray as *mut RTCRay, ); } ray.tfar == -f32::INFINITY From b79391e37ca136e63ebf11b2dfffce8770c7b53f Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 2 Mar 2023 17:38:41 +0100 Subject: [PATCH 54/65] poor man's inheritance for ray --- examples/curve_geometry/src/main.rs | 2 +- examples/displacement_geometry/src/main.rs | 2 +- examples/dynamic_scene/src/main.rs | 2 +- examples/intersection_filter/src/main.rs | 150 +++++----- examples/minimal/src/main.rs | 5 +- examples/obj_ao_parallel/src/main.rs | 4 +- examples/obj_viewer/src/main.rs | 2 +- examples/triangle_geometry/src/main.rs | 2 +- src/context.rs | 81 ++++++ src/geometry.rs | 43 +-- src/ray.rs | 303 +++++++++++++++++++-- src/scene.rs | 18 +- 12 files changed, 474 insertions(+), 140 deletions(-) diff --git a/examples/curve_geometry/src/main.rs b/examples/curve_geometry/src/main.rs index 1e5168dac..5e3558961 100644 --- a/examples/curve_geometry/src/main.rs +++ b/examples/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/displacement_geometry/src/main.rs b/examples/displacement_geometry/src/main.rs index a73809514..f2cf23d70 100644 --- a/examples/displacement_geometry/src/main.rs +++ b/examples/displacement_geometry/src/main.rs @@ -193,7 +193,7 @@ fn render_pixel( ) -> 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::new(Ray::new(camera.pos.into(), dir.into())); + let mut ray_hit = RayHit::from_ray(Ray::new(camera.pos.into(), dir.into())); scene.intersect(&mut ctx, &mut ray_hit); let mut color = vec3(0.0, 0.0, 0.0); diff --git a/examples/dynamic_scene/src/main.rs b/examples/dynamic_scene/src/main.rs index 3f55f6450..4bf29c868 100644 --- a/examples/dynamic_scene/src/main.rs +++ b/examples/dynamic_scene/src/main.rs @@ -226,7 +226,7 @@ fn render_pixel( ) { 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::new(Ray::new(camera.pos.into(), dir.into())); + let mut ray_hit = RayHit::from_ray(Ray::new(camera.pos.into(), dir.into())); scene.intersect(&mut intersection_ctx, &mut ray_hit); if ray_hit.is_valid() { diff --git a/examples/intersection_filter/src/main.rs b/examples/intersection_filter/src/main.rs index ca592a5f7..978aba864 100644 --- a/examples/intersection_filter/src/main.rs +++ b/examples/intersection_filter/src/main.rs @@ -10,8 +10,9 @@ //! occluder is hit. use embree::{ - BufferSlice, BufferUsage, BuildQuality, Device, Format, Geometry, GeometryKind, HitN, - IntersectContext, Ray, RayHit, RayN, Scene, ValidMasks, + AsRay, AsRayHit, BufferSlice, BufferUsage, BuildQuality, Device, Format, Geometry, + GeometryKind, HitN, IntersectContext, IntersectContextExt, Ray, RayExt, RayHit, RayHitExt, + RayN, Scene, ValidMasks, }; use glam::{vec3, Mat3, Vec3}; use support::{ @@ -79,9 +80,9 @@ const CUBE_TRI_INDICES: Align16Array = Align16Array([ const CUBE_QUAD_FACES: [u32; CUBE_NUM_QUAD_FACES] = [4; 6]; -// Extended ray structure that includes total transparency along the ray. -struct Ray2 { - ray_hit: RayHit, +#[repr(C)] +#[derive(Debug, Copy, Clone)] +struct ExtraData { transparency: f32, // accumulated transparency first_hit: u32, // index of first hit last_hit: u32, // index of last hit @@ -89,10 +90,9 @@ struct Ray2 { hit_prim_ids: [u32; HIT_LIST_LEN], } -impl Ray2 { - fn new(ray: Ray) -> Self { - Self { - ray_hit: RayHit::new(ray), +impl Default for ExtraData { + fn default() -> Self { + ExtraData { transparency: 1.0, first_hit: 0, last_hit: 0, @@ -102,70 +102,66 @@ impl Ray2 { } } +type Ray2 = RayHitExt; + fn transparency_function(h: Vec3) -> f32 { let v = ((4.0 * h.x).sin() * (4.0 * h.y).cos() * (4.0 * h.z).sin()).abs(); ((v - 0.1) * 3.0).clamp(0.0, 1.0) } -struct IntersectContextExt { - context: IntersectContext, - ray_ext: Ray2, -} +type IntersectContext2<'a> = IntersectContextExt<&'a mut Ray2>; fn render_pixel(x: u32, y: u32, camera: &Camera, scene: &Scene) -> u32 { let mut weight = 1.0; let mut color = Vec3::ZERO; - let mut ctx = IntersectContextExt { - context: IntersectContext::coherent(), - ray_ext: Ray2 { - ray_hit: RayHit::new(Ray::new_with_id( - camera.pos.into(), - camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)).into(), - 0, // needs to encode rayID for filter function - )), - transparency: 1.0, - first_hit: 0, - last_hit: 0, - hit_geom_ids: [0; HIT_LIST_LEN], - hit_prim_ids: [0; HIT_LIST_LEN], - }, - }; + let mut primary = Ray2::new_with_ray( + Ray::new_with_id( + camera.pos.into(), + camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)).into(), + 0, // needs to encode rayID for filter function + ), + ExtraData::default(), + ); + let mut ctx = IntersectContext2::coherent(&mut primary); loop { - scene.intersect(&mut ctx.context, &mut ctx.ray_ext.ray_hit); - if !ctx.ray_ext.ray_hit.is_valid() { + scene.intersect(&mut ctx.context, ctx.extra); + if !ctx.extra.ray.is_valid() { break; } - let opacity = 1.0 - ctx.ray_ext.transparency; - let diffuse = Vec3::from(COLORS[ctx.ray_ext.ray_hit.hit.primID as usize]); + let opacity = 1.0 - ctx.extra.ext.transparency; + let diffuse = Vec3::from(COLORS[ctx.extra.ray.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 = Ray2::new(Ray::segment( - ctx.ray_ext.ray_hit.hit_point(), - light_dir.into(), - 0.001, - f32::INFINITY, - )); - ctx.ray_ext = shadow_ray; - - // if !scene.occluded(&mut ctx.context, &mut ctx.ray_ext.ray_hit.ray) { - // let ll = diffuse - // * ctx.ray_ext.transparency - // * light_dir .dot(ctx.ray_ext.ray_hit.hit.normal_normalized().into()) - // .clamp(0.0, 1.0); - // color += weight * opacity * ll; - // } - - weight *= ctx.ray_ext.transparency; - ctx.ray_ext.ray_hit.ray.tnear = 1.001 * ctx.ray_ext.ray_hit.ray.tfar; - ctx.ray_ext.ray_hit.ray.tfar = f32::INFINITY; - ctx.ray_ext.ray_hit.hit.geomID = embree::INVALID_ID; - ctx.ray_ext.ray_hit.hit.primID = embree::INVALID_ID; - ctx.ray_ext.transparency = 0.0; + let mut shadow_ray = Ray2::new_with_ray( + Ray::segment( + ctx.extra.ray.hit_point(), + light_dir.into(), + 0.001, + f32::INFINITY, + ), + ExtraData::default(), + ); + ctx.extra = &mut shadow_ray; + + if !scene.occluded(&mut ctx.context, ctx.extra) { + let ll = diffuse + * ctx.extra.ext.transparency + * light_dir + .dot(ctx.extra.ray.hit.normal_normalized().into()) // + .clamp(0.0, 1.0); + color += weight * opacity * ll; + } + + weight *= ctx.extra.ext.transparency; + ctx.extra.ray.ray.tnear = 1.001 * ctx.extra.ray.ray.tfar; + ctx.extra.ray.ray.tfar = f32::INFINITY; + ctx.extra.ray.hit.geomID = embree::INVALID_ID; + ctx.extra.ray.hit.primID = embree::INVALID_ID; + ctx.extra.ext.transparency = 0.0; } rgba_to_u32( @@ -207,17 +203,11 @@ fn intersect_filter<'a>( rays: RayN<'a>, hits: HitN<'a>, mut valid: ValidMasks<'a>, - ctx: &mut IntersectContext, + ctx: &mut IntersectContextExt<&mut Ray2>, _user_data: Option<&mut ()>, ) { assert_eq!(rays.len(), 1); - let context = unsafe { - let ctx = ctx as *mut IntersectContext as *mut IntersectContextExt; - assert!(!ctx.is_null()); - &mut *ctx - }; - // ignore invalid rays if valid[0] != -1 { return; @@ -232,7 +222,7 @@ fn intersect_filter<'a>( valid[0] = 0; } else { // otherwise accept hit and remember transparency - context.ray_ext.transparency = t; + ctx.extra.ext.transparency = t; } } @@ -240,24 +230,19 @@ fn occlusion_filter<'a>( rays: RayN<'a>, hits: HitN<'a>, mut valid: ValidMasks<'a>, - context: &mut IntersectContext, + context: &mut IntersectContextExt<&mut Ray2>, _user_data: Option<&mut ()>, ) { assert_eq!(rays.len(), 1); - let context = unsafe { - let ctx = context as *mut IntersectContext as *mut IntersectContextExt; - assert!(!ctx.is_null()); - &mut *ctx - }; if valid[0] != -1 { return; } - for i in context.ray_ext.first_hit..context.ray_ext.last_hit { + for i in context.extra.ext.first_hit..context.extra.ext.last_hit { let slot = i as usize % HIT_LIST_LEN; - if context.ray_ext.hit_geom_ids[slot] == hits.geom_id(0) - && context.ray_ext.hit_prim_ids[slot] == hits.prim_id(0) + if context.extra.ext.hit_geom_ids[slot] == hits.geom_id(0) + && context.extra.ext.hit_prim_ids[slot] == hits.prim_id(0) { valid[0] = 0; // ignore duplicate intersections return; @@ -265,21 +250,24 @@ fn occlusion_filter<'a>( } // store hit in hit list - let slot = context.ray_ext.last_hit % HIT_LIST_LEN as u32; - context.ray_ext.hit_geom_ids[slot as usize] = hits.geom_id(0); - context.ray_ext.hit_prim_ids[slot as usize] = hits.prim_id(0); - context.ray_ext.last_hit += 1; - - eprintln!("{} {}", context.ray_ext.first_hit, context.ray_ext.last_hit); + let slot = context.extra.ext.last_hit % HIT_LIST_LEN as u32; + context.extra.ext.hit_geom_ids[slot as usize] = hits.geom_id(0); + context.extra.ext.hit_prim_ids[slot as usize] = hits.prim_id(0); + context.extra.ext.last_hit += 1; + + eprintln!( + "{} {}", + context.extra.ext.first_hit, context.extra.ext.last_hit + ); - if context.ray_ext.last_hit - context.ray_ext.first_hit > HIT_LIST_LEN as u32 { - context.ray_ext.first_hit += 1; + if context.extra.ext.last_hit - context.extra.ext.first_hit > HIT_LIST_LEN as u32 { + context.extra.ext.first_hit += 1; } let h = Vec3::from(rays.org(0)) + Vec3::from(rays.dir(0)) * rays.tfar(0); let t = transparency_function(h); - context.ray_ext.transparency *= t; + context.extra.ext.transparency *= t; if t != 0.0 { valid[0] = 0; } diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 1bbec386a..d37bbeb12 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -1,6 +1,6 @@ //! This example shows how to intersect a ray with a single triangle. -use embree::{BufferUsage, Device, Format, GeometryKind, IntersectContext, Ray, Scene}; +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]) { @@ -11,7 +11,8 @@ fn cast_ray(scene: &Scene, origin: [f32; 3], direction: [f32; 3]) { let mut context = IntersectContext::coherent(); // Intersect a single ray with the scene. - let ray_hit = scene.intersect(&mut context, ray); + let mut ray_hit = RayHit::from(ray); + scene.intersect(&mut context, &mut ray_hit); print!("{origin:?} "); diff --git a/examples/obj_ao_parallel/src/main.rs b/examples/obj_ao_parallel/src/main.rs index ab131fd87..9b2c9bc6e 100644 --- a/examples/obj_ao_parallel/src/main.rs +++ b/examples/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/src/main.rs b/examples/obj_viewer/src/main.rs index 4e3f53c5b..38685bee3 100644 --- a/examples/obj_viewer/src/main.rs +++ b/examples/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_geometry/src/main.rs b/examples/triangle_geometry/src/main.rs index d32c6180f..fc6d18177 100644 --- a/examples/triangle_geometry/src/main.rs +++ b/examples/triangle_geometry/src/main.rs @@ -169,7 +169,7 @@ fn main() { 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::new(Ray::new(camera.pos.into(), dir.into())); + let mut ray_hit = RayHit::from_ray(Ray::new(camera.pos.into(), dir.into())); state.scene.intersect(&mut ctx, &mut ray_hit); let mut pixel = 0; if ray_hit.is_valid() { diff --git a/src/context.rs b/src/context.rs index 8e71acef8..ed79c259e 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,5 +1,23 @@ use crate::sys::*; +pub unsafe trait AsIntersectContext { + type Ext; + + fn as_intersect_context(&self) -> &IntersectContext; + fn as_intersect_context_mut(&mut self) -> &mut IntersectContext; + + fn as_intersect_context_ptr(&self) -> *const IntersectContext { + self.as_intersect_context() as *const IntersectContext + } + + fn as_intersect_context_mut_ptr(&mut self) -> *mut IntersectContext { + self.as_intersect_context_mut() as *mut IntersectContext + } + + fn as_intersect_context_ext(&self) -> Option<&Self::Ext>; + fn as_intersect_context_ext_mut(&mut self) -> Option<&mut Self::Ext>; +} + /// Per ray-query intersection context. /// /// This is used to configure intersection flags, specify a filter callback @@ -52,6 +70,69 @@ impl IntersectContext { } } +unsafe impl AsIntersectContext for IntersectContext { + type Ext = (); + + fn as_intersect_context(&self) -> &IntersectContext { self } + + fn as_intersect_context_mut(&mut self) -> &mut IntersectContext { self } + + fn as_intersect_context_ext(&self) -> Option<&Self::Ext> { None } + + fn as_intersect_context_ext_mut(&mut self) -> Option<&mut Self::Ext> { None } +} + +#[repr(C)] +#[derive(Debug)] +pub struct IntersectContextExt +where + E: Sized, +{ + pub context: IntersectContext, + pub extra: E, +} + +unsafe impl AsIntersectContext for IntersectContextExt +where + E: Sized, +{ + type Ext = E; + + fn as_intersect_context(&self) -> &IntersectContext { &self.context } + + fn as_intersect_context_mut(&mut self) -> &mut IntersectContext { &mut self.context } + + fn as_intersect_context_ext(&self) -> Option<&Self::Ext> { Some(&self.extra) } + + fn as_intersect_context_ext_mut(&mut self) -> Option<&mut Self::Ext> { Some(&mut self.extra) } +} + +impl IntersectContextExt +where + E: Sized, +{ + pub fn new(flags: RTCIntersectContextFlags, extra: E) -> IntersectContextExt { + IntersectContextExt { + context: IntersectContext::new(flags), + extra, + } + } + + pub fn coherent(extra: E) -> IntersectContextExt { + IntersectContextExt { + context: IntersectContext::coherent(), + extra, + } + } + + pub fn incoherent(extra: E) -> IntersectContextExt { + IntersectContextExt { + context: IntersectContext::incoherent(), + extra, + } + } +} + /// A stack which stores the IDs and instance transformations during a BVH /// traversal for a point query. /// diff --git a/src/geometry.rs b/src/geometry.rs index 5c4755ddd..f821830b1 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -3,8 +3,9 @@ use std::{ }; use crate::{ - sys::*, Bounds, BufferSlice, BufferUsage, BuildQuality, Device, Error, Format, GeometryKind, - HitN, IntersectContext, QuaternionDecomposition, RayHitN, RayN, Scene, SubdivisionMode, + sys::*, AsIntersectContext, Bounds, BufferSlice, BufferUsage, BuildQuality, Device, Error, + Format, GeometryKind, HitN, IntersectContext, QuaternionDecomposition, RayHitN, RayN, Scene, + SubdivisionMode, }; use std::{ @@ -674,10 +675,11 @@ impl<'buf> Geometry<'buf> { /// 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) + pub fn set_intersect_filter_function(&mut self, filter: F) where D: UserGeometryData, - F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut IntersectContext, Option<&mut D>), + C: AsIntersectContext, + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut C, Option<&mut D>), { let mut geom_data = self.data.lock().unwrap(); unsafe { @@ -714,10 +716,11 @@ impl<'buf> Geometry<'buf> { /// 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) + pub fn set_occluded_filter_function(&mut self, filter: F) where D: UserGeometryData, - F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut IntersectContext, Option<&mut D>), + C: AsIntersectContext, + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut C, Option<&mut D>), { let mut geom_data = self.data.lock().unwrap(); unsafe { @@ -1854,15 +1857,17 @@ impl_geometry_type!(Instance, GeometryKind::INSTANCE, /// Helper function to convert a Rust closure to `RTCFilterFunctionN` callback /// for intersect. -fn intersect_filter_function(_f: &mut F) -> RTCFilterFunctionN +fn intersect_filter_function(_f: &mut F) -> RTCFilterFunctionN where D: UserGeometryData, - F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut IntersectContext, Option<&mut D>), + C: AsIntersectContext, + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut C, Option<&mut D>), { - unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) + unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) where D: UserGeometryData, - F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut IntersectContext, Option<&mut D>), + C: AsIntersectContext, + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut C, Option<&mut D>), { let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)).intersect_filter_fn as *mut F; @@ -1897,25 +1902,27 @@ where len, marker: PhantomData, }, - &mut *(*args).context, + &mut *((*args).context as *mut _ as *mut C), user_data, ); } } - Some(inner::) + Some(inner::) } /// Helper function to convert a Rust closure to `RTCFilterFunctionN` callback /// for occluded. -fn occluded_filter_function(_f: &mut F) -> RTCFilterFunctionN +fn occluded_filter_function(_f: &mut F) -> RTCFilterFunctionN where D: UserGeometryData, - F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut IntersectContext, Option<&mut D>), + C: AsIntersectContext, + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut C, Option<&mut D>), { - unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) + unsafe extern "C" fn inner(args: *const RTCFilterFunctionNArguments) where D: UserGeometryData, - F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut IntersectContext, Option<&mut D>), + C: AsIntersectContext, + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut C, Option<&mut D>), { let len = (*args).N as usize; let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)).occluded_filter_fn as *mut F; @@ -1949,13 +1956,13 @@ where len, marker: PhantomData, }, - &mut *(*args).context, + &mut *((*args).context as *mut _ as *mut C), user_data, ); } } - Some(inner::) + Some(inner::) } /// Helper function to convert a Rust closure to `RTCBoundsFunction` callback. diff --git a/src/ray.rs b/src/ray.rs index 41b7574ed..4043f4220 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -1,4 +1,5 @@ use crate::{sys, INVALID_ID}; +use std::fmt::{Debug, Formatter}; mod packet; mod soa; @@ -10,38 +11,51 @@ pub use stream::*; /// Trait for types that can be converted to a [`Ray`]. /// +/// Embree uses poor man's inheritance to make it possible to extend the [`Ray`] +/// and [`RayHit`] types with additional data. This trait provides a way to +/// convert between the base types and the extended types. See also the +/// [`AsRayHit`] +/// +/// # Safety +/// /// Structs that implement this trait must guarantee that they are /// layout-compatible with [`Ray`] (i.e. pointer casts between the two types are -/// valid). Make the [`Ray`] type the first field of the struct. +/// valid). The corresponding pattern in C is called poor man's inheritance. See +/// [`RayExt`] for an example of how to do this. pub unsafe trait AsRay: Sized { type RayExt: Sized; fn as_ray(&self) -> &Ray; fn as_ray_mut(&mut self) -> &mut Ray; - fn as_ext(&self) -> Option<&Self::RayExt>; - fn as_ext_mut(&mut self) -> Option<&mut Self::RayExt>; - - fn as_ray_mut_ptr(&mut self) -> *mut Ray { self.as_ray_mut() as *mut Ray } + fn as_ray_ext(&self) -> Option<&Self::RayExt>; + fn as_ray_ext_mut(&mut self) -> Option<&mut Self::RayExt>; fn as_ray_ptr(&self) -> *const Ray { self.as_ray() as *const Ray } + fn as_ray_mut_ptr(&mut self) -> *mut Ray { self.as_ray_mut() as *mut Ray } } /// Trait for types that can be converted to a [`Ray`]. /// -/// Structs that implement this trait must guarantee that -/// they are layout-compatible with [`Ray`] (i.e. pointer -/// casts between the two types are valid). +/// Embree uses poor man's inheritance to make it possible to extend the [`Ray`] +/// and [`RayHit`] types with additional data. This trait provides a way to +/// convert between the base types and the extended types. See also the +/// [`AsRay`] +/// +/// # Safety +/// +/// Structs that implement this trait must guarantee that they are layout +/// compatible with [`Ray`] (i.e. pointer casts between the two types are +/// valid). pub unsafe trait AsRayHit: AsRay { type RayHitExt: Sized; fn as_ray_hit(&self) -> &RayHit; fn as_ray_hit_mut(&mut self) -> &mut RayHit; - fn as_ext(&self) -> Option<&Self::RayHitExt>; - fn as_ext_mut(&mut self) -> Option<&mut Self::RayHitExt>; - - fn as_ray_hit_mut_ptr(&mut self) -> *mut RayHit { self.as_ray_hit_mut() as *mut RayHit } + fn as_ray_hit_ext(&self) -> Option<&Self::RayHitExt>; + fn as_ray_hit_ext_mut(&mut self) -> Option<&mut Self::RayHitExt>; fn as_ray_hit_ptr(&self) -> *const RayHit { self.as_ray_hit() as *const RayHit } + fn as_ray_hit_mut_ptr(&mut self) -> *mut RayHit { self.as_ray_hit_mut() as *mut RayHit } } /// New type alias for [`sys::RTCRay`] that provides some convenience @@ -138,8 +152,8 @@ unsafe impl AsRay for Ray { fn as_ray(&self) -> &Ray { self } fn as_ray_mut(&mut self) -> &mut Ray { self } - fn as_ext(&self) -> Option<&()> { None } - fn as_ext_mut(&mut self) -> Option<&mut ()> { None } + fn as_ray_ext(&self) -> Option<&()> { None } + fn as_ray_ext_mut(&mut self) -> Option<&mut ()> { None } } /// Extended ray type that contains an additional data field. @@ -150,14 +164,72 @@ unsafe impl AsRay for Ray { /// it is SAFE to extend the ray by additional data and access this data /// inside the filter callback functions (e.g. to accumulate opacity) and /// user geometry callback functions (e.g. to accumulate color). -pub struct RayExt { +/// +/// To make sure that the extended ray type is layout-compatible with +/// the ray type, the additional data field must be `#[repr(C)]`. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct RayExt { pub ray: Ray, pub ext: E, } -pub struct RayHitExt { - pub ray: RayHit, - pub ext: E, +impl Debug for RayExt +where + E: Debug + Sized + Clone + Copy, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("RayExt") + .field("ray", &self.ray) + .field("ext", &self.ext) + .finish() + } +} + +impl RayExt +where + E: Sized + Copy + Clone, +{ + pub fn new(ray: Ray, ext: E) -> Self { Self { ray, ext } } +} + +#[test] +fn test_ray_ext_pointer_compatability_with_ray_pointer() { + let ray = RayExt { + ray: Ray::new([0.0, 0.0, 0.0], [1.0, 0.0, 0.0]), + ext: 10u32, + }; + assert_eq!( + &ray as *const RayExt as *const Ray, + &ray.ray as *const Ray + ); + + #[repr(C)] + #[derive(Clone, Copy, Debug)] + struct Extra { + a: u32, + b: u32, + c: f32, + d: f32, + } + + let mut ray2 = RayExt:: { + ray: Ray::new([0.0, 0.0, 0.0], [1.0, 0.0, 0.0]), + ext: Extra { + a: 1, + b: 2, + c: 3.0, + d: 4.0, + }, + }; + + let ray_ptr = &mut ray2 as *mut RayExt as *mut Ray; + let ray_ext = unsafe { &mut *(ray_ptr as *mut RayExt) }; + ray_ext.ext.a = 10; + ray_ext.ext.d = 40.0; + + assert_eq!(ray2.ext.a, 10); + assert_eq!(ray2.ext.d, 40.0); } /// New type alias for [`sys::RTCHit`] that provides some convenience @@ -233,7 +305,7 @@ pub type RayHit = sys::RTCRayHit; impl RayHit { /// Creates a new [`RayHit`] ready to be used in an intersection query. - pub fn new(ray: Ray) -> RayHit { + pub fn from_ray(ray: Ray) -> RayHit { RayHit { ray, hit: Hit::default(), @@ -254,6 +326,10 @@ impl RayHit { } } +impl From for RayHit { + fn from(value: Ray) -> Self { RayHit::from_ray(value) } +} + unsafe impl AsRay for RayHit { type RayExt = Hit; @@ -261,9 +337,9 @@ unsafe impl AsRay for RayHit { fn as_ray_mut(&mut self) -> &mut Ray { &mut self.ray } - fn as_ext(&self) -> Option<&Self::RayExt> { Some(&self.hit) } + fn as_ray_ext(&self) -> Option<&Self::RayExt> { Some(&self.hit) } - fn as_ext_mut(&mut self) -> Option<&mut Self::RayExt> { Some(&mut self.hit) } + fn as_ray_ext_mut(&mut self) -> Option<&mut Self::RayExt> { Some(&mut self.hit) } } unsafe impl AsRayHit for RayHit { @@ -273,7 +349,188 @@ unsafe impl AsRayHit for RayHit { fn as_ray_hit_mut(&mut self) -> &mut RayHit { self } - fn as_ext(&self) -> Option<&Self::RayHitExt> { None } + fn as_ray_hit_ext(&self) -> Option<&Self::RayHitExt> { None } + + fn as_ray_hit_ext_mut(&mut self) -> Option<&mut Self::RayHitExt> { None } +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct RayHitAsRayExtra { + pub hit: Hit, + pub ext: E, +} + +impl Debug for RayHitAsRayExtra +where + E: Debug + Sized + Copy + Clone, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("RayHitExtra") + .field("hit", &self.hit) + .field("ext", &self.ext) + .finish() + } +} + +/// Extended ray type that contains an additional data field. +/// +/// To make sure that the extended ray type is layout-compatible with +/// the ray type, the additional data field must be `#[repr(C)]`. +#[repr(C)] +#[repr(align(16))] +#[derive(Clone, Copy)] +pub struct RayHitExt { + pub ray: RayHit, + pub ext: E, +} + +impl Debug for RayHitExt +where + E: Debug + Sized + Clone + Copy, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("RayHitExt") + .field("ray", &self.ray) + .field("ext", &self.ext) + .finish() + } +} + +impl RayHitExt +where + E: Sized + Clone + Copy, +{ + pub fn new(ray_hit: RayHit, ext: E) -> Self { Self { ray: ray_hit, ext } } + + pub fn new_with_ray(ray: Ray, ext: E) -> Self { + Self { + ray: RayHit::from_ray(ray), + ext, + } + } +} + +unsafe impl AsRay for RayHitExt +where + E: Sized + Clone + Copy, +{ + type RayExt = RayHitAsRayExtra; + + fn as_ray(&self) -> &Ray { &self.ray.ray } + + fn as_ray_mut(&mut self) -> &mut Ray { &mut self.ray.ray } + + fn as_ray_ext(&self) -> Option<&Self::RayExt> { + Some(unsafe { &*(&self.ray.hit as *const Hit as *const Self::RayExt) }) + } + + fn as_ray_ext_mut(&mut self) -> Option<&mut Self::RayExt> { + Some(unsafe { &mut *(&mut self.ray.hit as *mut Hit as *mut Self::RayExt) }) + } +} + +unsafe impl AsRayHit for RayHitExt +where + E: Sized + Copy + Clone, +{ + type RayHitExt = E; + + fn as_ray_hit(&self) -> &RayHit { &self.ray } + + fn as_ray_hit_mut(&mut self) -> &mut RayHit { &mut self.ray } + + fn as_ray_hit_ext(&self) -> Option<&Self::RayHitExt> { Some(&self.ext) } + + fn as_ray_hit_ext_mut(&mut self) -> Option<&mut Self::RayHitExt> { Some(&mut self.ext) } +} + +#[test] +fn test_ray_hit_ext_pointer_compatability_with_ray_hit() { + let ray = RayHitExt { + ray: RayHit::from_ray(Ray::new([0.0, 0.0, 0.0], [1.0, 0.0, 0.0])), + ext: [0.0f32, 0.0, 0.0, 0.0], + }; + assert_eq!( + &ray as *const RayHitExt<[f32; 4]> as *const RayHit, + &ray.ray as *const RayHit + ); + + #[repr(C)] + #[derive(Clone, Copy)] + struct Extra { + a: i32, + b: u64, + c: f32, + d: [u8; 4], + } + + let mut ray = RayHitExt:: { + ray: RayHit::from_ray(Ray::new([0.0, 0.0, 0.0], [1.0, 0.0, 0.0])), + ext: Extra { + a: 1, + b: 2, + c: 3.0, + d: [4, 5, 6, 7], + }, + }; + + let ray_ptr = &mut ray as *mut RayHitExt as *mut RayHit; + let ray_ext = unsafe { &mut *(ray_ptr as *mut RayHitExt) }; + ray_ext.ext.a = 10; + ray_ext.ext.d = [30, 31, 32, 33]; + + assert_eq!(ray.ext.a, 10); + assert_eq!(ray.ext.d, [30, 31, 32, 33]); + + ray.as_ray_hit_ext_mut().unwrap().a = 20; + + assert_eq!(ray.ext.a, 20); +} + +#[test] +fn test_ray_hit_ext_pointer_compatability_with_ray() { + let ray = RayHitExt { + ray: RayHit::from_ray(Ray::new([0.0, 0.0, 0.0], [1.0, 0.0, 0.0])), + ext: [0.0f32, 0.0, 0.0, 0.0], + }; + assert_eq!( + &ray as *const RayHitExt<[f32; 4]> as *const RayHit, + &ray.ray as *const RayHit + ); + + #[repr(C)] + #[derive(Clone, Copy)] + struct Extra { + a: i32, + b: u64, + d: [u8; 4], + } + + let mut ray = RayHitExt:: { + ray: RayHit::from_ray(Ray::new([0.0, 0.0, 0.0], [1.0, 0.0, 0.0])), + ext: Extra { + a: 1, + b: 2, + d: [4, 5, 6, 7], + }, + }; + + ray.as_ray_mut().org_x = 10.0; + ray.as_ray_mut().dir_x = 20.0; + + match ray.as_ray_ext_mut() { + None => {} + Some(ray_ext) => { + ray_ext.hit.geomID = 30; + ray_ext.hit.primID = 40; + ray_ext.ext.d = [50, 51, 52, 53]; + } + } - fn as_ext_mut(&mut self) -> Option<&mut Self::RayHitExt> { None } + assert_eq!(ray.ray.ray.org(), [10.0, 0.0, 0.0]); + assert_eq!(ray.ray.ray.dir(), [20.0, 0.0, 0.0]); + assert_eq!(ray.ext.d, [50, 51, 52, 53]); + assert_eq!(ray.ray.hit.geomID, 30); + assert_eq!(ray.ray.hit.primID, 40); } diff --git a/src/scene.rs b/src/scene.rs index 1c9e19b9f..6c57ef8cc 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,6 +1,6 @@ use crate::{ - AsRay, AsRayHit, Bounds, Error, PointQuery, PointQueryContext, Ray16, Ray8, RayHit16, RayHit8, - RayHitNp, RayHitPacket, RayPacket, SceneFlags, + AsIntersectContext, AsRay, AsRayHit, Bounds, Error, PointQuery, PointQueryContext, Ray16, Ray8, + RayHit16, RayHit8, RayHitNp, RayHitPacket, RayPacket, SceneFlags, }; use std::{ any::TypeId, @@ -13,7 +13,7 @@ use crate::{ context::IntersectContext, device::Device, geometry::Geometry, - ray::{Ray, Ray4, RayHit, RayHit4, RayNp}, + ray::{Ray4, RayHit4, RayNp}, sys::*, }; @@ -444,11 +444,11 @@ impl<'a> Scene<'a> { /// 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 IntersectContext, ray: &mut R) { + pub fn intersect(&self, ctx: &mut C, ray: &mut R) { unsafe { rtcIntersect1( self.handle, - ctx as *mut RTCIntersectContext, + ctx.as_intersect_context_mut_ptr(), ray.as_ray_hit_mut_ptr(), ); } @@ -571,13 +571,13 @@ impl<'a> Scene<'a> { /// additional data. See [`IntersectContext`] for more information. /// /// * `ray` - The ray to intersect with the scene. - pub fn occluded(&self, ctx: &mut IntersectContext, ray: &mut R) -> bool { - let mut ray = ray.as_ray_mut(); + pub fn occluded(&self, ctx: &mut C, ray: &mut R) -> bool { + let ray = ray.as_ray_mut(); unsafe { rtcOccluded1( self.handle, - ctx as *mut RTCIntersectContext, - ray as *mut RTCRay, + ctx.as_intersect_context_mut_ptr(), + ray.as_ray_mut_ptr(), ); } ray.tfar == -f32::INFINITY From a9d5ba117e7bf564bdc1862ece2683b8b9ddf450 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 2 Mar 2023 18:31:41 +0100 Subject: [PATCH 55/65] remove AsRay/AsRayHit --- examples/intersection_filter/src/main.rs | 151 +++++----- src/context.rs | 50 +++- src/ray.rs | 339 ----------------------- src/scene.rs | 11 +- 4 files changed, 118 insertions(+), 433 deletions(-) diff --git a/examples/intersection_filter/src/main.rs b/examples/intersection_filter/src/main.rs index 978aba864..d13d1ea33 100644 --- a/examples/intersection_filter/src/main.rs +++ b/examples/intersection_filter/src/main.rs @@ -10,11 +10,10 @@ //! occluder is hit. use embree::{ - AsRay, AsRayHit, BufferSlice, BufferUsage, BuildQuality, Device, Format, Geometry, - GeometryKind, HitN, IntersectContext, IntersectContextExt, Ray, RayExt, RayHit, RayHitExt, - RayN, Scene, ValidMasks, + BufferSlice, BufferUsage, BuildQuality, Device, Format, Geometry, GeometryKind, HitN, + IntersectContext, IntersectContextExt, Ray, RayHit, RayN, Scene, ValidMasks, }; -use glam::{vec3, Mat3, Vec3}; +use glam::{vec3, Mat3, Mat4, Vec3, Vec4}; use support::{ rgba_to_u32, Align16Array, Camera, Mode, ParallelIterator, RgbaImage, Tile, TiledImage, DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH, TILE_SIZE_X, TILE_SIZE_Y, @@ -44,14 +43,14 @@ const COLORS: [[f32; 3]; 12] = [ const MODE: Mode = Mode::Normal; const CUBE_VERTICES: Align16Array<[f32; 4], CUBE_NUM_VERTICES> = Align16Array([ - [-1.0, -1.0, -1.0, 0.0], - [1.0, -1.0, -1.0, 0.0], - [1.0, -1.0, 1.0, 0.0], - [-1.0, -1.0, 1.0, 0.0], - [-1.0, 1.0, -1.0, 0.0], - [1.0, 1.0, -1.0, 0.0], - [1.0, 1.0, 1.0, 0.0], - [-1.0, 1.0, 1.0, 0.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, 1.0], ]); const CUBE_QUAD_INDICES: Align16Array = Align16Array([ @@ -82,7 +81,7 @@ const CUBE_QUAD_FACES: [u32; CUBE_NUM_QUAD_FACES] = [4; 6]; #[repr(C)] #[derive(Debug, Copy, Clone)] -struct ExtraData { +struct RayExtra { transparency: f32, // accumulated transparency first_hit: u32, // index of first hit last_hit: u32, // index of last hit @@ -90,9 +89,9 @@ struct ExtraData { hit_prim_ids: [u32; HIT_LIST_LEN], } -impl Default for ExtraData { +impl Default for RayExtra { fn default() -> Self { - ExtraData { + RayExtra { transparency: 1.0, first_hit: 0, last_hit: 0, @@ -102,66 +101,63 @@ impl Default for ExtraData { } } -type Ray2 = RayHitExt; - fn transparency_function(h: Vec3) -> f32 { let v = ((4.0 * h.x).sin() * (4.0 * h.y).cos() * (4.0 * h.z).sin()).abs(); ((v - 0.1) * 3.0).clamp(0.0, 1.0) } -type IntersectContext2<'a> = IntersectContextExt<&'a mut Ray2>; +type IntersectContext2 = 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 = Ray2::new_with_ray( - Ray::new_with_id( - camera.pos.into(), - camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)).into(), - 0, // needs to encode rayID for filter function - ), - ExtraData::default(), - ); - let mut ctx = IntersectContext2::coherent(&mut primary); + let mut primary = RayHit::from_ray(Ray::new_with_id( + camera.pos.into(), + camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)).into(), + 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 ctx.context, ctx.extra); - if !ctx.extra.ray.is_valid() { + scene.intersect(&mut primary_ctx, &mut primary); + + if !primary.is_valid() { break; } - let opacity = 1.0 - ctx.extra.ext.transparency; - let diffuse = Vec3::from(COLORS[ctx.extra.ray.hit.primID as usize]); + 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 = Ray2::new_with_ray( - Ray::segment( - ctx.extra.ray.hit_point(), - light_dir.into(), - 0.001, - f32::INFINITY, - ), - ExtraData::default(), - ); - ctx.extra = &mut shadow_ray; + let mut shadow_ray = + Ray::segment(primary.hit_point(), light_dir.into(), 0.001, f32::INFINITY); + shadow_ray.id = 0; - if !scene.occluded(&mut ctx.context, ctx.extra) { + if !scene.occluded(&mut ctx_shadow, &mut shadow_ray) { let ll = diffuse - * ctx.extra.ext.transparency + * shadow_extra.transparency * light_dir - .dot(ctx.extra.ray.hit.normal_normalized().into()) // + .dot(primary.hit.normal_normalized().into()) // .clamp(0.0, 1.0); color += weight * opacity * ll; } - weight *= ctx.extra.ext.transparency; - ctx.extra.ray.ray.tnear = 1.001 * ctx.extra.ray.ray.tfar; - ctx.extra.ray.ray.tfar = f32::INFINITY; - ctx.extra.ray.hit.geomID = embree::INVALID_ID; - ctx.extra.ray.hit.primID = embree::INVALID_ID; - ctx.extra.ext.transparency = 0.0; + 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( @@ -203,7 +199,7 @@ fn intersect_filter<'a>( rays: RayN<'a>, hits: HitN<'a>, mut valid: ValidMasks<'a>, - ctx: &mut IntersectContextExt<&mut Ray2>, + ctx: &mut IntersectContext2, _user_data: Option<&mut ()>, ) { assert_eq!(rays.len(), 1); @@ -222,7 +218,7 @@ fn intersect_filter<'a>( valid[0] = 0; } else { // otherwise accept hit and remember transparency - ctx.extra.ext.transparency = t; + ctx.ext.transparency = t; } } @@ -230,7 +226,7 @@ fn occlusion_filter<'a>( rays: RayN<'a>, hits: HitN<'a>, mut valid: ValidMasks<'a>, - context: &mut IntersectContextExt<&mut Ray2>, + context: &mut IntersectContext2, _user_data: Option<&mut ()>, ) { assert_eq!(rays.len(), 1); @@ -239,10 +235,10 @@ fn occlusion_filter<'a>( return; } - for i in context.extra.ext.first_hit..context.extra.ext.last_hit { + for i in context.ext.first_hit..context.ext.last_hit { let slot = i as usize % HIT_LIST_LEN; - if context.extra.ext.hit_geom_ids[slot] == hits.geom_id(0) - && context.extra.ext.hit_prim_ids[slot] == hits.prim_id(0) + 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; @@ -250,24 +246,19 @@ fn occlusion_filter<'a>( } // store hit in hit list - let slot = context.extra.ext.last_hit % HIT_LIST_LEN as u32; - context.extra.ext.hit_geom_ids[slot as usize] = hits.geom_id(0); - context.extra.ext.hit_prim_ids[slot as usize] = hits.prim_id(0); - context.extra.ext.last_hit += 1; - - eprintln!( - "{} {}", - context.extra.ext.first_hit, context.extra.ext.last_hit - ); + 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.extra.ext.last_hit - context.extra.ext.first_hit > HIT_LIST_LEN as u32 { - context.extra.ext.first_hit += 1; + if context.ext.last_hit - context.ext.first_hit > HIT_LIST_LEN as u32 { + context.ext.first_hit += 1; } let h = Vec3::from(rays.org(0)) + Vec3::from(rays.dir(0)) * rays.tfar(0); let t = transparency_function(h); - context.extra.ext.transparency *= t; + context.ext.transparency *= t; if t != 0.0 { valid[0] = 0; } @@ -300,15 +291,23 @@ fn create_cube<'a>(device: &Device, offset: Vec3, scale: Vec3, rotation: f32) -> // 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| { - let vtx = Vec3::new(v[0], v[1], v[2]); - let vtx = offset + Mat3::from_axis_angle(Vec3::Y, rotation) * scale * vtx; - [vtx.x, vtx.y, vtx.z, 0.0] + (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, 8) - .unwrap() - .view_mut::<[f32; 4]>() - .unwrap() - .copy_from_slice(&rotated); + 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, diff --git a/src/context.rs b/src/context.rs index ed79c259e..5b6a02a15 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,5 +1,14 @@ 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; @@ -82,29 +91,46 @@ unsafe impl AsIntersectContext for IntersectContext { fn as_intersect_context_ext_mut(&mut self) -> Option<&mut Self::Ext> { None } } +/// 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 context: IntersectContext, - pub extra: E, + 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_intersect_context(&self) -> &IntersectContext { &self.context } + fn as_intersect_context(&self) -> &IntersectContext { &self.ctx } - fn as_intersect_context_mut(&mut self) -> &mut IntersectContext { &mut self.context } + fn as_intersect_context_mut(&mut self) -> &mut IntersectContext { &mut self.ctx } - fn as_intersect_context_ext(&self) -> Option<&Self::Ext> { Some(&self.extra) } + fn as_intersect_context_ext(&self) -> Option<&Self::Ext> { Some(&self.ext) } - fn as_intersect_context_ext_mut(&mut self) -> Option<&mut Self::Ext> { Some(&mut self.extra) } + fn as_intersect_context_ext_mut(&mut self) -> Option<&mut Self::Ext> { Some(&mut self.ext) } } impl IntersectContextExt @@ -113,22 +139,22 @@ where { pub fn new(flags: RTCIntersectContextFlags, extra: E) -> IntersectContextExt { IntersectContextExt { - context: IntersectContext::new(flags), - extra, + ctx: IntersectContext::new(flags), + ext: extra, } } pub fn coherent(extra: E) -> IntersectContextExt { IntersectContextExt { - context: IntersectContext::coherent(), - extra, + ctx: IntersectContext::coherent(), + ext: extra, } } pub fn incoherent(extra: E) -> IntersectContextExt { IntersectContextExt { - context: IntersectContext::incoherent(), - extra, + ctx: IntersectContext::incoherent(), + ext: extra, } } } diff --git a/src/ray.rs b/src/ray.rs index 4043f4220..b7d6059ee 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -9,55 +9,6 @@ pub use packet::*; pub use soa::*; pub use stream::*; -/// Trait for types that can be converted to a [`Ray`]. -/// -/// Embree uses poor man's inheritance to make it possible to extend the [`Ray`] -/// and [`RayHit`] types with additional data. This trait provides a way to -/// convert between the base types and the extended types. See also the -/// [`AsRayHit`] -/// -/// # Safety -/// -/// Structs that implement this trait must guarantee that they are -/// layout-compatible with [`Ray`] (i.e. pointer casts between the two types are -/// valid). The corresponding pattern in C is called poor man's inheritance. See -/// [`RayExt`] for an example of how to do this. -pub unsafe trait AsRay: Sized { - type RayExt: Sized; - - fn as_ray(&self) -> &Ray; - fn as_ray_mut(&mut self) -> &mut Ray; - fn as_ray_ext(&self) -> Option<&Self::RayExt>; - fn as_ray_ext_mut(&mut self) -> Option<&mut Self::RayExt>; - - fn as_ray_ptr(&self) -> *const Ray { self.as_ray() as *const Ray } - fn as_ray_mut_ptr(&mut self) -> *mut Ray { self.as_ray_mut() as *mut Ray } -} - -/// Trait for types that can be converted to a [`Ray`]. -/// -/// Embree uses poor man's inheritance to make it possible to extend the [`Ray`] -/// and [`RayHit`] types with additional data. This trait provides a way to -/// convert between the base types and the extended types. See also the -/// [`AsRay`] -/// -/// # Safety -/// -/// Structs that implement this trait must guarantee that they are layout -/// compatible with [`Ray`] (i.e. pointer casts between the two types are -/// valid). -pub unsafe trait AsRayHit: AsRay { - type RayHitExt: Sized; - - fn as_ray_hit(&self) -> &RayHit; - fn as_ray_hit_mut(&mut self) -> &mut RayHit; - fn as_ray_hit_ext(&self) -> Option<&Self::RayHitExt>; - fn as_ray_hit_ext_mut(&mut self) -> Option<&mut Self::RayHitExt>; - - fn as_ray_hit_ptr(&self) -> *const RayHit { self.as_ray_hit() as *const RayHit } - fn as_ray_hit_mut_ptr(&mut self) -> *mut RayHit { self.as_ray_hit_mut() as *mut RayHit } -} - /// New type alias for [`sys::RTCRay`] that provides some convenience /// methods. /// @@ -147,91 +98,6 @@ impl Ray { } } -unsafe impl AsRay for Ray { - type RayExt = (); - - fn as_ray(&self) -> &Ray { self } - fn as_ray_mut(&mut self) -> &mut Ray { self } - fn as_ray_ext(&self) -> Option<&()> { None } - fn as_ray_ext_mut(&mut self) -> Option<&mut ()> { None } -} - -/// Extended ray type that contains an additional data field. -/// -/// For the reason that the ray passed to the filter callback functions -/// and user geometry callback functions is guaranteed to be the same -/// ray pointer initially provided to the ray query function by the user, -/// it is SAFE to extend the ray by additional data and access this data -/// inside the filter callback functions (e.g. to accumulate opacity) and -/// user geometry callback functions (e.g. to accumulate color). -/// -/// To make sure that the extended ray type is layout-compatible with -/// the ray type, the additional data field must be `#[repr(C)]`. -#[repr(C)] -#[derive(Clone, Copy)] -pub struct RayExt { - pub ray: Ray, - pub ext: E, -} - -impl Debug for RayExt -where - E: Debug + Sized + Clone + Copy, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("RayExt") - .field("ray", &self.ray) - .field("ext", &self.ext) - .finish() - } -} - -impl RayExt -where - E: Sized + Copy + Clone, -{ - pub fn new(ray: Ray, ext: E) -> Self { Self { ray, ext } } -} - -#[test] -fn test_ray_ext_pointer_compatability_with_ray_pointer() { - let ray = RayExt { - ray: Ray::new([0.0, 0.0, 0.0], [1.0, 0.0, 0.0]), - ext: 10u32, - }; - assert_eq!( - &ray as *const RayExt as *const Ray, - &ray.ray as *const Ray - ); - - #[repr(C)] - #[derive(Clone, Copy, Debug)] - struct Extra { - a: u32, - b: u32, - c: f32, - d: f32, - } - - let mut ray2 = RayExt:: { - ray: Ray::new([0.0, 0.0, 0.0], [1.0, 0.0, 0.0]), - ext: Extra { - a: 1, - b: 2, - c: 3.0, - d: 4.0, - }, - }; - - let ray_ptr = &mut ray2 as *mut RayExt as *mut Ray; - let ray_ext = unsafe { &mut *(ray_ptr as *mut RayExt) }; - ray_ext.ext.a = 10; - ray_ext.ext.d = 40.0; - - assert_eq!(ray2.ext.a, 10); - assert_eq!(ray2.ext.d, 40.0); -} - /// New type alias for [`sys::RTCHit`] that provides some convenience /// methods. /// @@ -329,208 +195,3 @@ impl RayHit { impl From for RayHit { fn from(value: Ray) -> Self { RayHit::from_ray(value) } } - -unsafe impl AsRay for RayHit { - type RayExt = Hit; - - fn as_ray(&self) -> &Ray { &self.ray } - - fn as_ray_mut(&mut self) -> &mut Ray { &mut self.ray } - - fn as_ray_ext(&self) -> Option<&Self::RayExt> { Some(&self.hit) } - - fn as_ray_ext_mut(&mut self) -> Option<&mut Self::RayExt> { Some(&mut self.hit) } -} - -unsafe impl AsRayHit for RayHit { - type RayHitExt = (); - - fn as_ray_hit(&self) -> &RayHit { self } - - fn as_ray_hit_mut(&mut self) -> &mut RayHit { self } - - fn as_ray_hit_ext(&self) -> Option<&Self::RayHitExt> { None } - - fn as_ray_hit_ext_mut(&mut self) -> Option<&mut Self::RayHitExt> { None } -} - -#[repr(C)] -#[derive(Clone, Copy)] -pub struct RayHitAsRayExtra { - pub hit: Hit, - pub ext: E, -} - -impl Debug for RayHitAsRayExtra -where - E: Debug + Sized + Copy + Clone, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("RayHitExtra") - .field("hit", &self.hit) - .field("ext", &self.ext) - .finish() - } -} - -/// Extended ray type that contains an additional data field. -/// -/// To make sure that the extended ray type is layout-compatible with -/// the ray type, the additional data field must be `#[repr(C)]`. -#[repr(C)] -#[repr(align(16))] -#[derive(Clone, Copy)] -pub struct RayHitExt { - pub ray: RayHit, - pub ext: E, -} - -impl Debug for RayHitExt -where - E: Debug + Sized + Clone + Copy, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("RayHitExt") - .field("ray", &self.ray) - .field("ext", &self.ext) - .finish() - } -} - -impl RayHitExt -where - E: Sized + Clone + Copy, -{ - pub fn new(ray_hit: RayHit, ext: E) -> Self { Self { ray: ray_hit, ext } } - - pub fn new_with_ray(ray: Ray, ext: E) -> Self { - Self { - ray: RayHit::from_ray(ray), - ext, - } - } -} - -unsafe impl AsRay for RayHitExt -where - E: Sized + Clone + Copy, -{ - type RayExt = RayHitAsRayExtra; - - fn as_ray(&self) -> &Ray { &self.ray.ray } - - fn as_ray_mut(&mut self) -> &mut Ray { &mut self.ray.ray } - - fn as_ray_ext(&self) -> Option<&Self::RayExt> { - Some(unsafe { &*(&self.ray.hit as *const Hit as *const Self::RayExt) }) - } - - fn as_ray_ext_mut(&mut self) -> Option<&mut Self::RayExt> { - Some(unsafe { &mut *(&mut self.ray.hit as *mut Hit as *mut Self::RayExt) }) - } -} - -unsafe impl AsRayHit for RayHitExt -where - E: Sized + Copy + Clone, -{ - type RayHitExt = E; - - fn as_ray_hit(&self) -> &RayHit { &self.ray } - - fn as_ray_hit_mut(&mut self) -> &mut RayHit { &mut self.ray } - - fn as_ray_hit_ext(&self) -> Option<&Self::RayHitExt> { Some(&self.ext) } - - fn as_ray_hit_ext_mut(&mut self) -> Option<&mut Self::RayHitExt> { Some(&mut self.ext) } -} - -#[test] -fn test_ray_hit_ext_pointer_compatability_with_ray_hit() { - let ray = RayHitExt { - ray: RayHit::from_ray(Ray::new([0.0, 0.0, 0.0], [1.0, 0.0, 0.0])), - ext: [0.0f32, 0.0, 0.0, 0.0], - }; - assert_eq!( - &ray as *const RayHitExt<[f32; 4]> as *const RayHit, - &ray.ray as *const RayHit - ); - - #[repr(C)] - #[derive(Clone, Copy)] - struct Extra { - a: i32, - b: u64, - c: f32, - d: [u8; 4], - } - - let mut ray = RayHitExt:: { - ray: RayHit::from_ray(Ray::new([0.0, 0.0, 0.0], [1.0, 0.0, 0.0])), - ext: Extra { - a: 1, - b: 2, - c: 3.0, - d: [4, 5, 6, 7], - }, - }; - - let ray_ptr = &mut ray as *mut RayHitExt as *mut RayHit; - let ray_ext = unsafe { &mut *(ray_ptr as *mut RayHitExt) }; - ray_ext.ext.a = 10; - ray_ext.ext.d = [30, 31, 32, 33]; - - assert_eq!(ray.ext.a, 10); - assert_eq!(ray.ext.d, [30, 31, 32, 33]); - - ray.as_ray_hit_ext_mut().unwrap().a = 20; - - assert_eq!(ray.ext.a, 20); -} - -#[test] -fn test_ray_hit_ext_pointer_compatability_with_ray() { - let ray = RayHitExt { - ray: RayHit::from_ray(Ray::new([0.0, 0.0, 0.0], [1.0, 0.0, 0.0])), - ext: [0.0f32, 0.0, 0.0, 0.0], - }; - assert_eq!( - &ray as *const RayHitExt<[f32; 4]> as *const RayHit, - &ray.ray as *const RayHit - ); - - #[repr(C)] - #[derive(Clone, Copy)] - struct Extra { - a: i32, - b: u64, - d: [u8; 4], - } - - let mut ray = RayHitExt:: { - ray: RayHit::from_ray(Ray::new([0.0, 0.0, 0.0], [1.0, 0.0, 0.0])), - ext: Extra { - a: 1, - b: 2, - d: [4, 5, 6, 7], - }, - }; - - ray.as_ray_mut().org_x = 10.0; - ray.as_ray_mut().dir_x = 20.0; - - match ray.as_ray_ext_mut() { - None => {} - Some(ray_ext) => { - ray_ext.hit.geomID = 30; - ray_ext.hit.primID = 40; - ray_ext.ext.d = [50, 51, 52, 53]; - } - } - - assert_eq!(ray.ray.ray.org(), [10.0, 0.0, 0.0]); - assert_eq!(ray.ray.ray.dir(), [20.0, 0.0, 0.0]); - assert_eq!(ray.ext.d, [50, 51, 52, 53]); - assert_eq!(ray.ray.hit.geomID, 30); - assert_eq!(ray.ray.hit.primID, 40); -} diff --git a/src/scene.rs b/src/scene.rs index 6c57ef8cc..80d213da6 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,5 +1,5 @@ use crate::{ - AsIntersectContext, AsRay, AsRayHit, Bounds, Error, PointQuery, PointQueryContext, Ray16, Ray8, + AsIntersectContext, Bounds, Error, PointQuery, PointQueryContext, Ray, Ray16, Ray8, RayHit, RayHit16, RayHit8, RayHitNp, RayHitPacket, RayPacket, SceneFlags, }; use std::{ @@ -444,12 +444,12 @@ impl<'a> Scene<'a> { /// 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 R) { + pub fn intersect(&self, ctx: &mut C, ray: &mut RayHit) { unsafe { rtcIntersect1( self.handle, ctx.as_intersect_context_mut_ptr(), - ray.as_ray_hit_mut_ptr(), + ray as *mut _ as *mut _, ); } } @@ -571,13 +571,12 @@ impl<'a> Scene<'a> { /// additional data. See [`IntersectContext`] for more information. /// /// * `ray` - The ray to intersect with the scene. - pub fn occluded(&self, ctx: &mut C, ray: &mut R) -> bool { - let ray = ray.as_ray_mut(); + pub fn occluded(&self, ctx: &mut C, ray: &mut Ray) -> bool { unsafe { rtcOccluded1( self.handle, ctx.as_intersect_context_mut_ptr(), - ray.as_ray_mut_ptr(), + ray as *mut RTCRay, ); } ray.tfar == -f32::INFINITY From b06d3b309f5791f1b82f388e51a4588899995b36 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 2 Mar 2023 18:34:57 +0100 Subject: [PATCH 56/65] fix instancing & intersect filter --- examples/instancing/src/main.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/instancing/src/main.rs b/examples/instancing/src/main.rs index f0bf2283b..f5bae02e8 100644 --- a/examples/instancing/src/main.rs +++ b/examples/instancing/src/main.rs @@ -4,7 +4,7 @@ extern crate support; use cgmath::{InnerSpace, Matrix, Matrix4, SquareMatrix, Vector3, Vector4}; use embree::{ - BufferUsage, BuildQuality, Device, Format, Geometry, Instance, IntersectContext, Ray, + BufferUsage, BuildQuality, Device, Format, Geometry, Instance, IntersectContext, Ray, RayHit, SceneFlags, INVALID_ID, }; use support::Camera; @@ -228,10 +228,8 @@ fn main() { 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_hit = scene.intersect( - &mut intersection_ctx, - Ray::new(camera.pos.into(), dir.into()), - ); + let mut ray_hit = RayHit::from_ray(Ray::new(camera.pos.into(), dir.into())); + scene.intersect(&mut intersection_ctx, &mut ray_hit); if ray_hit.is_valid() { // Transform the normals of the instances into world space with the From d62f216ac6ee7f7cc0295e6c33f046607ad26fa3 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 2 Mar 2023 22:05:22 +0100 Subject: [PATCH 57/65] update some callback functions --- src/geometry.rs | 111 +++++++++++++++++++++++------------------------- 1 file changed, 54 insertions(+), 57 deletions(-) diff --git a/src/geometry.rs b/src/geometry.rs index f821830b1..a5333627a 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -47,11 +47,11 @@ pub(crate) struct GeometryUserData { /// [`GeometryKind::USER`]. #[derive(Debug, Clone)] pub(crate) struct UserGeometryPayloads { - /// Payload for the [`UserGeometry::set_intersect_function`] call. + /// Payload for the [`Geometry::set_intersect_function`] call. pub intersect_fn: *mut std::os::raw::c_void, - /// Payload for the [`UserGeometry::set_occluded_function`] call. + /// Payload for the [`Geometry::set_occluded_function`] call. pub occluded_fn: *mut std::os::raw::c_void, - /// Payload for the [`UserGeometry::set_bounds_function`] call. + /// Payload for the [`Geometry::set_bounds_function`] call. pub bounds_fn: *mut std::os::raw::c_void, } @@ -59,7 +59,7 @@ pub(crate) struct UserGeometryPayloads { /// [`GeometryKind::SUBDIVISION`]. #[derive(Debug, Clone)] pub(crate) struct SubdivisionGeometryPayloads { - /// Payload for the [`SubdivisionGeometry::set_vertex_function`] call. + /// Payload for the [`Geometry::set_displacement_function`] call. pub displacement_fn: *mut std::os::raw::c_void, } @@ -1167,7 +1167,7 @@ impl<'buf> Geometry<'buf> { pub fn set_bounds_function(&mut self, bounds: F) where D: UserGeometryData, - F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), + F: FnMut(&mut Bounds, u32, u32, Option<&mut D>), { match self.kind { GeometryKind::USER => unsafe { @@ -1253,17 +1253,11 @@ impl<'buf> Geometry<'buf> { /// 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) + pub fn set_intersect_function(&mut self, intersect: F) where D: UserGeometryData, - F: for<'a> FnMut( - &'a mut [i32], - Option<&mut D>, - u32, - u32, - &mut IntersectContext, - RayHitN<'a>, - ), + C: AsIntersectContext, + F: for<'a> FnMut(RayHitN<'a>, ValidMasks<'a>, &mut C, u32, u32, Option<&mut D>), { match self.kind { GeometryKind::USER => unsafe { @@ -1291,10 +1285,11 @@ impl<'buf> Geometry<'buf> { /// /// Similar to [`Geometry::set_intersect_function`], but for occlusion /// queries. - pub fn set_occluded_function(&mut self, occluded: F) + pub fn set_occluded_function(&mut self, occluded: F) where D: UserGeometryData, - F: for<'a> FnMut(&'a mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayN<'a>), + C: AsIntersectContext, + F: for<'a> FnMut(&'a mut [i32], Option<&mut D>, u32, u32, &mut C, RayN<'a>), { match self.kind { GeometryKind::USER => { @@ -1470,7 +1465,7 @@ impl<'buf> Geometry<'buf> { pub unsafe fn set_displacement_function(&mut self, displacement: F) where D: UserGeometryData, - F: for<'a> FnMut(RTCGeometry, Option<&mut D>, u32, u32, Vertices<'a>), + F: for<'a> FnMut(RTCGeometry, Vertices<'a>, u32, u32, Option<&mut D>), { match self.kind { GeometryKind::SUBDIVISION => { @@ -1888,17 +1883,17 @@ where let len = (*args).N as usize; cb( RayN { - ptr: &mut *(*args).ray, + ptr: (*args).ray, len, marker: PhantomData, }, HitN { - ptr: &mut *(*args).hit, + ptr: (*args).hit, len, marker: PhantomData, }, ValidMasks { - ptr: &mut *(*args).valid, + ptr: (*args).valid, len, marker: PhantomData, }, @@ -1942,17 +1937,17 @@ where }; cb( RayN { - ptr: &mut *(*args).ray, + ptr: (*args).ray, len, marker: PhantomData, }, HitN { - ptr: &mut *(*args).hit, + ptr: (*args).hit, len, marker: PhantomData, }, ValidMasks { - ptr: &mut *(*args).valid, + ptr: (*args).valid, len, marker: PhantomData, }, @@ -1969,12 +1964,12 @@ where fn bounds_function(_f: &mut F) -> RTCBoundsFunction where D: UserGeometryData, - F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), + F: FnMut(&mut Bounds, u32, u32, Option<&mut D>), { unsafe extern "C" fn inner(args: *const RTCBoundsFunctionArguments) where D: UserGeometryData, - F: FnMut(Option<&mut D>, u32, u32, &mut Bounds), + F: FnMut(&mut Bounds, u32, u32, Option<&mut D>), { let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) .user_fns @@ -1999,10 +1994,10 @@ where } }; cb( - user_data, + &mut *(*args).bounds_o, (*args).primID, (*args).timeStep, - &mut *(*args).bounds_o, + user_data, ); } } @@ -2012,22 +2007,17 @@ where /// Helper function to convert a Rust closure to `RTCIntersectFunctionN` /// callback. -fn intersect_function(_f: &mut F) -> RTCIntersectFunctionN +fn intersect_function(_f: &mut F) -> RTCIntersectFunctionN where D: UserGeometryData, - F: for<'a> FnMut(&'a mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayHitN<'a>), + C: AsIntersectContext, + F: for<'a> FnMut(RayHitN<'a>, ValidMasks<'a>, &mut C, u32, u32, Option<&mut D>), { - unsafe extern "C" fn inner(args: *const RTCIntersectFunctionNArguments) + unsafe extern "C" fn inner(args: *const RTCIntersectFunctionNArguments) where D: UserGeometryData, - F: for<'a> FnMut( - &'a mut [i32], - Option<&mut D>, - u32, - u32, - &mut IntersectContext, - RayHitN<'a>, - ), + C: AsIntersectContext, + F: for<'a> FnMut(RayHitN<'a>, ValidMasks<'a>, &mut C, u32, u32, Option<&mut D>), { let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) .user_fns @@ -2037,6 +2027,7 @@ where 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 = { @@ -2052,34 +2043,40 @@ where } }; cb( - std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), - user_data, - (*args).geomID, - (*args).primID, - &mut *(*args).context, RayHitN { ptr: (*args).rayhit, - len: (*args).N as usize, + len, + marker: PhantomData, + }, + ValidMasks { + ptr: (*args).valid, + len, marker: PhantomData, }, + &mut *((*args).context as *mut _ as *mut C), + (*args).geomID, + (*args).primID, + user_data, ); } } - Some(inner::) + Some(inner::) } /// Helper function to convert a Rust closure to `RTCOccludedFunctionN` /// callback. -fn occluded_function(_f: &mut F) -> RTCOccludedFunctionN +fn occluded_function(_f: &mut F) -> RTCOccludedFunctionN where D: UserGeometryData, - F: for<'a> FnMut(&'a mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayN<'a>), + C: AsIntersectContext, + F: for<'a> FnMut(&'a mut [i32], Option<&mut D>, u32, u32, &mut C, RayN<'a>), { - unsafe extern "C" fn inner(args: *const RTCOccludedFunctionNArguments) + unsafe extern "C" fn inner(args: *const RTCOccludedFunctionNArguments) where D: UserGeometryData, - F: for<'a> FnMut(&'a mut [i32], Option<&mut D>, u32, u32, &mut IntersectContext, RayN<'a>), + C: AsIntersectContext, + F: for<'a> FnMut(&'a mut [i32], Option<&mut D>, u32, u32, &mut C, RayN<'a>), { let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) .user_fns @@ -2108,7 +2105,7 @@ where user_data, (*args).geomID, (*args).primID, - &mut *(*args).context, + &mut *((*args).context as *mut _ as *mut C), RayN { ptr: (*args).ray, len: (*args).N as usize, @@ -2118,7 +2115,7 @@ where } } - Some(inner::) + Some(inner::) } /// Struct holding data for a set of vertices in SoA layout. @@ -2197,12 +2194,12 @@ impl<'a> ExactSizeIterator for VerticesIterMut<'a> { fn displacement_function(_f: &mut F) -> RTCDisplacementFunctionN where D: UserGeometryData, - F: for<'a> FnMut(RTCGeometry, Option<&mut D>, u32, u32, Vertices<'a>), + F: for<'a> FnMut(RTCGeometry, Vertices<'a>, u32, u32, Option<&mut D>), { unsafe extern "C" fn inner(args: *const RTCDisplacementFunctionNArguments) where D: UserGeometryData, - F: FnMut(RTCGeometry, Option<&mut D>, u32, u32, Vertices), + F: for<'a> FnMut(RTCGeometry, Vertices<'a>, u32, u32, Option<&mut D>), { let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) .subdivision_fns @@ -2241,10 +2238,10 @@ where }; cb( (*args).geometry, - user_data, + vertices, (*args).primID, (*args).timeStep, - vertices, + user_data, ); } } @@ -2253,8 +2250,8 @@ where } /// Struct holding data for valid masks used in the callback function set by -/// [`Geometry::set_intersection_filter_function`], -/// [`Geometry::set_occlusion_filter_function`]. +/// [`Geometry::set_intersect_filter_function`], +/// [`Geometry::set_occluded_filter_function`]. pub struct ValidMasks<'a> { ptr: *const i32, len: usize, From c170d09ab67bae7b97c1d952287c5d32f456d76e Mon Sep 17 00:00:00 2001 From: matthiascy Date: Mon, 6 Mar 2023 20:11:16 +0100 Subject: [PATCH 58/65] api ergonomics improvement & performace boost & intersection filter --- .gitignore | 1 + Cargo.toml | 1 + examples/displacement_geometry/src/main.rs | 72 ++-- examples/dynamic_scene/Cargo.toml | 2 +- examples/dynamic_scene/src/main.rs | 11 +- examples/instancing/src/main.rs | 193 +++++++---- examples/intersection_filter/src/main.rs | 300 +++++++++++++++-- examples/minimal/src/main.rs | 4 +- examples/support/Cargo.toml | 6 +- examples/support/src/display.rs | 56 ++-- examples/support/src/lib.rs | 37 ++ .../{ => todos}/curve_geometry/Cargo.toml | 0 .../{ => todos}/curve_geometry/src/main.rs | 0 .../{ => todos}/obj_ao_parallel/Cargo.toml | 0 .../{ => todos}/obj_ao_parallel/src/main.rs | 0 examples/{ => todos}/obj_viewer/Cargo.toml | 0 examples/{ => todos}/obj_viewer/src/main.rs | 0 examples/triangle/src/main.rs | 4 +- examples/triangle_geometry/src/main.rs | 11 +- scripts/build-examples-linux-mac.sh | 17 +- scripts/build-test-mac.sh | 17 +- scripts/build-test-windows.ps1 | 9 +- scripts/check-examples-formatting.sh | 6 +- src/context.rs | 44 +-- src/device.rs | 14 +- src/geometry.rs | 317 +++++++++++------- src/lib.rs | 45 ++- src/ray.rs | 152 +++++---- src/ray/packet.rs | 249 +++++++------- src/ray/soa.rs | 19 +- src/ray/stream.rs | 15 +- src/scene.rs | 116 ++++--- 32 files changed, 1153 insertions(+), 565 deletions(-) rename examples/{ => todos}/curve_geometry/Cargo.toml (100%) rename examples/{ => todos}/curve_geometry/src/main.rs (100%) rename examples/{ => todos}/obj_ao_parallel/Cargo.toml (100%) rename examples/{ => todos}/obj_ao_parallel/src/main.rs (100%) rename examples/{ => todos}/obj_viewer/Cargo.toml (100%) rename examples/{ => todos}/obj_viewer/src/main.rs (100%) 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 6f5a2e08c..c99c27a9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,3 +25,4 @@ exclude = [ members = [ "examples/*", ] +exclude = ["examples/todos"] diff --git a/examples/displacement_geometry/src/main.rs b/examples/displacement_geometry/src/main.rs index f2cf23d70..470d87eff 100644 --- a/examples/displacement_geometry/src/main.rs +++ b/examples/displacement_geometry/src/main.rs @@ -1,6 +1,6 @@ use embree::{ - BufferSlice, BufferUsage, Device, Format, Geometry, GeometryKind, InterpolateInput, - InterpolateOutput, IntersectContext, Ray, RayHit, Scene, SceneFlags, + BufferSlice, BufferUsage, Device, Format, Geometry, GeometryKind, IntersectContext, Ray, + RayHit, Scene, SceneFlags, }; use glam::{vec3, Vec3}; use support::{ @@ -11,6 +11,7 @@ use support::{ 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: Align16Array = Align16Array([ @@ -48,6 +49,7 @@ fn displacement(p: [f32; 3]) -> f32 { 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 @@ -96,7 +98,7 @@ fn create_cube(device: &Device) -> Geometry<'static> { .copy_from_slice(&[EDGE_LEVEL; NUM_INDICES]); unsafe { geom.set_displacement_function( - |raw_geom, user_data: Option<&mut ()>, prim_id, _, vertices| { + |_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]]; @@ -193,11 +195,16 @@ fn render_pixel( ) -> 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::new(camera.pos.into(), dir.into())); + 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.is_valid() { + if ray_hit.hit.is_valid() { let diffuse = if ray_hit.hit.geomID == cube_id { vec3(0.9, 0.6, 0.5) } else { @@ -205,34 +212,37 @@ fn render_pixel( }; color += diffuse * 0.5; - let mut normal = glam::Vec3::from(ray_hit.hit.normal_normalized()); - - #[cfg(feature = "smooth_normals")] - { - let hit_point: glam::Vec3 = ray_hit.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 = glam::Vec3::from_slice(output.dp_du().as_ref().unwrap()); - let mut dp_dv = glam::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); - normal = dp_du.cross(dp_dv).normalize(); + 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.hit_point(), LIGHT_DIR, 0.001, f32::INFINITY); + 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) { diff --git a/examples/dynamic_scene/Cargo.toml b/examples/dynamic_scene/Cargo.toml index 6742709cb..6709a5b11 100644 --- a/examples/dynamic_scene/Cargo.toml +++ b/examples/dynamic_scene/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Yang Chen "] edition = "2021" [dependencies] -glam = "0.22.0" +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 index 4bf29c868..82490444b 100644 --- a/examples/dynamic_scene/src/main.rs +++ b/examples/dynamic_scene/src/main.rs @@ -226,13 +226,18 @@ fn render_pixel( ) { 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::new(camera.pos.into(), dir.into())); + 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.is_valid() { + if ray_hit.hit.is_valid() { let diffuse = colors[ray_hit.hit.geomID as usize]; - let mut shadow_ray = Ray::segment(ray_hit.hit_point(), LIGHT_DIR, 0.001, f32::INFINITY); + 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) { diff --git a/examples/instancing/src/main.rs b/examples/instancing/src/main.rs index f5bae02e8..20929756c 100644 --- a/examples/instancing/src/main.rs +++ b/examples/instancing/src/main.rs @@ -5,9 +5,12 @@ extern crate support; use cgmath::{InnerSpace, Matrix, Matrix4, SquareMatrix, Vector3, Vector4}; use embree::{ BufferUsage, BuildQuality, Device, Format, Geometry, Instance, IntersectContext, Ray, RayHit, - SceneFlags, INVALID_ID, + Scene, SceneFlags, INVALID_ID, +}; +use support::{ + rgba_to_u32, Camera, ParallelIterator, RgbaImage, TiledImage, DEFAULT_DISPLAY_WIDTH, + TILE_SIZE_X, TILE_SIZE_Y, }; -use support::Camera; const NUM_PHI: usize = 5; const NUM_THETA: usize = 2 * NUM_PHI; @@ -134,8 +137,16 @@ fn create_ground_plane(device: &Device) -> Geometry<'static> { 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; @@ -145,18 +156,21 @@ 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 * 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 State { + transforms: Vec>, + normal_transforms: Vec>, + ground_plane_id: u32, + light_dir: Vector3, } fn main() { @@ -200,16 +214,34 @@ fn main() { let ground_plane = create_ground_plane(&device); let ground_plane_id = scene.attach_geometry(&ground_plane); - let light_dir = Vector3::new(1.0, 1.0, -1.0).normalize(); - let mut intersection_ctx = IntersectContext::coherent(); + let mut state = State { + 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 mut tiled = TiledImage::new( + DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_WIDTH, + TILE_SIZE_X, + TILE_SIZE_Y, + ); + + let mut last_time = 0.0; support::display::run(display, move |image, camera_pose, time| { for p in image.iter_mut() { *p = 0; } // Update scene transformations - let (transforms, normal_transforms) = animate_instances(time, instances.len()); - for (inst, tfm) in instances.iter_mut().zip(transforms.iter()) { + animate_instances( + time, + instances.len(), + &mut state.transforms, + &mut state.normal_transforms, + ); + for (inst, tfm) in instances.iter_mut().zip(state.transforms.iter()) { inst.set_transform(0, tfm.as_ref()); inst.commit(); } @@ -224,50 +256,95 @@ fn main() { img_dims, ); - // 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::from_ray(Ray::new(camera.pos.into(), dir.into())); - scene.intersect(&mut intersection_ctx, &mut ray_hit); - - if ray_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::new(hit.Ng_x, hit.Ng_y, hit.Ng_z).normalize(); - if inst_id != INVALID_ID { - 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.into(), light_dir.into(), 0.001, f32::INFINITY); - scene.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 == INVALID_ID && geom_id == ground_plane_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 = &COLORS[inst_id as usize][geom_id as usize]; - p[0] = (255.0 * illum * color[0]) as u8; - p[1] = (255.0 * illum * color[1]) as u8; - p[2] = (255.0 * illum * color[2]) as u8; - } - } - } + render_frame(&mut tiled, image, time, &scene, &camera, &state); + + let elapsed = time - last_time; + last_time = time; + let fps = 1.0 / elapsed; + eprint!("\r{} fps", fps); + }); +} + +fn render_pixel( + x: u32, + y: u32, + pixel: &mut u32, + _time: f32, + scene: &Scene, + camera: &Camera, + state: &State, +) { + 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, + )); + scene.intersect(&mut ctx, &mut ray_hit); + + 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.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.light_dir.into(), + 0.001, + f32::INFINITY, + ); + scene.occluded(&mut ctx, &mut shadow_ray); + + if shadow_ray.tfar >= 0.0 { + illum = support::clamp(illum + f32::max(state.light_dir.dot(normal), 0.0), 0.0, 1.0); } + + *pixel = if inst_id == INVALID_ID && geom_id == state.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( + tiled: &mut TiledImage, + frame: &mut RgbaImage, + time: f32, + scene: &Scene, + camera: &Camera, + state: &State, +) { + tiled.reset_pixels(); + tiled.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, scene, camera, &state); + }); }); + tiled.write_to_image(frame); } diff --git a/examples/intersection_filter/src/main.rs b/examples/intersection_filter/src/main.rs index d13d1ea33..3df1761b3 100644 --- a/examples/intersection_filter/src/main.rs +++ b/examples/intersection_filter/src/main.rs @@ -11,9 +11,9 @@ use embree::{ BufferSlice, BufferUsage, BuildQuality, Device, Format, Geometry, GeometryKind, HitN, - IntersectContext, IntersectContextExt, Ray, RayHit, RayN, Scene, ValidMasks, + IntersectContextExt, Ray, RayHit, RayN, Scene, SoAHit, SoARay, ValidityN, INVALID_ID, }; -use glam::{vec3, Mat3, Mat4, Vec3, Vec4}; +use glam::{vec3, Mat4, Vec3, Vec4}; use support::{ rgba_to_u32, Align16Array, Camera, Mode, ParallelIterator, RgbaImage, Tile, TiledImage, DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH, TILE_SIZE_X, TILE_SIZE_Y, @@ -25,6 +25,8 @@ 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], @@ -40,7 +42,6 @@ const COLORS: [[f32; 3]; 12] = [ [1.0, 1.0, 0.0], [1.0, 1.0, 0.0], ]; -const MODE: Mode = Mode::Normal; const CUBE_VERTICES: Align16Array<[f32; 4], CUBE_NUM_VERTICES> = Align16Array([ [-1.0, -1.0, -1.0, 1.0], @@ -53,6 +54,7 @@ const CUBE_VERTICES: Align16Array<[f32; 4], CUBE_NUM_VERTICES> = Align16Array([ [1.0, 1.0, 1.0, 1.0], ]); +#[allow(dead_code)] const CUBE_QUAD_INDICES: Align16Array = Align16Array([ 0, 1, 3, 2, // 5, 4, 6, 7, // @@ -77,6 +79,7 @@ const CUBE_TRI_INDICES: Align16Array = Align16Array([ 5, 7, 3, // ]); +#[allow(dead_code)] const CUBE_QUAD_FACES: [u32; CUBE_NUM_QUAD_FACES] = [4; 6]; #[repr(C)] @@ -101,19 +104,24 @@ impl Default for RayExtra { } } -fn transparency_function(h: Vec3) -> f32 { - let v = ((4.0 * h.x).sin() * (4.0 * h.y).cos() * (4.0 * h.z).sin()).abs(); +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_with_id( + 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 { @@ -128,7 +136,7 @@ fn render_pixel(x: u32, y: u32, camera: &Camera, scene: &Scene) -> u32 { loop { scene.intersect(&mut primary_ctx, &mut primary); - if !primary.is_valid() { + if !primary.hit.is_valid() { break; } @@ -139,15 +147,19 @@ fn render_pixel(x: u32, y: u32, camera: &Camera, scene: &Scene) -> u32 { let light_dir = vec3(0.57, 0.57, 0.57); // initialize shadow ray - let mut shadow_ray = - Ray::segment(primary.hit_point(), light_dir.into(), 0.001, f32::INFINITY); - shadow_ray.id = 0; + 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.normal_normalized().into()) // + .dot(primary.hit.unit_normal().into()) // .clamp(0.0, 1.0); color += weight * opacity * ll; } @@ -176,10 +188,147 @@ fn render_tile(tile: &mut Tile, camera: &Camera, scene: &Scene) { }); } -fn render_tile_stream(tile: &mut Tile, camera: &Camera, scene: &Scene) { todo!() } +fn render_tile_stream(tile: &mut Tile, 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(tiled: &mut TiledImage, frame: &mut RgbaImage, camera: &Camera, scene: &Scene) { tiled.reset_pixels(); + let width = tiled.width; + let height = tiled.height; match MODE { Mode::Normal => { tiled @@ -189,7 +338,7 @@ fn render_frame(tiled: &mut TiledImage, frame: &mut RgbaImage, camera: &Camera, Mode::Stream => { tiled .par_tiles_mut() - .for_each(|mut tile| render_tile_stream(&mut tile, camera, scene)); + .for_each(|mut tile| render_tile_stream(&mut tile, width, height, camera, scene)); } } tiled.write_to_image(frame); @@ -197,8 +346,8 @@ fn render_frame(tiled: &mut TiledImage, frame: &mut RgbaImage, camera: &Camera, fn intersect_filter<'a>( rays: RayN<'a>, - hits: HitN<'a>, - mut valid: ValidMasks<'a>, + _hits: HitN<'a>, + mut valid: ValidityN<'a>, ctx: &mut IntersectContext2, _user_data: Option<&mut ()>, ) { @@ -210,8 +359,7 @@ fn intersect_filter<'a>( } // calculate transparency - let h = Vec3::from(rays.org(0)) + Vec3::from(rays.dir(0)) * rays.tfar(0); - let t = transparency_function(h); + let t = transparency_function(rays.hit_point(0)); // ignore hit if completely transparent if t >= 1.0 { @@ -222,10 +370,44 @@ fn intersect_filter<'a>( } } -fn occlusion_filter<'a>( +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: ValidMasks<'a>, + mut valid: ValidityN<'a>, context: &mut IntersectContext2, _user_data: Option<&mut ()>, ) { @@ -255,15 +437,78 @@ fn occlusion_filter<'a>( context.ext.first_hit += 1; } - let h = Vec3::from(rays.org(0)) + Vec3::from(rays.dir(0)) * rays.tfar(0); - - let t = transparency_function(h); + 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(); { @@ -322,12 +567,11 @@ fn create_cube<'a>(device: &Device, offset: Vec3, scale: Vec3, rotation: f32) -> match MODE { Mode::Normal => { geom.set_intersect_filter_function(intersect_filter); - geom.set_occluded_filter_function(occlusion_filter); + geom.set_occluded_filter_function(occluded_filter); } Mode::Stream => { - // geom.set_intersect_filter_function(|_, _, _, _| {}); - // geom.set_occluded_filter_function(|_, _, _| {}); - todo!() + geom.set_intersect_filter_function(intersect_filter_n); + geom.set_occluded_filter_function(occluded_filter_n); } } geom.commit(); @@ -340,8 +584,8 @@ fn main() { 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); - let ground_id = scene.attach_geometry(&ground); - let cube_id = scene.attach_geometry(&cube); + scene.attach_geometry(&ground); + scene.attach_geometry(&cube); scene.commit(); let display = support::Display::new( diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index d37bbeb12..093716749 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -4,7 +4,7 @@ use embree::{BufferUsage, Device, Format, GeometryKind, IntersectContext, Ray, R /// Casts a single ray with the given origin and direction. fn cast_ray(scene: &Scene, origin: [f32; 3], direction: [f32; 3]) { - let ray = Ray::new(origin, direction); + 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. @@ -16,7 +16,7 @@ fn cast_ray(scene: &Scene, origin: [f32; 3], direction: [f32; 3]) { print!("{origin:?} "); - if ray_hit.is_valid() { + 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 diff --git a/examples/support/Cargo.toml b/examples/support/Cargo.toml index cc7f4a4b3..1e6816fdb 100644 --- a/examples/support/Cargo.toml +++ b/examples/support/Cargo.toml @@ -5,12 +5,12 @@ 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" +wgpu = "0.15.1" +winit = "0.28.1" futures = {version = "0.3", features = ["executor"]} rayon = "1.5" diff --git a/examples/support/src/display.rs b/examples/support/src/display.rs index 48b8f27ee..6ff402158 100644 --- a/examples/support/src/display.rs +++ b/examples/support/src/display.rs @@ -9,7 +9,10 @@ use image::RgbaImage; use wgpu; use winit::{ dpi::{LogicalSize, Size}, - event::{ElementState, Event, MouseButton, MouseScrollDelta, VirtualKeyCode, WindowEvent}, + event::{ + ElementState, Event, KeyboardInput, MouseButton, MouseScrollDelta, VirtualKeyCode, + WindowEvent, + }, event_loop::{ControlFlow, EventLoop}, window::{Window, WindowBuilder}, }; @@ -20,11 +23,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 +37,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 +57,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, @@ -83,8 +88,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(), @@ -143,13 +152,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)), }); @@ -183,6 +192,7 @@ where dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8Unorm, usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[], }); let bindgroup_layout = @@ -241,7 +251,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 +289,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, }); @@ -301,11 +313,13 @@ where 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 - } + 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, @@ -371,14 +385,14 @@ where { 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, }); diff --git a/examples/support/src/lib.rs b/examples/support/src/lib.rs index dadb40f98..a2e845c79 100644 --- a/examples/support/src/lib.rs +++ b/examples/support/src/lib.rs @@ -14,6 +14,7 @@ pub mod math { } /// The type of ray used for rendering with Embree. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Mode { /// A single ray. Normal, @@ -22,6 +23,8 @@ pub enum Mode { } /// 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. pub struct TiledImage { pub width: u32, pub height: u32, @@ -71,6 +74,40 @@ impl TiledImage { } } + pub fn tile_mut(&mut self, index: usize) -> Tile<'_> { + 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: &mut self.pixels[offset..offset + self.tile_size as usize], + } + } + + pub fn tiles_mut(&mut self) -> impl Iterator> { + 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; + 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> { self.pixels 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 100% rename from examples/curve_geometry/src/main.rs rename to examples/todos/curve_geometry/src/main.rs 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 100% rename from examples/obj_ao_parallel/src/main.rs rename to examples/todos/obj_ao_parallel/src/main.rs 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 100% rename from examples/obj_viewer/src/main.rs rename to examples/todos/obj_viewer/src/main.rs diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index cb00be930..a459dd475 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -60,8 +60,8 @@ fn main() { 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[0] = (uv[0] * 255.0) as u8; + p[1] = (uv[1] * 255.0) as u8; p[2] = 0; } } diff --git a/examples/triangle_geometry/src/main.rs b/examples/triangle_geometry/src/main.rs index fc6d18177..1f373a012 100644 --- a/examples/triangle_geometry/src/main.rs +++ b/examples/triangle_geometry/src/main.rs @@ -169,10 +169,15 @@ fn main() { 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::new(camera.pos.into(), dir.into())); + 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.is_valid() { + if ray_hit.hit.is_valid() { let diffuse = if ray_hit.hit.geomID == state.ground_id { glam::vec3(0.6, 0.6, 0.6) } else { @@ -180,7 +185,7 @@ fn render_pixel(x: u32, y: u32, _time: f32, camera: &Camera, state: &State) -> u }; let mut shadow_ray = Ray::segment( - ray_hit.hit_point(), + ray_hit.ray.hit_point(), state.light_dir.into(), 0.001, f32::INFINITY, diff --git a/scripts/build-examples-linux-mac.sh b/scripts/build-examples-linux-mac.sh index 987e8ee43..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 ./minimal ./dynamic_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 101ebb969..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 ./minimal ./dynamic_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/src/context.rs b/src/context.rs index 5b6a02a15..92ac23a68 100644 --- a/src/context.rs +++ b/src/context.rs @@ -12,19 +12,19 @@ use crate::sys::*; pub unsafe trait AsIntersectContext { type Ext; - fn as_intersect_context(&self) -> &IntersectContext; - fn as_intersect_context_mut(&mut self) -> &mut IntersectContext; + fn as_context(&self) -> &IntersectContext; + fn as_mut_context(&mut self) -> &mut IntersectContext; - fn as_intersect_context_ptr(&self) -> *const IntersectContext { - self.as_intersect_context() as *const IntersectContext + fn as_context_ptr(&self) -> *const IntersectContext { + self.as_context() as *const IntersectContext } - fn as_intersect_context_mut_ptr(&mut self) -> *mut IntersectContext { - self.as_intersect_context_mut() as *mut IntersectContext + fn as_mut_context_ptr(&mut self) -> *mut IntersectContext { + self.as_mut_context() as *mut IntersectContext } - fn as_intersect_context_ext(&self) -> Option<&Self::Ext>; - fn as_intersect_context_ext_mut(&mut self) -> Option<&mut Self::Ext>; + fn as_extended(&self) -> Option<&Self::Ext>; + fn as_mut_extended(&mut self) -> Option<&mut Self::Ext>; } /// Per ray-query intersection context. @@ -45,7 +45,7 @@ pub unsafe trait AsIntersectContext { /// ## Note /// /// The support for the context filter function must be enabled for a scene by -/// using the [`RTCSceneFlags::CONTEXT_FILTER_FUNCTION`] flag. +/// 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. @@ -54,9 +54,11 @@ pub unsafe trait AsIntersectContext { /// /// 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). +/// [`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 { @@ -82,15 +84,17 @@ impl IntersectContext { unsafe impl AsIntersectContext for IntersectContext { type Ext = (); - fn as_intersect_context(&self) -> &IntersectContext { self } + fn as_context(&self) -> &IntersectContext { self } - fn as_intersect_context_mut(&mut self) -> &mut IntersectContext { self } + fn as_mut_context(&mut self) -> &mut IntersectContext { self } - fn as_intersect_context_ext(&self) -> Option<&Self::Ext> { None } + fn as_extended(&self) -> Option<&Self::Ext> { None } - fn as_intersect_context_ext_mut(&mut self) -> Option<&mut 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. @@ -124,13 +128,13 @@ where { type Ext = E; - fn as_intersect_context(&self) -> &IntersectContext { &self.ctx } + fn as_context(&self) -> &IntersectContext { &self.ctx } - fn as_intersect_context_mut(&mut self) -> &mut IntersectContext { &mut self.ctx } + fn as_mut_context(&mut self) -> &mut IntersectContext { &mut self.ctx } - fn as_intersect_context_ext(&self) -> Option<&Self::Ext> { Some(&self.ext) } + fn as_extended(&self) -> Option<&Self::Ext> { Some(&self.ext) } - fn as_intersect_context_ext_mut(&mut self) -> Option<&mut Self::Ext> { Some(&mut self.ext) } + fn as_mut_extended(&mut self) -> Option<&mut Self::Ext> { Some(&mut self.ext) } } impl IntersectContextExt diff --git a/src/device.rs b/src/device.rs index 071c3a274..2d50ad7b4 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,4 +1,7 @@ -use crate::{sys::*, Buffer, BufferSize, Bvh, Error, Geometry, GeometryKind, Scene}; +use crate::{ + sys::*, Buffer, BufferSize, Bvh, DeviceProperty, Error, Geometry, GeometryKind, Scene, + SceneFlags, +}; use std::{ ffi::CString, fmt::{self, Display, Formatter}, @@ -171,7 +174,7 @@ impl Device { /// # Returns /// /// An integer of type `isize`. - pub fn get_property(&self, prop: RTCDeviceProperty) -> Result { + pub fn get_property(&self, prop: DeviceProperty) -> Result { let ret = unsafe { rtcGetDeviceProperty(self.handle, prop) }; if ret == 0 { Err(self.get_error()) @@ -200,8 +203,9 @@ impl Device { /// 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 [`SceneConfig`] for possible values. - pub fn create_scene_with_flags(&self, flags: RTCSceneFlags) -> Result { + /// + /// See [`SceneFlags`] for possible values. + pub fn create_scene_with_flags(&self, flags: SceneFlags) -> Result { Scene::new_with_flags(self.clone(), flags) } @@ -361,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"))] { diff --git a/src/geometry.rs b/src/geometry.rs index a5333627a..10e274a55 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -4,8 +4,7 @@ use std::{ use crate::{ sys::*, AsIntersectContext, Bounds, BufferSlice, BufferUsage, BuildQuality, Device, Error, - Format, GeometryKind, HitN, IntersectContext, QuaternionDecomposition, RayHitN, RayN, Scene, - SubdivisionMode, + Format, GeometryKind, HitN, QuaternionDecomposition, RayHitN, RayN, Scene, SubdivisionMode, }; use std::{ @@ -141,7 +140,7 @@ impl Default for GeometryData { /// 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]. +/// sub range of \[0,1\]. /// /// The API supports per-geometry filter callback functions (see /// [`Geometry::set_intersect_filter_function`] @@ -485,8 +484,8 @@ impl<'buf> Geometry<'buf> { } } - /// Creates a new [`Buffer`] and binds it as a specific attribute for this - /// geometry. + /// 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). /// @@ -666,9 +665,8 @@ impl<'buf> Geometry<'buf> { /// 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::intersect1Mp`], [`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. + /// [`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, @@ -679,7 +677,7 @@ impl<'buf> Geometry<'buf> { where D: UserGeometryData, C: AsIntersectContext, - F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut C, Option<&mut D>), + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidityN<'a>, &mut C, Option<&mut D>), { let mut geom_data = self.data.lock().unwrap(); unsafe { @@ -720,7 +718,7 @@ impl<'buf> Geometry<'buf> { where D: UserGeometryData, C: AsIntersectContext, - F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut C, Option<&mut D>), + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidityN<'a>, &mut C, Option<&mut D>), { let mut geom_data = self.data.lock().unwrap(); unsafe { @@ -817,7 +815,7 @@ impl<'buf> Geometry<'buf> { /// 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]. + /// instancing see the tutorial *ClosestPoint*. pub unsafe fn set_point_query_function(&mut self, query_fn: RTCPointQueryFunction) { rtcSetGeometryPointQueryFunction(self.handle, query_fn); } @@ -862,8 +860,8 @@ impl<'buf> Geometry<'buf> { /// 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`] device property using - /// [`Device::get_property`]. + /// [`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); @@ -892,25 +890,25 @@ impl<'buf> Geometry<'buf> { /// 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. + /// 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 + /// 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. + /// \[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]. + /// 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. @@ -1198,66 +1196,85 @@ impl<'buf> Geometry<'buf> { /// 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`]. /// - /// 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. The callback function of type - /// [`RTCIntersectFunctionN`] gets passed a number of arguments through - /// the [`RTCIntersectFunctionNArguments`] structure. The value N - /// specifies the ray packet size, valid points to an array of - /// integers that specify whether the corresponding ray is valid (-1) or - /// invalid (0), the `geometryUserPtr` member points to the geometry - /// user data previously set through [`Geometry::set_user_data`], the - /// context member points to the intersection context passed to the ray - /// query, the rayhit member points to a ray and hit packet of variable - /// size N, and the geomID and primID member identifies the geometry ID - /// and primitive ID of the primitive to intersect. The ray component of - /// the rayhit structure contains valid data, in particular - /// the tfar value is the current closest hit distance found. All data - /// inside the hit component of the rayhit structure are undefined and - /// should not be read by the function. - /// 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 was found in - /// the valid range (from tnear to tfar), it should update the hit distance - /// of the ray (tfar member) and the hit (u, v, Ng, 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. + /// + /// # 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 rtcFilterIntersection call. Within the user geometry intersect - /// function, it is safe to trace new rays and create new scenes and - /// geometries. When performing ray queries using rtcIntersect1, it is - /// guaranteed that the packet size is 1 when the callback is invoked. - /// When performing ray queries using the rtcIntersect4/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 rtcGetDevice- Property. When performing ray queries - /// using the stream API such as rtcIntersect1M, rtcIntersect1Mp, - /// rtcIntersectNM, or rtcIntersectNp the or- der 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. + /// 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>, ValidMasks<'a>, &mut C, u32, u32, Option<&mut D>), + F: for<'a> FnMut(RayHitN<'a>, ValidityN<'a>, &mut C, u32, u32, Option<&mut D>), { match self.kind { GeometryKind::USER => unsafe { @@ -1285,11 +1302,28 @@ impl<'buf> Geometry<'buf> { /// /// 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(&'a mut [i32], Option<&mut D>, u32, u32, &mut C, RayN<'a>), + F: for<'a> FnMut(RayN<'a>, ValidityN<'a>, &mut C, u32, u32, Option<&mut D>), { match self.kind { GeometryKind::USER => { @@ -1439,24 +1473,31 @@ impl<'buf> Geometry<'buf> { /// 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. - /// Passing `None` will remove the displacement function. + /// 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. /// - /// The displacement function is called for each vertex of the subdivision - /// geometry. The function is called with the following parameters: + /// # Arguments + /// + /// * `displacement`: The displacement function. The displacement function + /// is called for each vertex of the subdivision geometry. /// - /// * `geometry`: The geometry handle. - /// * `geometry_user_data`: The user data. - /// * `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. - /// * `vertices`: The information about the vertices to displace. See - /// [`Vertices`]. + /// 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 /// @@ -1856,20 +1897,22 @@ fn intersect_filter_function(_f: &mut F) -> RTCFilterFunctionN where D: UserGeometryData, C: AsIntersectContext, - F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut C, Option<&mut D>), + 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>, ValidMasks<'a>, &mut C, Option<&mut D>), + F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidityN<'a>, &mut C, Option<&mut D>), { - let cb_ptr = - (*((*args).geometryUserPtr as *mut GeometryData)).intersect_filter_fn as *mut F; + 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 (*((*args).geometryUserPtr as *mut GeometryData)).user_data { + match geom_data.user_data { Some(ref user_data) => { if user_data.data.is_null() || user_data.type_id != TypeId::of::() { None @@ -1892,7 +1935,7 @@ where len, marker: PhantomData, }, - ValidMasks { + ValidityN { ptr: (*args).valid, len, marker: PhantomData, @@ -1911,20 +1954,23 @@ fn occluded_filter_function(_f: &mut F) -> RTCFilterFunctionN where D: UserGeometryData, C: AsIntersectContext, - F: for<'a> FnMut(RayN<'a>, HitN<'a>, ValidMasks<'a>, &mut C, Option<&mut D>), + 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>, ValidMasks<'a>, &mut C, Option<&mut D>), + 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 = (*((*args).geometryUserPtr as *mut GeometryData)).occluded_filter_fn as *mut F; + 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 (*((*args).geometryUserPtr as *mut GeometryData)).user_data { + match geom_data.user_data { Some(ref user_data) => { if user_data.data.is_null() || user_data.type_id != TypeId::of::() { None @@ -1946,7 +1992,7 @@ where len, marker: PhantomData, }, - ValidMasks { + ValidityN { ptr: (*args).valid, len, marker: PhantomData, @@ -1971,7 +2017,10 @@ where D: UserGeometryData, F: FnMut(&mut Bounds, u32, u32, Option<&mut D>), { - let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) + 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( @@ -1982,7 +2031,7 @@ where if !cb_ptr.is_null() { let cb = &mut *cb_ptr; let user_data = { - match (*((*args).geometryUserPtr as *mut GeometryData)).user_data { + match geom_data.user_data { Some(ref user_data) => { if user_data.data.is_null() || user_data.type_id != TypeId::of::() { None @@ -2011,15 +2060,18 @@ fn intersect_function(_f: &mut F) -> RTCIntersectFunctionN where D: UserGeometryData, C: AsIntersectContext, - F: for<'a> FnMut(RayHitN<'a>, ValidMasks<'a>, &mut C, u32, u32, Option<&mut D>), + 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>, ValidMasks<'a>, &mut C, u32, u32, Option<&mut D>), + F: for<'a> FnMut(RayHitN<'a>, ValidityN<'a>, &mut C, u32, u32, Option<&mut D>), { - let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) + 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( @@ -2031,7 +2083,7 @@ where if !cb_ptr.is_null() { let cb = &mut *cb_ptr; let user_data = { - match (*((*args).geometryUserPtr as *mut GeometryData)).user_data { + match geom_data.user_data { Some(ref user_data) => { if user_data.data.is_null() || user_data.type_id != TypeId::of::() { None @@ -2048,7 +2100,7 @@ where len, marker: PhantomData, }, - ValidMasks { + ValidityN { ptr: (*args).valid, len, marker: PhantomData, @@ -2070,15 +2122,18 @@ fn occluded_function(_f: &mut F) -> RTCOccludedFunctionN where D: UserGeometryData, C: AsIntersectContext, - F: for<'a> FnMut(&'a mut [i32], Option<&mut D>, u32, u32, &mut C, RayN<'a>), + 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(&'a mut [i32], Option<&mut D>, u32, u32, &mut C, RayN<'a>), + F: for<'a> FnMut(RayN<'a>, ValidityN<'a>, &mut C, u32, u32, Option<&mut D>), { - let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) + 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( @@ -2089,7 +2144,7 @@ where if !cb_ptr.is_null() { let cb = &mut *cb_ptr; let user_data = { - match (*((*args).geometryUserPtr as *mut GeometryData)).user_data { + match geom_data.user_data { Some(ref user_data) => { if user_data.data.is_null() || user_data.type_id != TypeId::of::() { None @@ -2101,16 +2156,20 @@ where } }; cb( - std::slice::from_raw_parts_mut((*args).valid, (*args).N as usize), - user_data, - (*args).geomID, - (*args).primID, - &mut *((*args).context as *mut _ as *mut C), 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, ) } } @@ -2201,7 +2260,10 @@ where D: UserGeometryData, F: for<'a> FnMut(RTCGeometry, Vertices<'a>, u32, u32, Option<&mut D>), { - let cb_ptr = (*((*args).geometryUserPtr as *mut GeometryData)) + 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( @@ -2212,7 +2274,7 @@ where if !cb_ptr.is_null() { let cb = &mut *cb_ptr; let user_data = { - match (*((*args).geometryUserPtr as *mut GeometryData)).user_data { + match geom_data.user_data { Some(ref user_data) => { if user_data.data.is_null() || user_data.type_id != TypeId::of::() { None @@ -2249,39 +2311,46 @@ where Some(inner::) } -/// Struct holding data for valid masks used in the callback function set by +/// Struct holding data for validity masks used in the callback function set by /// [`Geometry::set_intersect_filter_function`], -/// [`Geometry::set_occluded_filter_function`]. -pub struct ValidMasks<'a> { +/// [`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 ValidMasksIter<'a, 'b> { - inner: &'b ValidMasks<'a>, +pub struct ValidityNIter<'a, 'b> { + inner: &'b ValidityN<'a>, cur: usize, } -impl<'a> ValidMasks<'a> { - pub fn iter<'b>(&'b self) -> ValidMasksIter<'a, 'b> { - ValidMasksIter { +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) -> ValidMasksIterMut<'a, 'b> { - ValidMasksIterMut { + 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 ValidMasks<'a> { +impl<'a> Index for ValidityN<'a> { type Output = i32; fn index(&self, index: usize) -> &Self::Output { @@ -2290,13 +2359,13 @@ impl<'a> Index for ValidMasks<'a> { } } -impl<'a> IndexMut for ValidMasks<'a> { +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 ValidMasksIter<'a, 'b> { +impl<'a, 'b> Iterator for ValidityNIter<'a, 'b> { type Item = i32; fn next(&mut self) -> Option { @@ -2312,12 +2381,12 @@ impl<'a, 'b> Iterator for ValidMasksIter<'a, 'b> { } } -pub struct ValidMasksIterMut<'a, 'b> { - inner: &'b mut ValidMasks<'a>, +pub struct ValidityNIterMut<'a, 'b> { + inner: &'b mut ValidityN<'a>, cur: usize, } -impl<'a, 'b> Iterator for ValidMasksIterMut<'a, 'b> { +impl<'a, 'b> Iterator for ValidityNIterMut<'a, 'b> { type Item = &'a mut i32; fn next(&mut self) -> Option { diff --git a/src/lib.rs b/src/lib.rs index c2cce9253..f8b1b48bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,7 +51,7 @@ pub type Bounds = sys::RTCBounds; /// /// The [`BufferUsage::VERTEX_ATTRIBUTE`] slot can get used to assign /// arbitrary additional vertex data which can get interpolated using the -/// [`rtcInterpolate`] API call. +/// [`Geometry::interpolate`] and [`Geometry::interpolate_n`] API calls. /// /// The [`BufferUsage::NORMAL`], [`BufferUsage::TANGENT`], and /// [`BufferUsage::NORMAL_DERIVATIVE`] are special buffers required to assign @@ -88,7 +88,7 @@ pub type GeometryKind = sys::RTCGeometryType; /// /// 1. A upper triangular scaling/skew/shift matrix /// -/// ```ignore +/// ```text /// | scale_x skew_xy skew_xz shift_x | /// | 0 scale_y skew_yz shift_y | /// | 0 0 scale_z shitf_z | @@ -96,7 +96,7 @@ pub type GeometryKind = sys::RTCGeometryType; /// ``` /// /// 2. A translation matrix -/// ```ignore +/// ```text /// | 1 0 0 translation_x | /// | 0 1 0 translation_y | /// | 0 0 1 translation_z | @@ -104,14 +104,16 @@ pub type GeometryKind = sys::RTCGeometryType; /// ``` /// /// 3. A rotation matrix R, represented as a quaternion -/// ```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. +/// ```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; @@ -265,3 +267,32 @@ fn test_aligned_vector_alloc() { assert_eq!(*x, 1.0); } } + +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/ray.rs b/src/ray.rs index b7d6059ee..151472c2e 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -1,9 +1,8 @@ -use crate::{sys, INVALID_ID}; -use std::fmt::{Debug, Formatter}; +use crate::{normalise_vector3, sys, INVALID_ID}; -mod packet; +pub mod packet; mod soa; -mod stream; +pub mod stream; pub use packet::*; pub use soa::*; @@ -22,7 +21,7 @@ pub use stream::*; /// [`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 +/// 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. @@ -31,53 +30,58 @@ pub use stream::*; /// 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 (RTCRay4 type), size 8 (RTCRay8 type), -/// and size 16 (RTCRay16 type). A const-generic type [`RayNt`] is -/// defined for ray packets of arbitrary size N at compile time. +/// 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; impl Ray { - /// Creates a new ray starting at `origin` and heading in direction `dir` - pub fn new(origin: [f32; 3], direction: [f32; 3]) -> Ray { - Ray::segment(origin, direction, 0.0, f32::INFINITY) - } - - pub fn new_with_id(origin: [f32; 3], direction: [f32; 3], id: u32) -> Ray { + /// 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], - tnear: 0.0, + tnear: near, dir_x: direction[0], dir_y: direction[1], dir_z: direction[2], - tfar: f32::INFINITY, - time: 0.0, - mask: u32::MAX, + tfar: far, + time, + mask, id, flags: 0, } } - /// Creates a new ray starting at `origin` and heading in direction `dir` + /// 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 { - Ray { - org_x: origin[0], - org_y: origin[1], - org_z: origin[2], - tnear, - dir_x: direction[0], - dir_y: direction[1], - dir_z: direction[2], - tfar, - time: 0.0, - mask: u32::MAX, - id: 0, - flags: 0, - } + 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. @@ -88,13 +92,36 @@ impl Ray { /// Returns the normalized direction of the ray. /// - /// The direction is normalized by dividing it by its length, which - /// may produce a NaN if the direction is zero. - pub fn dir_normalized(&self) -> [f32; 3] { - let dir = self.dir(); - let len = dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]; - let len = len.sqrt(); - [dir[0] / len, dir[1] / len, dir[2] / len] + /// 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, + flags: 0, + } } } @@ -108,17 +135,13 @@ impl Ray { /// 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. +/// (`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. +/// 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 (RTCHit4 type), size 8 (RTCHit8 type), and size 16 (RTCHit16 -/// type). -/// -/// [`HitNt`] defines the type for hit packets of arbitrary size N at -/// compile time. +/// of size 4 ([`Hit4`]), size 8 ([`Hit8`]), and size 16 ([`Hit16`]). /// /// See [`sys::RTCHit`] for more details. pub type Hit = sys::RTCHit; @@ -143,15 +166,13 @@ impl Hit { 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 normal_normalized(&self) -> [f32; 3] { - let normal = self.normal(); - let len = normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]; - let len = len.sqrt(); - [normal[0] / len, normal[1] / len, normal[2] / len] - } + 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 @@ -162,9 +183,8 @@ impl Hit { /// 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 (RTCRayHit4 type), size 8 (RTCRayHit8 -/// type), and size 16 (RTCRayHit16 type). A const-generic type [`RayHitNt`] -/// is defined for ray/hit packets of arbitrary size N at compile time. +/// 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; @@ -177,18 +197,14 @@ impl RayHit { hit: Hit::default(), } } +} - /// Returns true if the hit is valid (i.e. the ray hit something). - pub fn is_valid(&self) -> bool { self.hit.geomID != INVALID_ID } - - /// Calculates the hit point from the ray and the hit distance. - pub fn hit_point(&self) -> [f32; 3] { - let t = self.ray.tfar; - [ - self.ray.org_x + self.ray.dir_x * t, - self.ray.org_y + self.ray.dir_y * t, - self.ray.org_z + self.ray.dir_z * t, - ] +impl Default for RayHit { + fn default() -> Self { + RayHit { + ray: Ray::default(), + hit: Hit::default(), + } } } diff --git a/src/ray/packet.rs b/src/ray/packet.rs index cec9403f3..10bde6c59 100644 --- a/src/ray/packet.rs +++ b/src/ray/packet.rs @@ -1,13 +1,11 @@ +//! Ray packet types and traits. + use crate::{ - sys, Hit, Ray, RayHit, SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut, - INVALID_ID, + normalise_vector3, sys, Hit, Ray, RayHit, SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, + SoARayIterMut, INVALID_ID, }; use std::marker::PhantomData; -mod sealed { - pub trait Sealed {} -} - /// A ray packet of size 4. pub type Ray4 = sys::RTCRay4; @@ -38,16 +36,19 @@ pub type RayHit16 = sys::RTCRayHit16; /// Represents a packet of rays. /// /// Used as a trait bound for functions that operate on ray packets. -/// See [`Scene::occluded_stream_aos`] and [`Scene::intersect_stream_aos`]. -pub trait RayPacket: Sized + sealed::Sealed { +/// 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; } -pub trait HitPacket: Sized + sealed::Sealed { +/// Represents a packet of hits. +pub trait HitPacket: Sized { const LEN: usize; } -pub trait RayHitPacket: Sized + sealed::Sealed { +/// Represents a packet of ray/hit pairs. +pub trait RayHitPacket: Sized { type Ray: RayPacket; type Hit: HitPacket; const LEN: usize = Self::Ray::LEN; @@ -56,17 +57,14 @@ pub trait RayHitPacket: Sized + sealed::Sealed { macro_rules! impl_packet_traits { ($($ray:ident, $hit:ident, $rayhit:ident, $n:expr);*) => { $( - impl sealed::Sealed for $ray {} impl RayPacket for $ray { const LEN: usize = $n; } - impl sealed::Sealed for $hit {} impl HitPacket for $hit { const LEN: usize = $n; } - impl sealed::Sealed for $rayhit {} impl RayHitPacket for $rayhit { type Ray = $ray; type Hit = $hit; @@ -194,8 +192,8 @@ macro_rules! impl_hit_packets { instID: [[INVALID_ID; $n]], } } - pub fn any_hit(&self) -> bool { self.hits().any(|h| h) } - pub fn hits(&self) -> impl Iterator + '_ { + 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) } @@ -210,13 +208,23 @@ macro_rules! impl_hit_packets { 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 uv(&self, i: usize) -> (f32, f32) { (self.u[i], 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; } @@ -261,7 +269,29 @@ pub struct RayN<'a> { } impl<'a> RayN<'a> { - pub const fn org(&self, i: usize) -> [f32; 3] { + /// 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; @@ -273,55 +303,17 @@ impl<'a> RayN<'a> { } } - pub const fn org_x(&self, i: usize) -> f32 { - debug_assert!(i < self.len, "index out of bounds"); - unsafe { *(self.ptr as *const f32).add(i) } - } - - pub fn set_org_x(&mut self, i: usize, x: f32) { + fn set_org(&mut self, i: usize, o: [f32; 3]) { debug_assert!(i < self.len, "index out of bounds"); unsafe { - *(self.ptr as *mut f32).add(i) = x; + 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]; } } - pub const fn org_y(&self, i: usize) -> f32 { - debug_assert!(i < self.len, "index out of bounds"); - unsafe { *(self.ptr as *const f32).add(self.len + i) } - } - - pub fn set_org_y(&mut self, i: usize, y: f32) { - debug_assert!(i < self.len, "index out of bounds"); - unsafe { - *(self.ptr as *mut f32).add(self.len + i) = y; - } - } - - pub const fn org_z(&self, i: usize) -> f32 { - debug_assert!(i < self.len, "index out of bounds"); - unsafe { *(self.ptr as *const f32).add(2 * self.len + i) } - } - - pub fn set_org_z(&mut self, i: usize, z: f32) { - debug_assert!(i < self.len, "index out of bounds"); - unsafe { - *(self.ptr as *mut f32).add(2 * self.len + i) = z; - } - } - - pub const 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) } - } - - pub 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; - } - } - - pub const fn dir(&self, i: usize) -> [f32; 3] { + fn dir(&self, i: usize) -> [f32; 3] { debug_assert!(i < self.len, "index out of bounds"); unsafe { let ptr = self.ptr as *const f32; @@ -333,103 +325,87 @@ impl<'a> RayN<'a> { } } - pub const fn dir_x(&self, i: usize) -> f32 { - debug_assert!(i < self.len, "index out of bounds"); - unsafe { *(self.ptr as *const f32).add(4 * self.len + i) } - } - - pub fn set_dir_x(&mut self, i: usize, x: f32) { + fn set_dir(&mut self, i: usize, d: [f32; 3]) { debug_assert!(i < self.len, "index out of bounds"); unsafe { - *(self.ptr as *mut f32).add(4 * self.len + i) = x; + 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]; } } - pub const fn dir_y(&self, i: usize) -> f32 { + fn tnear(&self, i: usize) -> f32 { debug_assert!(i < self.len, "index out of bounds"); - unsafe { *(self.ptr as *const f32).add(5 * self.len + i) } + unsafe { *(self.ptr as *const f32).add(3 * self.len + i) } } - pub fn set_dir_y(&mut self, i: usize, y: f32) { + 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(5 * self.len + i) = y; + *(self.ptr as *mut f32).add(3 * self.len + i) = t; } } - pub const fn dir_z(&self, i: usize) -> f32 { + fn tfar(&self, i: usize) -> f32 { debug_assert!(i < self.len, "index out of bounds"); - unsafe { *(self.ptr as *const f32).add(6 * self.len + i) } + unsafe { *(self.ptr as *const f32).add(8 * self.len + i) } } - pub fn set_dir_z(&mut self, i: usize, z: f32) { + 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(6 * self.len + i) = z; + *(self.ptr as *mut f32).add(8 * self.len + i) = t; } } - pub const fn time(&self, i: usize) -> f32 { + 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) } } - pub fn set_time(&mut self, i: usize, t: f32) { + 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; } } - pub const 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) } - } - - pub 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; - } - } - - pub const fn mask(&self, i: usize) -> u32 { + 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) } } - pub fn set_mask(&mut self, i: usize, m: u32) { + 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; } } - pub const fn id(&self, i: usize) -> u32 { + 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) } } - pub fn set_id(&mut self, i: usize, id: u32) { + 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; } } - pub const fn flags(&self, i: usize) -> u32 { + 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) } } - pub fn set_flags(&mut self, i: usize, f: u32) { + 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; } } - - pub const fn len(&self) -> usize { self.len } } /// Hit packet of runtime size. @@ -443,48 +419,97 @@ pub struct HitN<'a> { pub(crate) marker: PhantomData<&'a mut sys::RTCHitN>, } -impl<'a> HitN<'a> { - pub const fn ng_x(&self, i: usize) -> f32 { +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) } + 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), + ] + } } - pub const fn ng_y(&self, i: usize) -> f32 { + 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 { *(self.ptr as *const f32).add(self.len + i) } + 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]; + } } - pub const fn ng_z(&self, i: usize) -> f32 { + fn uv(&self, i: usize) -> [f32; 2] { debug_assert!(i < self.len, "index out of bounds"); - unsafe { *(self.ptr as *const f32).add(2 * self.len + i) } + unsafe { + [ + *(self.ptr as *const f32).add(3 * self.len + i), + *(self.ptr as *const f32).add(4 * self.len + i), + ] + } } - pub const fn u(&self, i: usize) -> f32 { + fn set_u(&mut self, i: usize, u: f32) { debug_assert!(i < self.len, "index out of bounds"); - unsafe { *(self.ptr as *const f32).add(3 * self.len + i) } + unsafe { + *(self.ptr as *mut f32).add(3 * self.len + i) = u; + } } - pub const fn v(&self, i: usize) -> f32 { + fn set_v(&mut self, i: usize, v: f32) { debug_assert!(i < self.len, "index out of bounds"); - unsafe { *(self.ptr as *const f32).add(4 * self.len + i) } + unsafe { + *(self.ptr as *mut f32).add(4 * self.len + i) = v; + } } - pub const fn prim_id(&self, i: usize) -> u32 { + 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) } } - pub const fn geom_id(&self, i: usize) -> u32 { + 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) } } - pub const fn inst_id(&self, i: usize) -> u32 { + 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. diff --git a/src/ray/soa.rs b/src/ray/soa.rs index dfc859421..e8f1e40dd 100644 --- a/src/ray/soa.rs +++ b/src/ray/soa.rs @@ -1,3 +1,4 @@ +use crate::INVALID_ID; use std::{ iter::{ExactSizeIterator, Iterator}, marker::PhantomData, @@ -31,9 +32,10 @@ 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 uv(&self, i: usize) -> [f32; 2]; fn set_u(&mut self, i: usize, u: f32); fn set_v(&mut self, i: usize, v: f32); @@ -46,7 +48,7 @@ pub trait SoAHit { 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> { @@ -202,11 +204,12 @@ 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 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 hit(&self) -> bool { self.hit.hit(self.idx) } + pub fn hit(&self) -> bool { self.hit.is_valid(self.idx) } } pub struct SoAHitIter<'a, T> { @@ -251,11 +254,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) } @@ -293,7 +300,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) } } diff --git a/src/ray/stream.rs b/src/ray/stream.rs index 379448bc8..d23910580 100644 --- a/src/ray/stream.rs +++ b/src/ray/stream.rs @@ -1,8 +1,10 @@ +//! Ray stream types in SOA layout. + use std::iter::Iterator; use crate::{ - aligned_vector, aligned_vector_init, sys, SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, - SoARayIterMut, + aligned_vector, aligned_vector_init, normalise_vector3, sys, SoAHit, SoAHitIter, SoAHitRef, + SoARay, SoARayIter, SoARayIterMut, }; // struct RayNp2 { @@ -154,7 +156,7 @@ impl HitNp { SoAHitIter::new(self, self.len()).filter(|h| h.hit()) } pub fn len(&self) -> usize { self.ng_x.len() } - pub fn empty(&self) -> bool { self.len() == 0 } + pub fn is_empty(&self) -> bool { self.len() == 0 } pub unsafe fn as_hitnp(&mut self) -> sys::RTCHitNp { sys::RTCHitNp { Ng_x: self.ng_x.as_mut_ptr(), @@ -171,13 +173,16 @@ impl HitNp { impl SoAHit for HitNp { 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] { normalise_vector3(self.normal(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 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; } @@ -208,7 +213,7 @@ impl RayHitNp { self.ray.iter().zip(self.hit.iter()) } pub fn len(&self) -> usize { self.ray.len() } - pub fn empty(&self) -> bool { self.len() == 0 } + pub fn is_empty(&self) -> bool { self.len() == 0 } pub unsafe fn as_rayhitnp(&mut self) -> sys::RTCRayHitNp { sys::RTCRayHitNp { ray: self.ray.as_raynp(), diff --git a/src/scene.rs b/src/scene.rs index 80d213da6..fb3b029d9 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,6 +1,6 @@ use crate::{ - AsIntersectContext, Bounds, Error, PointQuery, PointQueryContext, Ray, Ray16, Ray8, RayHit, - RayHit16, RayHit8, RayHitNp, RayHitPacket, RayPacket, SceneFlags, + AsIntersectContext, Bounds, BuildQuality, Error, PointQuery, PointQueryContext, Ray, Ray16, + Ray8, RayHit, RayHit16, RayHit8, RayHitNp, RayHitPacket, RayPacket, SceneFlags, }; use std::{ any::TypeId, @@ -10,7 +10,6 @@ use std::{ }; use crate::{ - context::IntersectContext, device::Device, geometry::Geometry, ray::{Ray4, RayHit4, RayNp}, @@ -140,7 +139,20 @@ impl<'a> Scene<'a> { } } - // TODO: add get_geometry with rtcGetGeometryThreadSafe + /// 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() + } + } /// Returns the raw underlying handle to the scene, e.g. for passing it to /// native code or ISPC kernels. @@ -202,7 +214,7 @@ impl<'a> Scene<'a> { } /// Set the scene flags. Multiple flags can be enabled using an OR - /// operation. See [`RTCSceneFlags`] for all possible flags. + /// operation. See [`SceneFlags`] for all possible flags. /// On failure an error code is set that can be queried using /// [`rtcGetDeviceError`]. /// @@ -218,7 +230,7 @@ impl<'a> Scene<'a> { /// 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: RTCSceneFlags) { + pub fn set_flags(&self, flags: SceneFlags) { unsafe { rtcSetSceneFlags(self.handle, flags); } @@ -235,7 +247,7 @@ impl<'a> Scene<'a> { /// let flags = scene.get_flags(); /// scene.set_flags(flags | SceneFlags::ROBUST); /// ``` - pub fn get_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. /// @@ -294,9 +306,7 @@ impl<'a> Scene<'a> { /// The point query structure must be aligned to 16 bytes. /// /// Currently, all primitive types are supported by the point query API - /// except of points (see [`GeometryKind::POINT`]), curves (see - /// [`GeometryKind::CURVE`]) and subdivision surfaces (see - /// [`GeometryKind::SUBDIVISION]). + /// except of points, curves and subdivision surfaces. /// /// See **closet_point** in examples folder for an example of this. pub fn point_query( @@ -342,7 +352,7 @@ impl<'a> Scene<'a> { } } - /// Set the build quality of the scene. See [`RTCBuildQuality`] for all + /// 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 @@ -365,7 +375,7 @@ impl<'a> Scene<'a> { /// /// - [`BuildQuality::REFIT`]: Uses a BVH refitting approach when changing /// only the vertex buffer. - pub fn set_build_quality(&self, quality: RTCBuildQuality) { + pub fn set_build_quality(&self, quality: BuildQuality) { unsafe { rtcSetSceneBuildQuality(self.handle, quality); } @@ -410,7 +420,7 @@ impl<'a> Scene<'a> { /// Finds the closest hit of a single ray with the scene. /// - /// Analogous to [`sys::rtcIntersect1`]. + /// 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 @@ -448,7 +458,7 @@ impl<'a> Scene<'a> { unsafe { rtcIntersect1( self.handle, - ctx.as_intersect_context_mut_ptr(), + ctx.as_mut_context_ptr(), ray as *mut _ as *mut _, ); } @@ -477,12 +487,17 @@ impl<'a> Scene<'a> { /// /// Only active rays are processed, and hit data of inactive rays is not /// changed. - pub fn intersect4(&self, ctx: &mut IntersectContext, ray: &mut RayHit4, valid: &[i32; 4]) { + pub fn intersect4( + &self, + ctx: &mut C, + ray: &mut RayHit4, + valid: &[i32; 4], + ) { unsafe { rtcIntersect4( valid.as_ptr(), self.handle, - ctx as *mut RTCIntersectContext, + ctx.as_mut_context_ptr(), ray as *mut RTCRayHit4, ); } @@ -511,12 +526,17 @@ impl<'a> Scene<'a> { /// /// Only active rays are processed, and hit data of inactive rays is not /// changed. - pub fn intersect8(&self, ctx: &mut IntersectContext, ray: &mut RayHit8, valid: &[i32; 8]) { + pub fn intersect8( + &self, + ctx: &mut C, + ray: &mut RayHit8, + valid: &[i32; 8], + ) { unsafe { rtcIntersect8( valid.as_ptr(), self.handle, - ctx as *mut RTCIntersectContext, + ctx.as_mut_context_ptr(), ray as *mut RTCRayHit8, ); } @@ -545,12 +565,17 @@ impl<'a> Scene<'a> { /// /// Only active rays are processed, and hit data of inactive rays is not /// changed. - pub fn intersect16(&self, ctx: &mut IntersectContext, ray: &mut RayHit16, valid: &[i32; 16]) { + pub fn intersect16( + &self, + ctx: &mut C, + ray: &mut RayHit16, + valid: &[i32; 16], + ) { unsafe { rtcIntersect16( valid.as_ptr(), self.handle, - ctx as *mut RTCIntersectContext, + ctx.as_mut_context_ptr(), ray as *mut RTCRayHit16, ); } @@ -573,13 +598,9 @@ impl<'a> Scene<'a> { /// * `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_intersect_context_mut_ptr(), - ray as *mut RTCRay, - ); + rtcOccluded1(self.handle, ctx.as_mut_context_ptr(), ray as *mut RTCRay); } - ray.tfar == -f32::INFINITY + ray.tfar == f32::NEG_INFINITY } /// Checks for each active ray of a ray packet of size 4 if whether there is @@ -606,12 +627,12 @@ impl<'a> Scene<'a> { /// /// Only active rays are processed, and hit data of inactive rays is not /// changed. - pub fn occluded4(&self, ctx: &mut IntersectContext, ray: &mut Ray4, valid: &[i32; 4]) { + 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, ); } @@ -641,12 +662,12 @@ impl<'a> Scene<'a> { /// /// Only active rays are processed, and hit data of inactive rays is not /// changed. - pub fn occluded8(&self, ctx: &mut IntersectContext, ray: &mut Ray8, valid: &[i32; 8]) { + pub fn occluded8(&self, ctx: &mut C, ray: &mut Ray8, valid: &[i32; 8]) { unsafe { rtcOccluded8( valid.as_ptr(), self.handle, - ctx as *mut RTCIntersectContext, + ctx.as_mut_context_ptr(), ray as *mut RTCRay8, ); } @@ -676,12 +697,17 @@ impl<'a> Scene<'a> { /// /// Only active rays are processed, and hit data of inactive rays is not /// changed. - pub fn occluded16(&self, ctx: &mut IntersectContext, ray: &mut Ray16, valid: &[i32; 16]) { + pub fn occluded16( + &self, + ctx: &mut C, + ray: &mut Ray16, + valid: &[i32; 16], + ) { unsafe { rtcOccluded16( valid.as_ptr(), self.handle, - ctx as *mut RTCIntersectContext, + ctx.as_mut_context_ptr(), ray as *mut RTCRay16, ); } @@ -714,9 +740,9 @@ impl<'a> Scene<'a> { /// additional data. See [`IntersectContext`] for more information. /// /// * `rays` - The ray stream to intersect with the scene. - pub fn intersect_stream_aos( + pub fn intersect_stream_aos( &self, - ctx: &mut IntersectContext, + ctx: &mut C, rays: &mut Vec

, ) { let m = rays.len(); @@ -724,7 +750,7 @@ impl<'a> Scene<'a> { if P::Ray::LEN == 1 { rtcIntersect1M( self.handle, - ctx as *mut RTCIntersectContext, + ctx.as_mut_context_ptr(), rays.as_mut_ptr() as *mut _, m as u32, mem::size_of::

(), @@ -732,7 +758,7 @@ impl<'a> Scene<'a> { } else { rtcIntersectNM( self.handle, - ctx as *mut RTCIntersectContext, + ctx.as_mut_context_ptr(), rays.as_mut_ptr() as *mut _, P::Ray::LEN as u32, m as u32, @@ -770,13 +796,17 @@ impl<'a> Scene<'a> { /// [`IntersectContext`] for more information. /// /// * `rays` - The ray stream to intersect with the scene. - pub fn occluded_stream_aos(&self, ctx: &mut IntersectContext, rays: &mut Vec

) { + 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 RTCIntersectContext, + ctx.as_mut_context_ptr(), rays.as_mut_ptr() as *mut RTCRay, m as u32, mem::size_of::

(), @@ -784,7 +814,7 @@ impl<'a> Scene<'a> { } else { rtcOccludedNM( self.handle, - ctx as *mut RTCIntersectContext, + ctx.as_mut_context_ptr(), rays.as_mut_ptr() as *mut RTCRayN, P::LEN as u32, m as u32, @@ -806,13 +836,13 @@ impl<'a> Scene<'a> { /// /// 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 IntersectContext, rays: &mut RayHitNp) { + pub fn intersect_stream_soa(&self, ctx: &mut C, rays: &mut RayHitNp) { let n = rays.len(); unsafe { let mut rayhit = rays.as_rayhitnp(); rtcIntersectNp( self.handle, - ctx as *mut RTCIntersectContext, + ctx.as_mut_context_ptr(), &mut rayhit as *mut RTCRayHitNp, n as u32, ); @@ -831,13 +861,13 @@ impl<'a> Scene<'a> { /// /// 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 IntersectContext, rays: &mut RayNp) { + pub fn occluded_stream_soa(&self, ctx: &mut C, rays: &mut RayNp) { let n = rays.len(); unsafe { let mut r = rays.as_raynp(); rtcOccludedNp( self.handle, - ctx as *mut RTCIntersectContext, + ctx.as_mut_context_ptr(), &mut r as *mut RTCRayNp, n as u32, ); From 5a7ed57b7023db8ec4488267d8561f75ebb66392 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Tue, 7 Mar 2023 16:42:52 +0100 Subject: [PATCH 59/65] refactor ray/hit stream --- examples/triangle/src/main.rs | 7 +- src/ray.rs | 3 +- src/ray/packet.rs | 59 +++- src/ray/soa.rs | 25 +- src/ray/stream.rs | 510 +++++++++++++++++++++++++--------- src/scene.rs | 12 +- 6 files changed, 457 insertions(+), 159 deletions(-) diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index a459dd475..147c80d82 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -57,7 +57,12 @@ fn main() { let mut ray_hit = RayHitNp::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()) { + for (i, hit) in ray_hit + .hit + .iter() + .enumerate() + .filter(|(_i, h)| h.is_valid()) + { let p = image.get_pixel_mut(i as u32, j); let uv = hit.uv(); p[0] = (uv[0] * 255.0) as u8; diff --git a/src/ray.rs b/src/ray.rs index 151472c2e..66814b3a2 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -57,8 +57,8 @@ impl Ray { dir_x: direction[0], dir_y: direction[1], dir_z: direction[2], - tfar: far, time, + tfar: far, mask, id, flags: 0, @@ -199,6 +199,7 @@ impl RayHit { } } +#[allow(clippy::derivable_impls)] impl Default for RayHit { fn default() -> Self { RayHit { diff --git a/src/ray/packet.rs b/src/ray/packet.rs index 10bde6c59..77f4d0beb 100644 --- a/src/ray/packet.rs +++ b/src/ray/packet.rs @@ -139,35 +139,43 @@ macro_rules! impl_ray_packets { 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 tnear(&self, i: usize) -> f32 { self.tnear[i] } - fn set_tnear(&mut self, i: usize, t: f32) { self.tnear[i] = t } + 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 time(&self, i: usize) -> f32 { self.time[i] } - fn set_time(&mut self, i: usize, t: f32) { self.time[i] = t } + 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 } } )* @@ -193,12 +201,15 @@ macro_rules! impl_hit_packets { } } 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.hit()) + SoAHitIter::new(self, 4).filter(|h| h.is_valid()) } } @@ -208,6 +219,7 @@ macro_rules! impl_hit_packets { 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]; @@ -218,23 +230,39 @@ macro_rules! impl_hit_packets { [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; } } )* @@ -453,6 +481,25 @@ impl<'a> SoAHit for HitN<'a> { } } + 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 { diff --git a/src/ray/soa.rs b/src/ray/soa.rs index e8f1e40dd..cdd87cbdd 100644 --- a/src/ray/soa.rs +++ b/src/ray/soa.rs @@ -8,18 +8,18 @@ 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 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); @@ -32,20 +32,33 @@ 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 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 is_valid(&self, i: usize) -> bool { self.geom_id(i) != INVALID_ID } @@ -209,7 +222,7 @@ impl<'a, T: SoAHit + 'a> SoAHitRef<'a, T> { 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.is_valid(self.idx) } + pub fn is_valid(&self) -> bool { self.hit.is_valid(self.idx) } } pub struct SoAHitIter<'a, T> { diff --git a/src/ray/stream.rs b/src/ray/stream.rs index d23910580..ece1bb254 100644 --- a/src/ray/stream.rs +++ b/src/ray/stream.rs @@ -1,199 +1,432 @@ //! Ray stream types in SOA layout. -use std::iter::Iterator; +use std::{alloc, iter::Iterator, marker::PhantomData, ptr::NonNull}; use crate::{ - aligned_vector, aligned_vector_init, normalise_vector3, sys, SoAHit, SoAHitIter, SoAHitRef, - SoARay, SoARayIter, SoARayIterMut, + normalise_vector3, + sys::{RTCHitNp, RTCRayHitNp, RTCRayNp}, + SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut, INVALID_ID, }; -// struct RayNp2 { -// ptr: NonNull, -// len: usize, -// marker: PhantomData, -// } -// -// impl RayNp2 { -// pub fn new(n: usize) -> Self { -// unsafe { -// let layout = alloc::Layout::from_size_align(48 * n, 16).unwrap(); -// let ptr = match NonNull::new(alloc::alloc_zeroed(layout) as *mut -// u8) { Some(ptr) => ptr, -// None => alloc::handle_alloc_error(layout), -// }; -// RayNp2 { -// ptr, -// len: n, -// marker: PhantomData, -// } -// } -// } -// } - /// A ray stream stored in SoA format pub struct RayNp { - 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>, + /// 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 + /// Allocate a new Ray stream with room for `n` rays. + /// + /// The rays are uninitialized. pub fn new(n: usize) -> RayNp { - RayNp { - 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), + 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) } - pub fn len(&self) -> usize { self.org_x.len() } + + /// 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 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(), + + 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 SoARay for RayNp { - fn org(&self, i: usize) -> [f32; 3] { [self.org_x[i], self.org_y[i], self.org_z[i]] } + 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]) { - self.org_x[i] = o[0]; - self.org_y[i] = o[1]; - self.org_z[i] = o[2]; + 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 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]; + 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 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 { + 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; + } + } +} - fn tfar(&self, i: usize) -> f32 { self.tfar[i] } - fn set_tfar(&mut self, i: usize, far: f32) { self.tfar[i] = far; } +#[test] +fn test_stream_layout_raynp() { + let mut ray0 = RayNp::new(11); + assert_eq!(ray0.aligned_field_size, 48); - fn time(&self, i: usize) -> f32 { self.time[i] } - fn set_time(&mut self, i: usize, time: f32) { self.time[i] = time; } + let ray1 = RayNp::new(17); + assert_eq!(ray1.aligned_field_size, 80); - fn mask(&self, i: usize) -> u32 { self.mask[i] } - fn set_mask(&mut self, i: usize, mask: u32) { self.mask[i] = mask; } + assert_eq!( + std::mem::size_of::(), + 24, + concat!("Size of: ", stringify!(RayNp)) + ); - fn id(&self, i: usize) -> u32 { self.id[i] } - fn set_id(&mut self, i: usize, id: u32) { self.id[i] = id; } + 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); +} - fn flags(&self, i: usize) -> u32 { self.flags[i] } - fn set_flags(&mut self, i: usize, flags: u32) { self.flags[i] = flags; } +#[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. pub struct HitNp { - 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>, + /// 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 { - HitNp { - 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), + 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.hits().any(|g| g) } - pub fn hits(&self) -> impl Iterator + '_ { - self.geom_id.iter().map(|g| *g != u32::MAX) + + 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.hit()) + SoAHitIter::new(self, self.len()).filter(|h| h.is_valid()) } - pub fn len(&self) -> usize { self.ng_x.len() } - pub fn is_empty(&self) -> bool { self.len() == 0 } - 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], + + 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 SoAHit for HitNp { - fn normal(&self, i: usize) -> [f32; 3] { [self.ng_x[i], self.ng_y[i], self.ng_z[i]] } + 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]) { - self.ng_x[i] = n[0]; - self.ng_y[i] = n[1]; - self.ng_z[i] = n[2]; + 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 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 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); - 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; } + let hit1 = HitNp::new(18); + assert_eq!(hit1.aligned_field_size, 80); - 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; } + assert_eq!( + std::mem::size_of::(), + 24, + concat!("Size of: ", stringify!(RayNp)) + ); - 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; } + 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 { @@ -209,15 +442,18 @@ impl RayHitNp { 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 unsafe fn as_rayhitnp(&mut self) -> sys::RTCRayHitNp { - sys::RTCRayHitNp { - ray: self.ray.as_raynp(), - hit: self.hit.as_hitnp(), + + pub fn as_raw(&mut self) -> RTCRayHitNp { + RTCRayHitNp { + ray: self.ray.as_raw_mut(), + hit: self.hit.as_raw_mut(), } } } diff --git a/src/scene.rs b/src/scene.rs index fb3b029d9..4e83be065 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -837,14 +837,12 @@ impl<'a> Scene<'a> { /// 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) { - let n = rays.len(); unsafe { - let mut rayhit = rays.as_rayhitnp(); rtcIntersectNp( self.handle, ctx.as_mut_context_ptr(), - &mut rayhit as *mut RTCRayHitNp, - n as u32, + &mut rays.as_raw() as *mut _, + rays.len() as u32, ); } } @@ -862,14 +860,12 @@ impl<'a> Scene<'a> { /// 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) { - let n = rays.len(); unsafe { - let mut r = rays.as_raynp(); rtcOccludedNp( self.handle, ctx.as_mut_context_ptr(), - &mut r as *mut RTCRayNp, - n as u32, + &mut rays.as_raw_mut() as *mut RTCRayNp, + rays.len() as u32, ); } } From f428d9b9f8039d637e28aa0084c381d5ba2214d3 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Wed, 8 Mar 2023 13:30:06 +0100 Subject: [PATCH 60/65] add miri test; fix memory leak & incorrect memory deallocation --- .github/workflows/main.yml | 12 +++++ src/lib.rs | 89 +++++++++++++++++++++++++++++--------- src/ray/stream.rs | 24 +++++++++- 3 files changed, 104 insertions(+), 21 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 589818869..a933f72e1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,6 +17,18 @@ jobs: run: cargo fmt -- --check - name: Format Examples run: scripts/check-examples-formatting.sh + miri: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + - 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: diff --git a/src/lib.rs b/src/lib.rs index f8b1b48bf..79a34c340 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,14 @@ extern crate core; -use std::{alloc, mem}; +use std::{ + alloc, + marker::PhantomData, + mem, + mem::needs_drop, + ops::{Deref, DerefMut}, + ptr, +}; mod buffer; mod bvh; @@ -237,32 +244,74 @@ pub type PointQuery = sys::RTCPointQuery; /// Primitives that can be used to build a BVH. pub type BuildPrimitive = sys::RTCBuildPrimitive; -/// 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) +/// Utility for making specifically aligned vector. +/// +/// 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, + marker: PhantomData, +} + +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, + marker: PhantomData, + } + } + } + + 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 } } -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; + +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); } diff --git a/src/ray/stream.rs b/src/ray/stream.rs index ece1bb254..13fb479ef 100644 --- a/src/ray/stream.rs +++ b/src/ray/stream.rs @@ -8,7 +8,9 @@ use crate::{ SoAHit, SoAHitIter, SoAHitRef, SoARay, SoARayIter, SoARayIterMut, INVALID_ID, }; -/// A ray stream stored in SoA format +/// 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, @@ -84,6 +86,15 @@ impl RayNp { } } +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 { @@ -229,6 +240,8 @@ fn test_stream_new_raynp() { } /// A hit stream in SoA format. +/// +/// Each hit component is aligned to 16 bytes. pub struct HitNp { /// The pointer to the data. ptr: NonNull, @@ -301,6 +314,15 @@ impl HitNp { } } +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 { From 9b139307618287ea152ac6ca8cdb8ce6c2d8dc7a Mon Sep 17 00:00:00 2001 From: matthiascy Date: Wed, 8 Mar 2023 13:31:55 +0100 Subject: [PATCH 61/65] update ci --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a933f72e1..4538fd826 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,7 +21,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: actions-rs/toolchain@v1 - name: Install Miri run: | rustup toolchain install nightly --component miri From d5e06f50c61c747b5f424215d697e0667d726c25 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Wed, 8 Mar 2023 19:53:04 +0100 Subject: [PATCH 62/65] remove marker --- src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 79a34c340..1b3a5baf3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -255,7 +255,6 @@ pub type BuildPrimitive = sys::RTCBuildPrimitive; pub struct AlignedVector { vec: Vec, layout: alloc::Layout, - marker: PhantomData, } impl AlignedVector { @@ -271,7 +270,6 @@ impl AlignedVector { AlignedVector { vec: Vec::from_raw_parts(alloc::alloc(layout) as *mut T, len, len), layout, - marker: PhantomData, } } } From 18fb8ffbab6ee344faed6224dd3718918440dcb1 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Wed, 8 Mar 2023 20:10:20 +0100 Subject: [PATCH 63/65] remove marker from aligned vector & add aligned array --- examples/displacement_geometry/src/main.rs | 10 +++++----- examples/intersection_filter/src/main.rs | 12 +++++------ examples/support/Cargo.toml | 5 ++++- examples/support/src/common.rs | 13 ------------ examples/support/src/display.rs | 14 +++++++++++-- src/lib.rs | 23 ++++++++++++++++++++++ 6 files changed, 50 insertions(+), 27 deletions(-) diff --git a/examples/displacement_geometry/src/main.rs b/examples/displacement_geometry/src/main.rs index 470d87eff..2cec97328 100644 --- a/examples/displacement_geometry/src/main.rs +++ b/examples/displacement_geometry/src/main.rs @@ -1,11 +1,11 @@ use embree::{ - BufferSlice, BufferUsage, Device, Format, Geometry, GeometryKind, IntersectContext, Ray, - RayHit, Scene, SceneFlags, + AlignedArray, BufferSlice, BufferUsage, Device, Format, Geometry, GeometryKind, + IntersectContext, Ray, RayHit, Scene, SceneFlags, }; use glam::{vec3, Vec3}; use support::{ - noise, rgba_to_u32, Align16Array, Camera, ParallelIterator, RgbaImage, TiledImage, - DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH, TILE_SIZE_X, TILE_SIZE_Y, + noise, rgba_to_u32, Camera, ParallelIterator, RgbaImage, TiledImage, DEFAULT_DISPLAY_HEIGHT, + DEFAULT_DISPLAY_WIDTH, TILE_SIZE_X, TILE_SIZE_Y, }; const EDGE_LEVEL: f32 = 256.0; @@ -14,7 +14,7 @@ const NUM_FACES: usize = 6; #[allow(dead_code)] const FACE_SIZE: usize = 4; -const CUBE_VERTICES: Align16Array = Align16Array([ +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 diff --git a/examples/intersection_filter/src/main.rs b/examples/intersection_filter/src/main.rs index 3df1761b3..25f85a6b1 100644 --- a/examples/intersection_filter/src/main.rs +++ b/examples/intersection_filter/src/main.rs @@ -10,12 +10,12 @@ //! occluder is hit. use embree::{ - BufferSlice, BufferUsage, BuildQuality, Device, Format, Geometry, GeometryKind, HitN, - IntersectContextExt, Ray, RayHit, RayN, Scene, SoAHit, SoARay, ValidityN, INVALID_ID, + 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, Align16Array, Camera, Mode, ParallelIterator, RgbaImage, Tile, TiledImage, + rgba_to_u32, Camera, Mode, ParallelIterator, RgbaImage, Tile, TiledImage, DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH, TILE_SIZE_X, TILE_SIZE_Y, }; @@ -43,7 +43,7 @@ const COLORS: [[f32; 3]; 12] = [ [1.0, 1.0, 0.0], ]; -const CUBE_VERTICES: Align16Array<[f32; 4], CUBE_NUM_VERTICES> = Align16Array([ +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], @@ -55,7 +55,7 @@ const CUBE_VERTICES: Align16Array<[f32; 4], CUBE_NUM_VERTICES> = Align16Array([ ]); #[allow(dead_code)] -const CUBE_QUAD_INDICES: Align16Array = Align16Array([ +const CUBE_QUAD_INDICES: AlignedArray = AlignedArray([ 0, 1, 3, 2, // 5, 4, 6, 7, // 0, 4, 5, 1, // @@ -64,7 +64,7 @@ const CUBE_QUAD_INDICES: Align16Array = Align16Array 3, 1, 5, 7, // ]); -const CUBE_TRI_INDICES: Align16Array = Align16Array([ +const CUBE_TRI_INDICES: AlignedArray = AlignedArray([ 0, 1, 3, // 3, 1, 2, // 5, 4, 6, // diff --git a/examples/support/Cargo.toml b/examples/support/Cargo.toml index 1e6816fdb..f5ceaa381 100644 --- a/examples/support/Cargo.toml +++ b/examples/support/Cargo.toml @@ -9,8 +9,11 @@ image = "0.24.5" arcball = "1.1.0" cgmath = "0.18" clock_ticks = "0.1.1" +#egui = { version = "0.21", features = ["bytemuck"] } +#egui-winit = "0.21" +#egui-wgpu = "0.21" wgpu = "0.15.1" winit = "0.28.1" -futures = {version = "0.3", features = ["executor"]} +futures = { version = "0.3", features = ["executor"]} rayon = "1.5" diff --git a/examples/support/src/common.rs b/examples/support/src/common.rs index 574e81f55..74b6d09e6 100644 --- a/examples/support/src/common.rs +++ b/examples/support/src/common.rs @@ -555,19 +555,6 @@ pub const G3: [f32; 128 * 4] = [ 0.0, ]; -#[repr(align(16))] -pub struct Align16Array(pub [T; N]); - -impl Deref for Align16Array { - type Target = [T; N]; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -impl DerefMut for Align16Array { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } -} - #[inline(always)] pub fn lerp(a: f32, b: f32, t: f32) -> f32 { a + (b - a) * t } diff --git a/examples/support/src/display.rs b/examples/support/src/display.rs index 6ff402158..fdb1806d8 100644 --- a/examples/support/src/display.rs +++ b/examples/support/src/display.rs @@ -13,7 +13,7 @@ use winit::{ ElementState, Event, KeyboardInput, MouseButton, MouseScrollDelta, VirtualKeyCode, WindowEvent, }, - event_loop::{ControlFlow, EventLoop}, + event_loop::{ControlFlow, EventLoop, EventLoopBuilder}, window::{Window, WindowBuilder}, }; @@ -64,6 +64,9 @@ pub struct Display { adapter: wgpu::Adapter, device: wgpu::Device, queue: wgpu::Queue, + // egui_ctx: egui::Context, + // egui_state: egui_winit::State, + // egui_input: egui::RawInput, } #[derive(Debug)] @@ -80,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) @@ -112,6 +115,10 @@ impl Display { )) .expect("Failed to create device"); + // let egui_ctx = egui::Context::default(); + // let egui_state = egui_winit::State::new(&event_loop); + // let egui_input = egui::RawInput::default(); + Display { window, event_loop, @@ -120,6 +127,9 @@ impl Display { adapter, device, queue, + // egui_ctx, + // egui_state, + // egui_input, } } } diff --git a/src/lib.rs b/src/lib.rs index 1b3a5baf3..cc2bffefe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -246,6 +246,10 @@ 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 @@ -284,6 +288,9 @@ impl AlignedVector { } v } + + /// Returns the alignment of the vector. + pub fn alignment(&self) -> usize { self.layout.align() } } impl Deref for AlignedVector { @@ -315,6 +322,22 @@ fn test_aligned_vector_alloc() { } } +/// 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 { From 4b531725dd0ab2fae86ccb4b696b3f5451d5c0e5 Mon Sep 17 00:00:00 2001 From: matthiascy Date: Thu, 9 Mar 2023 14:58:28 +0100 Subject: [PATCH 64/65] egui integration --- examples/displacement_geometry/src/main.rs | 37 +++-- examples/dynamic_scene/src/main.rs | 46 +++--- examples/instancing/src/main.rs | 67 ++++---- examples/intersection_filter/src/main.rs | 38 +++-- examples/support/Cargo.toml | 6 +- examples/support/src/common.rs | 2 - examples/support/src/display.rs | 170 +++++++++++++++------ examples/support/src/lib.rs | 1 + examples/triangle/src/main.rs | 62 ++++---- examples/triangle_geometry/src/main.rs | 32 ++-- 10 files changed, 266 insertions(+), 195 deletions(-) diff --git a/examples/displacement_geometry/src/main.rs b/examples/displacement_geometry/src/main.rs index 2cec97328..cda547c73 100644 --- a/examples/displacement_geometry/src/main.rs +++ b/examples/displacement_geometry/src/main.rs @@ -156,33 +156,32 @@ fn main() { DEFAULT_DISPLAY_HEIGHT, "Dynamic Scene", ); - let mut last_time = 0.0; let mut tiled = TiledImage::new( DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT, TILE_SIZE_X, TILE_SIZE_Y, ); - support::display::run(display, move |image, camera_pose, time| { - 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, - ); - render_frame(&mut tiled, image, &camera, &scene, cube_id, ground_id); + support::display::run( + display, + move |image, camera_pose, time| { + 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, + ); - let elapsed = time - last_time; - last_time = time; - let fps = 1.0 / elapsed; - eprint!("\r{} fps", fps); - }); + render_frame(&mut tiled, image, &camera, &scene, cube_id, ground_id); + }, + |_| {}, + ); } fn render_pixel( diff --git a/examples/dynamic_scene/src/main.rs b/examples/dynamic_scene/src/main.rs index 82490444b..a4ca967d5 100644 --- a/examples/dynamic_scene/src/main.rs +++ b/examples/dynamic_scene/src/main.rs @@ -180,7 +180,6 @@ fn main() { colors[id as usize] = Vec3::new(1.0, 1.0, 1.0); scene.commit(); - let mut last_time = 0.0; let display = support::Display::new(512, 512, "Dynamic Scene"); let mut tiled = TiledImage::new( DEFAULT_DISPLAY_WIDTH, @@ -188,31 +187,30 @@ fn main() { TILE_SIZE_X, TILE_SIZE_Y, ); - support::display::run(display, move |image, camera_pose, time| { - 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, - ); - - for i in 0..NUM_SPHERES { - animate_sphere(&scene, i as u32, positions[i], radii[i], time); - } - scene.commit(); + support::display::run( + display, + move |image, camera_pose, time| { + 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, + ); - render_frame(&mut tiled, image, time, &scene, &camera, &colors); + for i in 0..NUM_SPHERES { + animate_sphere(&scene, i as u32, positions[i], radii[i], time); + } + scene.commit(); - let elapsed = time - last_time; - last_time = time; - let fps = 1.0 / elapsed; - eprint!("\r{} fps", fps); - }); + render_frame(&mut tiled, image, time, &scene, &camera, &colors); + }, + |_| {}, + ); } fn render_pixel( diff --git a/examples/instancing/src/main.rs b/examples/instancing/src/main.rs index 20929756c..53ee1fb3d 100644 --- a/examples/instancing/src/main.rs +++ b/examples/instancing/src/main.rs @@ -228,41 +228,38 @@ fn main() { TILE_SIZE_Y, ); - let mut last_time = 0.0; - - support::display::run(display, move |image, camera_pose, time| { - for p in image.iter_mut() { - *p = 0; - } - // Update scene transformations - animate_instances( - time, - instances.len(), - &mut state.transforms, - &mut state.normal_transforms, - ); - for (inst, tfm) in instances.iter_mut().zip(state.transforms.iter()) { - inst.set_transform(0, tfm.as_ref()); - inst.commit(); - } - scene.commit(); - - let img_dims = image.dimensions(); - let camera = Camera::look_dir( - camera_pose.pos, - camera_pose.dir, - camera_pose.up, - 55.0, - img_dims, - ); - - render_frame(&mut tiled, image, time, &scene, &camera, &state); - - let elapsed = time - last_time; - last_time = time; - let fps = 1.0 / elapsed; - eprint!("\r{} fps", fps); - }); + support::display::run( + display, + move |image, camera_pose, time| { + for p in image.iter_mut() { + *p = 0; + } + // Update scene transformations + animate_instances( + time, + instances.len(), + &mut state.transforms, + &mut state.normal_transforms, + ); + for (inst, tfm) in instances.iter_mut().zip(state.transforms.iter()) { + inst.set_transform(0, tfm.as_ref()); + inst.commit(); + } + scene.commit(); + + let img_dims = image.dimensions(); + let camera = Camera::look_dir( + camera_pose.pos, + camera_pose.dir, + camera_pose.up, + 55.0, + img_dims, + ); + + render_frame(&mut tiled, image, time, &scene, &camera, &state); + }, + |_| {}, + ); } fn render_pixel( diff --git a/examples/intersection_filter/src/main.rs b/examples/intersection_filter/src/main.rs index 25f85a6b1..a54ba3b78 100644 --- a/examples/intersection_filter/src/main.rs +++ b/examples/intersection_filter/src/main.rs @@ -593,31 +593,29 @@ fn main() { DEFAULT_DISPLAY_HEIGHT, "Intersection Filter", ); - let mut last_time = 0.0; let mut tiled = TiledImage::new( DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT, TILE_SIZE_X, TILE_SIZE_Y, ); - support::display::run(display, move |image, camera_pose, time| { - 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, - ); - - render_frame(&mut tiled, image, &camera, &scene); + support::display::run( + display, + move |image, camera_pose, time| { + 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, + ); - let elapsed = time - last_time; - last_time = time; - let fps = 1.0 / elapsed; - eprint!("\r{} fps", fps); - }); + render_frame(&mut tiled, image, &camera, &scene); + }, + |_| {}, + ); } diff --git a/examples/support/Cargo.toml b/examples/support/Cargo.toml index f5ceaa381..25168498a 100644 --- a/examples/support/Cargo.toml +++ b/examples/support/Cargo.toml @@ -9,9 +9,9 @@ image = "0.24.5" arcball = "1.1.0" cgmath = "0.18" clock_ticks = "0.1.1" -#egui = { version = "0.21", features = ["bytemuck"] } -#egui-winit = "0.21" -#egui-wgpu = "0.21" +egui = { version = "0.21", features = ["bytemuck"] } +egui-winit = "0.21" +egui-wgpu = "0.21" wgpu = "0.15.1" winit = "0.28.1" futures = { version = "0.3", features = ["executor"]} diff --git a/examples/support/src/common.rs b/examples/support/src/common.rs index 74b6d09e6..a355ba4c6 100644 --- a/examples/support/src/common.rs +++ b/examples/support/src/common.rs @@ -1,5 +1,3 @@ -use std::ops::{Deref, DerefMut}; - pub const DEFAULT_DISPLAY_WIDTH: u32 = 512; pub const DEFAULT_DISPLAY_HEIGHT: u32 = 512; diff --git a/examples/support/src/display.rs b/examples/support/src/display.rs index fdb1806d8..9c7a0298b 100644 --- a/examples/support/src/display.rs +++ b/examples/support/src/display.rs @@ -4,6 +4,7 @@ use std::borrow::Cow; use arcball::ArcballCamera; use cgmath::{Vector2, Vector3}; use clock_ticks; +use egui_wgpu::renderer::ScreenDescriptor; use futures; use image::RgbaImage; use wgpu; @@ -64,9 +65,6 @@ pub struct Display { adapter: wgpu::Adapter, device: wgpu::Device, queue: wgpu::Queue, - // egui_ctx: egui::Context, - // egui_state: egui_winit::State, - // egui_input: egui::RawInput, } #[derive(Debug)] @@ -88,6 +86,7 @@ impl Display { let window = WindowBuilder::new() .with_inner_size(win_size) .with_title(title) + .with_resizable(false) .build(&event_loop) .unwrap(); @@ -115,10 +114,6 @@ impl Display { )) .expect("Failed to create device"); - // let egui_ctx = egui::Context::default(); - // let egui_state = egui_winit::State::new(&event_loop); - // let egui_input = egui::RawInput::default(); - Display { window, event_loop, @@ -127,17 +122,16 @@ impl Display { adapter, device, queue, - // egui_ctx, - // egui_state, - // egui_input, } } } + /// 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) +pub fn run(display: Display, mut render: F, run_ui: U) where - F: 'static + FnMut(&mut RgbaImage, CameraPose, f32), + F: FnMut(&mut RgbaImage, CameraPose, f32) + 'static, + U: FnOnce(&egui::Context) + Copy + 'static, { let window_size = display.window.inner_size(); let mut embree_target = RgbaImage::new(window_size.width, window_size.height); @@ -315,48 +309,66 @@ where a: 1.0, }; + 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 screen_desc = ScreenDescriptor { + size_in_pixels: display.window.inner_size().into(), + pixels_per_point: display.window.scale_factor() as f32, + }; + + 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, .. } => 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_camera.rotate(mouse_prev, mouse_cur); + 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_camera.rotate(mouse_prev, mouse_cur); + } + if mouse_pressed[2] { + arcball_camera.pan(mouse_cur - mouse_prev); + } + mouse_prev = mouse_cur; + } + WindowEvent::MouseWheel { delta, .. } => match delta { + MouseScrollDelta::LineDelta(_, y) => { + arcball_camera.zoom(y, 0.1); + } + MouseScrollDelta::PixelDelta(pos) => { + arcball_camera.zoom(pos.y as f32, 0.01); + } + }, + _ => (), } - if mouse_pressed[2] { - arcball_camera.pan(mouse_cur - mouse_prev); - } - mouse_prev = mouse_cur; } - WindowEvent::MouseWheel { delta, .. } => match delta { - MouseScrollDelta::LineDelta(_, y) => { - arcball_camera.zoom(y, 0.1); - } - MouseScrollDelta::PixelDelta(pos) => { - arcball_camera.zoom(pos.y as f32, 0.01); - } - }, - _ => (), - }, + } Event::MainEventsCleared => { + let egui_input = egui_state.take_egui_input(&display.window); let cam_pose = CameraPose::new( arcball_camera.eye_pos(), arcball_camera.eye_dir(), @@ -413,8 +425,74 @@ where 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).show(ctx, |ui| { + ui.label(format!("{:3} fps", fps.floor())); + }); + }); + 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; } _ => (), } diff --git a/examples/support/src/lib.rs b/examples/support/src/lib.rs index a2e845c79..97cfe3f18 100644 --- a/examples/support/src/lib.rs +++ b/examples/support/src/lib.rs @@ -6,6 +6,7 @@ pub use common::*; pub use camera::Camera; pub use display::Display; +pub use egui; pub use image::{Rgba, RgbaImage}; pub use rayon::{iter::*, prelude::*, slice::*, vec::*}; diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index 147c80d82..ab40b7802 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -38,37 +38,41 @@ fn main() { scene.attach_geometry(&triangle); scene.commit(); - support::display::run(display, move |image, _, _| { - let mut intersection_ctx = IntersectContext::coherent(); + support::display::run( + display, + move |image, _, _| { + let mut intersection_ctx = IntersectContext::coherent(); - 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; + 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 = 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]); + } - let mut ray_hit = RayHitNp::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.is_valid()) - { - 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; + let mut ray_hit = RayHitNp::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.is_valid()) + { + 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/src/main.rs b/examples/triangle_geometry/src/main.rs index 1f373a012..591346592 100644 --- a/examples/triangle_geometry/src/main.rs +++ b/examples/triangle_geometry/src/main.rs @@ -146,23 +146,21 @@ fn main() { state.scene.commit(); let mut tiled = TiledImage::new(DISPLAY_WIDTH, DISPLAY_HEIGHT, TILE_SIZE_X, TILE_SIZE_Y); - let mut last_time = 0.0; - display::run(display, move |image, camera_pose, time| { - let camera = Camera::look_dir( - camera_pose.pos, - camera_pose.dir, - camera_pose.up, - 75.0, - image.dimensions(), - ); - - render_frame(&mut tiled, image, time, &camera, &state); - - let elapsed = time - last_time; - last_time = time; - let fps = 1.0 / elapsed; - eprint!("\r{} fps", fps); - }); + display::run( + display, + move |image, camera_pose, time| { + let camera = Camera::look_dir( + camera_pose.pos, + camera_pose.dir, + camera_pose.up, + 75.0, + image.dimensions(), + ); + + render_frame(&mut tiled, image, time, &camera, &state); + }, + |_ctx| {}, + ); } // Task that renders a single pixel. From b050b1e3bcded2079718f672f487cd097ccba53e Mon Sep 17 00:00:00 2001 From: matthiascy Date: Mon, 13 Mar 2023 01:03:44 +0100 Subject: [PATCH 65/65] examples performance boost & different shading method --- examples/displacement_geometry/src/main.rs | 40 +- examples/dynamic_scene/src/main.rs | 50 +- examples/instancing/src/main.rs | 80 ++- examples/intersection_filter/src/main.rs | 50 +- examples/minimal/Cargo.toml | 2 +- examples/support/Cargo.toml | 1 + examples/support/src/display.rs | 576 +++++++++++++++++++-- examples/support/src/lib.rs | 208 +++++++- examples/triangle/src/main.rs | 20 +- examples/triangle_geometry/src/main.rs | 80 ++- src/lib.rs | 8 +- src/ray/soa.rs | 8 +- 12 files changed, 850 insertions(+), 273 deletions(-) diff --git a/examples/displacement_geometry/src/main.rs b/examples/displacement_geometry/src/main.rs index cda547c73..2ca30e7f5 100644 --- a/examples/displacement_geometry/src/main.rs +++ b/examples/displacement_geometry/src/main.rs @@ -4,8 +4,8 @@ use embree::{ }; use glam::{vec3, Vec3}; use support::{ - noise, rgba_to_u32, Camera, ParallelIterator, RgbaImage, TiledImage, DEFAULT_DISPLAY_HEIGHT, - DEFAULT_DISPLAY_WIDTH, TILE_SIZE_X, TILE_SIZE_Y, + 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; @@ -156,29 +156,18 @@ fn main() { DEFAULT_DISPLAY_HEIGHT, "Dynamic Scene", ); - let mut tiled = TiledImage::new( - DEFAULT_DISPLAY_WIDTH, - DEFAULT_DISPLAY_HEIGHT, - TILE_SIZE_X, - TILE_SIZE_Y, - ); + + let state = DebugState { + scene: scene.clone(), + user: (), + }; support::display::run( display, - move |image, camera_pose, time| { - 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, - ); - - render_frame(&mut tiled, image, &camera, &scene, cube_id, ground_id); + state, + move |_, _| {}, + move |image, camera, time, _| { + render_frame(image, camera, &scene, cube_id, ground_id); }, |_| {}, ); @@ -258,20 +247,17 @@ fn render_pixel( } fn render_frame( - tiled: &mut TiledImage, - frame: &mut RgbaImage, + frame: &mut TiledImage, camera: &Camera, scene: &Scene, cube_id: u32, ground_id: u32, ) { - tiled.reset_pixels(); - tiled.par_tiles_mut().for_each(|tile| { + 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); }); }); - tiled.write_to_image(frame); } diff --git a/examples/dynamic_scene/src/main.rs b/examples/dynamic_scene/src/main.rs index a4ca967d5..8e405ebf8 100644 --- a/examples/dynamic_scene/src/main.rs +++ b/examples/dynamic_scene/src/main.rs @@ -6,8 +6,8 @@ use embree::{ }; use glam::Vec3; use support::{ - rgba_to_u32, Camera, IndexedParallelIterator, ParallelIterator, ParallelSliceMut, RgbaImage, - TiledImage, DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH, TILE_SIZE_X, TILE_SIZE_Y, + rgba_to_u32, Camera, DebugState, IndexedParallelIterator, ParallelIterator, ParallelSliceMut, + TiledImage, }; const NUM_SPHERES: usize = 20; @@ -181,33 +181,20 @@ fn main() { scene.commit(); let display = support::Display::new(512, 512, "Dynamic Scene"); - let mut tiled = TiledImage::new( - DEFAULT_DISPLAY_WIDTH, - DEFAULT_DISPLAY_HEIGHT, - TILE_SIZE_X, - TILE_SIZE_Y, - ); + + let state = DebugState { scene, user: () }; + support::display::run( display, - move |image, camera_pose, time| { - 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, - ); - + state, + move |time, state| { for i in 0..NUM_SPHERES { - animate_sphere(&scene, i as u32, positions[i], radii[i], time); + animate_sphere(&state.scene, i as u32, positions[i], radii[i], time); } - scene.commit(); - - render_frame(&mut tiled, image, time, &scene, &camera, &colors); + state.scene.commit(); + }, + move |image, camera, time, state| { + render_frame(image, &camera, time, &colors, state); }, |_| {}, ); @@ -254,20 +241,17 @@ fn render_pixel( } fn render_frame( - tiled: &mut TiledImage, - frame: &mut RgbaImage, - time: f32, - scene: &Scene, + frame: &mut TiledImage, camera: &Camera, + time: f32, colors: &[Vec3], + state: &mut DebugState<()>, ) { - tiled.reset_pixels(); - tiled.par_tiles_mut().for_each(|tile| { + 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, scene, camera, colors); + render_pixel(x, y, pixel, time, &state.scene, camera, colors); }); }); - tiled.write_to_image(frame); } diff --git a/examples/instancing/src/main.rs b/examples/instancing/src/main.rs index 53ee1fb3d..00a37369c 100644 --- a/examples/instancing/src/main.rs +++ b/examples/instancing/src/main.rs @@ -5,12 +5,9 @@ extern crate support; use cgmath::{InnerSpace, Matrix, Matrix4, SquareMatrix, Vector3, Vector4}; use embree::{ BufferUsage, BuildQuality, Device, Format, Geometry, Instance, IntersectContext, Ray, RayHit, - Scene, SceneFlags, INVALID_ID, -}; -use support::{ - rgba_to_u32, Camera, ParallelIterator, RgbaImage, TiledImage, DEFAULT_DISPLAY_WIDTH, - TILE_SIZE_X, TILE_SIZE_Y, + SceneFlags, INVALID_ID, }; +use support::{rgba_to_u32, Camera, DebugState, ParallelIterator, TiledImage}; const NUM_PHI: usize = 5; const NUM_THETA: usize = 2 * NUM_PHI; @@ -166,7 +163,7 @@ fn animate_instances( } } -struct State { +struct UserState { transforms: Vec>, normal_transforms: Vec>, ground_plane_id: u32, @@ -214,50 +211,36 @@ fn main() { let ground_plane = create_ground_plane(&device); let ground_plane_id = scene.attach_geometry(&ground_plane); - let mut state = State { + 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 mut tiled = TiledImage::new( - DEFAULT_DISPLAY_WIDTH, - DEFAULT_DISPLAY_WIDTH, - TILE_SIZE_X, - TILE_SIZE_Y, - ); + let state = DebugState { + scene: scene.clone(), + user: user_state, + }; support::display::run( display, - move |image, camera_pose, time| { - for p in image.iter_mut() { - *p = 0; - } + state, + move |time, state| { // Update scene transformations animate_instances( time, instances.len(), - &mut state.transforms, - &mut state.normal_transforms, + &mut state.user.transforms, + &mut state.user.normal_transforms, ); - for (inst, tfm) in instances.iter_mut().zip(state.transforms.iter()) { + for (inst, tfm) in instances.iter_mut().zip(state.user.transforms.iter()) { inst.set_transform(0, tfm.as_ref()); inst.commit(); } - scene.commit(); - - let img_dims = image.dimensions(); - let camera = Camera::look_dir( - camera_pose.pos, - camera_pose.dir, - camera_pose.up, - 55.0, - img_dims, - ); - - render_frame(&mut tiled, image, time, &scene, &camera, &state); + state.scene.commit(); }, + render_frame, |_| {}, ); } @@ -267,9 +250,8 @@ fn render_pixel( y: u32, pixel: &mut u32, _time: f32, - scene: &Scene, camera: &Camera, - state: &State, + state: &DebugState, ) { let mut ctx = IntersectContext::coherent(); let dir = camera.ray_dir((x as f32 + 0.5, y as f32 + 0.5)); @@ -279,7 +261,7 @@ fn render_pixel( 0.001, f32::INFINITY, )); - scene.intersect(&mut ctx, &mut ray_hit); + state.scene.intersect(&mut ctx, &mut ray_hit); if ray_hit.hit.is_valid() { // Transform the normals of the instances into world space with the @@ -289,7 +271,7 @@ fn render_pixel( let inst_id = hit.instID[0]; let mut normal = Vector3::from(hit.unit_normal()); if inst_id != INVALID_ID { - let v = state.normal_transforms[inst_id as usize] + 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() } @@ -297,17 +279,21 @@ fn render_pixel( let shadow_pos = camera.pos + dir * ray_hit.ray.tfar; let mut shadow_ray = Ray::segment( shadow_pos.into(), - state.light_dir.into(), + state.user.light_dir.into(), 0.001, f32::INFINITY, ); - scene.occluded(&mut ctx, &mut shadow_ray); + state.scene.occluded(&mut ctx, &mut shadow_ray); if shadow_ray.tfar >= 0.0 { - illum = support::clamp(illum + f32::max(state.light_dir.dot(normal), 0.0), 0.0, 1.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.ground_plane_id { + *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, @@ -328,20 +314,16 @@ fn render_pixel( } fn render_frame( - tiled: &mut TiledImage, - frame: &mut RgbaImage, - time: f32, - scene: &Scene, + frame: &mut TiledImage, camera: &Camera, - state: &State, + time: f32, + state: &mut DebugState, ) { - tiled.reset_pixels(); - tiled.par_tiles_mut().for_each(|tile| { + 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, scene, camera, &state); + render_pixel(x, y, pixel, time, camera, state); }); }); - tiled.write_to_image(frame); } diff --git a/examples/intersection_filter/src/main.rs b/examples/intersection_filter/src/main.rs index a54ba3b78..fa96bf0f1 100644 --- a/examples/intersection_filter/src/main.rs +++ b/examples/intersection_filter/src/main.rs @@ -15,8 +15,8 @@ use embree::{ }; use glam::{vec3, Mat4, Vec3, Vec4}; use support::{ - rgba_to_u32, Camera, Mode, ParallelIterator, RgbaImage, Tile, TiledImage, - DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH, TILE_SIZE_X, TILE_SIZE_Y, + rgba_to_u32, Camera, DebugState, Mode, ParallelIterator, TileMut, TiledImage, + DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH, }; const CUBE_NUM_VERTICES: usize = 8; @@ -180,7 +180,7 @@ fn render_pixel(x: u32, y: u32, camera: &Camera, scene: &Scene) -> u32 { ) } -fn render_tile(tile: &mut Tile, camera: &Camera, scene: &Scene) { +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; @@ -188,7 +188,7 @@ fn render_tile(tile: &mut Tile, camera: &Camera, scene: &Scene) { }); } -fn render_tile_stream(tile: &mut Tile, width: u32, height: u32, camera: &Camera, scene: &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; @@ -325,23 +325,21 @@ fn render_tile_stream(tile: &mut Tile, width: u32, height: u32, camera: &Camera, } } -fn render_frame(tiled: &mut TiledImage, frame: &mut RgbaImage, camera: &Camera, scene: &Scene) { - tiled.reset_pixels(); - let width = tiled.width; - let height = tiled.height; +fn render_frame(frame: &mut TiledImage, camera: &Camera, scene: &Scene) { + let width = frame.width; + let height = frame.height; match MODE { Mode::Normal => { - tiled + frame .par_tiles_mut() .for_each(|mut tile| render_tile(&mut tile, camera, scene)); } Mode::Stream => { - tiled + frame .par_tiles_mut() .for_each(|mut tile| render_tile_stream(&mut tile, width, height, camera, scene)); } } - tiled.write_to_image(frame); } fn intersect_filter<'a>( @@ -593,28 +591,18 @@ fn main() { DEFAULT_DISPLAY_HEIGHT, "Intersection Filter", ); - let mut tiled = TiledImage::new( - DEFAULT_DISPLAY_WIDTH, - DEFAULT_DISPLAY_HEIGHT, - TILE_SIZE_X, - TILE_SIZE_Y, - ); + + let state = DebugState { + scene: scene.clone(), + user: (), + }; + support::display::run( display, - move |image, camera_pose, time| { - 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, - ); - - render_frame(&mut tiled, image, &camera, &scene); + state, + |_, _| {}, + move |image, camera, _, _| { + render_frame(image, &camera, &scene); }, |_| {}, ); diff --git a/examples/minimal/Cargo.toml b/examples/minimal/Cargo.toml index fc931a474..6d78441c9 100644 --- a/examples/minimal/Cargo.toml +++ b/examples/minimal/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" [dependencies] embree = { path = "../.." } support = { path = "../support" } -glam = "0.22" +glam = "0.23.0" diff --git a/examples/support/Cargo.toml b/examples/support/Cargo.toml index 25168498a..b1a0483ef 100644 --- a/examples/support/Cargo.toml +++ b/examples/support/Cargo.toml @@ -12,6 +12,7 @@ clock_ticks = "0.1.1" 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"]} diff --git a/examples/support/src/display.rs b/examples/support/src/display.rs index 9c7a0298b..1485fc819 100644 --- a/examples/support/src/display.rs +++ b/examples/support/src/display.rs @@ -1,12 +1,14 @@ 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::{Vector2, Vector3}; +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}, @@ -86,7 +88,6 @@ impl Display { let window = WindowBuilder::new() .with_inner_size(win_size) .with_title(title) - .with_resizable(false) .build(&event_loop) .unwrap(); @@ -128,21 +129,35 @@ impl Display { /// 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, run_ui: U) -where - F: FnMut(&mut RgbaImage, CameraPose, f32) + 'static, +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(-30.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, @@ -183,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, @@ -216,7 +232,7 @@ where }], }); - let bindgroup = display + let mut bind_group = display .device .create_bind_group(&wgpu::BindGroupDescriptor { label: None, @@ -313,16 +329,18 @@ where 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 screen_desc = ScreenDescriptor { - size_in_pixels: display.window.inner_size().into(), + 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 { @@ -348,51 +366,218 @@ where WindowEvent::CursorMoved { position, .. } => { let mouse_cur = Vector2::new(position.x as f32, position.y as f32); if mouse_pressed[0] { - arcball_camera.rotate(mouse_prev, mouse_cur); + arcball.rotate(mouse_prev, mouse_cur); } if mouse_pressed[2] { - arcball_camera.pan(mouse_cur - mouse_prev); + arcball.pan(mouse_cur - mouse_prev); } mouse_prev = mouse_cur; } WindowEvent::MouseWheel { delta, .. } => match delta { MouseScrollDelta::LineDelta(_, y) => { - arcball_camera.zoom(y, 0.1); + arcball.zoom(y, 0.1); } MouseScrollDelta::PixelDelta(pos) => { - arcball_camera.zoom(pos.y as f32, 0.01); + 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 egui_input = egui_state.take_egui_input(&display.window); - 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 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()), @@ -401,6 +586,15 @@ 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 }); @@ -419,7 +613,7 @@ where }); 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); @@ -435,9 +629,80 @@ where { let egui_output = egui_ctx.run(egui_input, |ctx: &egui::Context| { run_ui(ctx); - egui::Window::new("fps").title_bar(false).show(ctx, |ui| { - ui.label(format!("{:3} fps", fps.floor())); - }); + 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, @@ -498,3 +763,230 @@ where } }); } + +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 97cfe3f18..d065159f7 100644 --- a/examples/support/src/lib.rs +++ b/examples/support/src/lib.rs @@ -8,8 +8,10 @@ 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::*; } @@ -23,9 +25,37 @@ pub enum Mode { 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, @@ -36,6 +66,8 @@ pub struct TiledImage { 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 { @@ -55,32 +87,79 @@ impl TiledImage { 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) { - for j in 0..self.height { + if !self.is_tiled { 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(u32_to_rgba(pixel))); + 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())); + } } } } - pub fn tile_mut(&mut self, index: usize) -> Tile<'_> { + /// 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; - Tile { + TileMut { idx, x, y, @@ -90,10 +169,67 @@ impl TiledImage { } } - pub fn tiles_mut(&mut self) -> impl Iterator> { + 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; @@ -110,7 +246,8 @@ impl TiledImage { } /// Iterate over the tiles of the tiled image. - pub fn par_tiles_mut(&mut self) -> impl IndexedParallelIterator> { + 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() @@ -118,7 +255,7 @@ impl TiledImage { 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 { + TileMut { idx, x, y, @@ -139,7 +276,7 @@ impl TiledImage { /// A tile of the tiled image. pub struct Tile<'a> { - /// The index of the tile. + /// The index of the tile in the tiled image. pub idx: u32, /// The x coordinate of the tile in the image. pub x: u32, @@ -149,24 +286,30 @@ pub struct Tile<'a> { pub w: u32, /// The height of the tile. pub h: u32, - /// The pixels of the tile. - pub pixels: &'a mut [u32], + /// The pixels of the tile, in RGBA format. + pub pixels: &'a [u32], } -/// Convert a u32 to a RGBA color. -#[inline(always)] -pub const fn u32_to_rgba(val: u32) -> [u8; 4] { - let r = (val >> 24) as u8; - let g = (val >> 16) as u8; - let b = (val >> 8) as u8; - let a = val as u8; - [r, g, b, a] +/// 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. +/// 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 { - ((r as u32) << 24) | ((g as u32) << 16) | ((b as u32) << 8) | (a as u32) + ((a as u32) << 24) | ((b as u32) << 16) | ((g as u32) << 8) | (r as u32) } /// Clamp `x` to be between `min` and `max` @@ -179,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/triangle/src/main.rs b/examples/triangle/src/main.rs index ab40b7802..8dbba1a16 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -4,6 +4,7 @@ extern crate embree; extern crate support; 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"); @@ -38,12 +39,17 @@ fn main() { scene.attach_geometry(&triangle); scene.commit(); + let state = DebugState { scene, user: () }; + support::display::run( display, - move |image, _, _| { + state, + |_, _| {}, + move |image, _, _, state| { let mut intersection_ctx = IntersectContext::coherent(); + image.reinterpret_as_none_tiled(); - let img_dims = image.dimensions(); + 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; @@ -58,18 +64,18 @@ fn main() { } let mut ray_hit = RayHitNp::new(rays); - scene.intersect_stream_soa(&mut intersection_ctx, &mut ray_hit); + 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 p = image.get_pixel_mut(i as u32, j); + let pixel = &mut image.pixels[i + (j * img_dims.0) as usize]; let uv = hit.uv(); - p[0] = (uv[0] * 255.0) as u8; - p[1] = (uv[1] * 255.0) as u8; - p[2] = 0; + *pixel = rgba_to_u32((uv[0] * 255.0) as u8, (uv[1] * 255.0) as u8, 0, 255); } } }, diff --git a/examples/triangle_geometry/src/main.rs b/examples/triangle_geometry/src/main.rs index 591346592..f3bb50cbe 100644 --- a/examples/triangle_geometry/src/main.rs +++ b/examples/triangle_geometry/src/main.rs @@ -4,7 +4,7 @@ extern crate embree; extern crate support; use embree::{ - BufferSlice, BufferUsage, Device, Format, IntersectContext, QuadMesh, Ray, RayHit, Scene, + BufferSlice, BufferUsage, Device, Format, IntersectContext, QuadMesh, Ray, RayHit, TriangleMesh, INVALID_ID, }; use glam::Vec3; @@ -36,23 +36,23 @@ fn make_cube(device: &Device, vertex_colors: &[[f32; 3]]) -> TriangleMesh<'stati .unwrap() .copy_from_slice(&[ // left side - [0, 2, 1], - [1, 2, 3], + [0, 1, 2], + [1, 3, 2], // right side - [4, 5, 6], - [5, 7, 6], + [4, 6, 5], + [5, 6, 7], // bottom side - [0, 1, 4], - [1, 5, 4], + [0, 4, 1], + [1, 4, 5], // top side - [2, 6, 3], - [3, 6, 7], + [2, 3, 6], + [3, 7, 6], // front side - [0, 4, 2], - [2, 4, 6], + [0, 2, 4], + [2, 6, 4], // back side - [1, 3, 5], - [3, 7, 5], + [1, 5, 3], + [3, 5, 7], ]); mesh.set_vertex_attribute_count(1); @@ -93,12 +93,13 @@ fn make_ground_plane(device: &Device) -> QuadMesh<'static> { mesh } -struct State { +type State = DebugState; + +struct UserState { ground_id: u32, cube_id: u32, face_colors: Vec<[f32; 3]>, light_dir: Vec3, - scene: Scene<'static>, } fn main() { @@ -107,6 +108,7 @@ fn main() { 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], @@ -118,7 +120,7 @@ fn main() { [1.0, 1.0, 1.0], ]; - let mut state = State { + let user_state = UserState { face_colors: vec![ [1.0, 0.0, 0.0], [1.0, 0.0, 0.0], @@ -136,31 +138,21 @@ fn main() { ground_id: INVALID_ID, cube_id: INVALID_ID, light_dir: Vec3::new(1.0, 1.0, 1.0).normalize(), - scene: device.create_scene().unwrap(), + }; + + let mut state = State { + scene: scene.clone(), + user: user_state, }; let cube = make_cube(&device, &vertex_colors); let ground = make_ground_plane(&device); - state.cube_id = state.scene.attach_geometry(&cube); - state.ground_id = state.scene.attach_geometry(&ground); + state.user.cube_id = state.scene.attach_geometry(&cube); + state.user.ground_id = state.scene.attach_geometry(&ground); + state.scene.commit(); - let mut tiled = TiledImage::new(DISPLAY_WIDTH, DISPLAY_HEIGHT, TILE_SIZE_X, TILE_SIZE_Y); - display::run( - display, - move |image, camera_pose, time| { - let camera = Camera::look_dir( - camera_pose.pos, - camera_pose.dir, - camera_pose.up, - 75.0, - image.dimensions(), - ); - - render_frame(&mut tiled, image, time, &camera, &state); - }, - |_ctx| {}, - ); + display::run(display, state, move |_, _| {}, render_frame, |_| {}); } // Task that renders a single pixel. @@ -176,15 +168,15 @@ fn render_pixel(x: u32, y: u32, _time: f32, camera: &Camera, state: &State) -> u 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.ground_id { + let diffuse = if ray_hit.hit.geomID == state.user.ground_id { glam::vec3(0.6, 0.6, 0.6) } else { - glam::Vec3::from(state.face_colors[ray_hit.hit.primID as usize]) + 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.light_dir.into(), + state.user.light_dir.into(), 0.001, f32::INFINITY, ); @@ -206,20 +198,12 @@ fn render_pixel(x: u32, y: u32, _time: f32, camera: &Camera, state: &State) -> u pixel } -fn render_frame( - tiled: &mut TiledImage, - frame: &mut RgbaImage, - time: f32, - camera: &Camera, - state: &State, -) { - tiled.reset_pixels(); - tiled.par_tiles_mut().for_each(|tile| { +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); }); }); - tiled.write_to_image(frame); } diff --git a/src/lib.rs b/src/lib.rs index cc2bffefe..9f5f3b2c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,12 +14,8 @@ extern crate core; use std::{ - alloc, - marker::PhantomData, - mem, - mem::needs_drop, + alloc, mem, ops::{Deref, DerefMut}, - ptr, }; mod buffer; @@ -55,7 +51,7 @@ pub type Bounds = sys::RTCBounds; /// 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. diff --git a/src/ray/soa.rs b/src/ray/soa.rs index cdd87cbdd..212de390a 100644 --- a/src/ray/soa.rs +++ b/src/ray/soa.rs @@ -1,4 +1,4 @@ -use crate::INVALID_ID; +use crate::{normalise_vector3, INVALID_ID}; use std::{ iter::{ExactSizeIterator, Iterator}, marker::PhantomData, @@ -14,6 +14,11 @@ pub trait SoARay { 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); @@ -72,6 +77,7 @@ 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 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) }