modelio-rs 0.2.0

Safe Rust bindings for Apple's ModelIO framework — assets, meshes, materials, lights, cameras, voxels, textures, and animation on macOS
Documentation
use std::path::Path;
use std::ptr;

use crate::error::Result;
use crate::ffi;
use crate::handle::ObjectHandle;
use crate::texture::Texture;
use crate::types::{MaterialFace, MaterialInfo, MaterialPropertyInfo, MaterialSemantic};
use crate::util::{c_string, parse_json, path_to_c_string, required_handle, take_string};

#[derive(Debug, Clone)]
pub struct Material {
    handle: ObjectHandle,
}

impl Material {
    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
        Self { handle }
    }

    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
        self.handle.as_ptr()
    }

    pub fn new(name: &str, physically_plausible: bool) -> Result<Self> {
        let name = c_string(name)?;
        let mut out_material = ptr::null_mut();
        let mut out_error = ptr::null_mut();
        let status = unsafe {
            ffi::mdl_material_new(
                name.as_ptr(),
                i32::from(physically_plausible),
                &mut out_material,
                &mut out_error,
            )
        };
        crate::util::status_result(status, out_error)?;
        Ok(Self::from_handle(required_handle(
            out_material,
            "MDLMaterial",
        )?))
    }

    pub fn info(&self) -> Result<MaterialInfo> {
        parse_json(
            unsafe { ffi::mdl_material_info_json(self.handle.as_ptr()) },
            "MDLMaterial",
        )
    }

    #[must_use]
    pub fn count(&self) -> usize {
        unsafe { ffi::mdl_material_count(self.handle.as_ptr()) as usize }
    }

    #[must_use]
    pub fn name(&self) -> Option<String> {
        take_string(unsafe { ffi::mdl_material_name_string(self.handle.as_ptr()) })
    }

    #[must_use]
    pub fn material_face(&self) -> Option<MaterialFace> {
        MaterialFace::from_raw(unsafe { ffi::mdl_material_material_face(self.handle.as_ptr()) })
    }

    pub fn set_material_face(&self, face: MaterialFace) {
        unsafe { ffi::mdl_material_set_material_face(self.handle.as_ptr(), face.as_raw()) };
    }

    pub fn remove_all_properties(&self) {
        unsafe { ffi::mdl_material_remove_all_properties(self.handle.as_ptr()) };
    }

    #[must_use]
    pub fn property(&self, index: usize) -> Option<MaterialProperty> {
        let ptr = unsafe { ffi::mdl_material_property_at(self.handle.as_ptr(), index as u64) };
        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
    }

    pub fn property_named(&self, name: &str) -> Result<Option<MaterialProperty>> {
        let name = c_string(name)?;
        let ptr = unsafe { ffi::mdl_material_property_named(self.handle.as_ptr(), name.as_ptr()) };
        Ok(unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle))
    }

    #[must_use]
    pub fn property_with_semantic(&self, semantic: MaterialSemantic) -> Option<MaterialProperty> {
        let ptr = unsafe {
            ffi::mdl_material_property_with_semantic(self.handle.as_ptr(), semantic as u32)
        };
        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
    }

    #[must_use]
    pub fn properties(&self) -> Vec<MaterialProperty> {
        (0..self.count())
            .filter_map(|index| self.property(index))
            .collect()
    }
}

#[derive(Debug, Clone)]
pub struct MaterialProperty {
    handle: ObjectHandle,
}

impl MaterialProperty {
    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
        Self { handle }
    }

    pub fn info(&self) -> Result<MaterialPropertyInfo> {
        parse_json(
            unsafe { ffi::mdl_material_property_info_json(self.handle.as_ptr()) },
            "MDLMaterialProperty",
        )
    }

    #[must_use]
    pub fn texture(&self) -> Option<Texture> {
        let ptr = unsafe { ffi::mdl_material_property_texture(self.handle.as_ptr()) };
        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Texture::from_handle)
    }

    pub fn set_float(&self, value: f32) {
        unsafe { ffi::mdl_material_property_set_float(self.handle.as_ptr(), value) };
    }

    pub fn set_float2(&self, value: [f32; 2]) {
        unsafe { ffi::mdl_material_property_set_float2(self.handle.as_ptr(), value[0], value[1]) };
    }

    pub fn set_float3(&self, value: [f32; 3]) {
        unsafe {
            ffi::mdl_material_property_set_float3(
                self.handle.as_ptr(),
                value[0],
                value[1],
                value[2],
            );
        };
    }

    pub fn set_float4(&self, value: [f32; 4]) {
        unsafe {
            ffi::mdl_material_property_set_float4(
                self.handle.as_ptr(),
                value[0],
                value[1],
                value[2],
                value[3],
            );
        };
    }

    pub fn set_matrix4x4(&self, value: [f32; 16]) {
        unsafe { ffi::mdl_material_property_set_matrix4x4(self.handle.as_ptr(), value.as_ptr()) };
    }

    pub fn set_string(&self, value: Option<&str>) -> Result<()> {
        let value = value.map(c_string).transpose()?;
        unsafe {
            ffi::mdl_material_property_set_string(
                self.handle.as_ptr(),
                value.as_ref().map_or(ptr::null(), |value| value.as_ptr()),
            );
        };
        Ok(())
    }

    pub fn set_url(&self, path: Option<impl AsRef<Path>>) -> Result<()> {
        let path = path
            .map(|path| path_to_c_string(path.as_ref()))
            .transpose()?;
        unsafe {
            ffi::mdl_material_property_set_url(
                self.handle.as_ptr(),
                path.as_ref().map_or(ptr::null(), |path| path.as_ptr()),
            );
        };
        Ok(())
    }

    pub fn set_color(&self, color: [f32; 4]) {
        unsafe {
            ffi::mdl_material_property_set_color(
                self.handle.as_ptr(),
                color[0],
                color[1],
                color[2],
                color[3],
            );
        };
    }

    pub fn set_luminance(&self, value: f32) {
        unsafe { ffi::mdl_material_property_set_luminance(self.handle.as_ptr(), value) };
    }
}