embree4-rs 0.0.16

High-level wrapper for Intel's Embree 4 high-performance ray tracing library.
Documentation
use std::ptr::null_mut;

use anyhow::{bail, Result};
use embree4_sys::{RTCBounds, RTCIntersectArguments};

use crate::{device_error_raw, geometry::Geometry, Device};

pub struct Scene {
    pub device: Device,
    handle: embree4_sys::RTCScene,
}

impl Scene {
    /// Constructs a new `Scene` instance from the given options.
    ///
    /// # Arguments
    /// * `device` - A reference to the `Device` instance.
    /// * `options` - The options for creating the scene.
    ///
    /// # Returns
    /// A `Result` containing the `Scene` instance if successful, or an error if an error occurred.
    ///
    /// # Example
    /// ```
    /// use embree4_rs::*;
    /// use embree4_sys::*;
    ///
    /// let device = Device::try_new(None).unwrap();
    /// let options = SceneOptions {
    ///     build_quality: RTCBuildQuality::HIGH,
    ///     flags: RTCSceneFlags::COMPACT | RTCSceneFlags::ROBUST,
    /// };
    /// let scene = Scene::try_new(&device, options).unwrap();
    /// ```
    pub fn try_new(device: Device, options: SceneOptions) -> Result<Self> {
        let handle = unsafe { embree4_sys::rtcNewScene(device.handle) };

        if handle.is_null() {
            let error = device_error_raw(device.handle);
            bail!("Could not create scene: {:?}", error);
        }

        let scene = Scene { device, handle };

        if options.build_quality != Default::default() {
            scene.set_build_quality(options.build_quality)?;
        }

        if options.flags != Default::default() {
            scene.set_flags(options.flags)?;
        }

        Ok(scene)
    }

    /// Sets the build quality of the scene.
    ///
    /// # Arguments
    /// * `quality` - The build quality to set.
    ///
    /// # Returns
    /// A `Result` indicating success or failure.
    pub fn set_build_quality(&self, quality: embree4_sys::RTCBuildQuality) -> Result<()> {
        unsafe {
            embree4_sys::rtcSetSceneBuildQuality(self.handle, quality);
        }
        self.device
            .error_or((), "Could not set scene build quality")
    }

    /// Sets the flags of the scene.
    ///
    /// # Arguments
    /// * `flags` - The flags to set.
    ///
    /// # Returns
    /// A `Result` indicating success or failure.
    pub fn set_flags(&self, flags: embree4_sys::RTCSceneFlags) -> Result<()> {
        unsafe {
            embree4_sys::rtcSetSceneFlags(self.handle, flags);
        }
        self.device.error_or((), "Could not set scene flags")
    }

    /// Attaches the given geometry to the scene.
    ///
    /// # Arguments
    /// * `geometry` - A reference to the `Geometry` instance to attach.
    ///
    /// # Returns
    /// * A `Result` containing the geometry ID if successful, or an error if an error occurred.
    pub fn attach_geometry(&self, geometry: &dyn Geometry) -> Result<u32> {
        let geom_id = unsafe { embree4_sys::rtcAttachGeometry(self.handle, *geometry.handle()) };
        self.device.error_or(geom_id, "Could not attach geometry")
    }

    /// Commits the scene.
    ///
    /// # Returns
    /// A `Result` containing the `CommittedScene` instance if successful, or an error if an error occurred.
    ///
    /// # Example
    /// ```
    /// use embree4_rs::*;
    /// use embree4_sys::*;
    ///
    /// let device = Device::try_new(None).unwrap();
    /// let options = Default::default();
    /// let scene = Scene::try_new(&device, options).unwrap();
    /// let scene = scene.commit().unwrap();
    /// ```
    pub fn commit(self) -> Result<CommittedScene> {
        unsafe {
            embree4_sys::rtcCommitScene(self.handle);
        }
        self.device.error_or((), "Could not commit scene")?;
        Ok(CommittedScene { scene: self })
    }
}

impl Drop for Scene {
    fn drop(&mut self) {
        unsafe {
            embree4_sys::rtcReleaseScene(self.handle);
        }
    }
}

#[derive(Default)]
pub struct SceneOptions {
    pub build_quality: embree4_sys::RTCBuildQuality,
    pub flags: embree4_sys::RTCSceneFlags,
}

pub struct CommittedScene {
    pub scene: Scene,
}

unsafe impl Sync for CommittedScene {}

impl CommittedScene {
    pub fn intersect_1(
        &self,
        ray: embree4_sys::RTCRay,
        args: Option<&RTCIntersectArguments>,
    ) -> Result<Option<embree4_sys::RTCRayHit>> {
        let mut ray_hit = embree4_sys::RTCRayHit {
            ray,
            hit: Default::default(),
        };

        unsafe {
            match args {
                Some(args) => {
                    embree4_sys::rtcIntersect1(
                        self.scene.handle,
                        &mut ray_hit,
                        args as *const _ as _,
                    );
                }
                None => embree4_sys::rtcIntersect1(self.scene.handle, &mut ray_hit, null_mut()),
            }
        }
        self.scene.device.error_or((), "Could not intersect ray")?;

        Ok(
            if ray_hit.hit.geomID != embree4_sys::RTC_INVALID_GEOMETRY_ID {
                Some(ray_hit)
            } else {
                None
            },
        )
    }

    pub fn bounds(&self) -> Result<embree4_sys::RTCBounds> {
        let bounds = RTCBounds::default();
        unsafe { embree4_sys::rtcGetSceneBounds(self.scene.handle, &bounds as *const _ as *mut _) }
        self.scene
            .device
            .error_or(bounds, "Could not get scene bounds")
    }
}