use std::panic::AssertUnwindSafe;
use std::ptr;
use crate::error::{ModelIoError, Result};
use crate::ffi;
use crate::handle::ObjectHandle;
use crate::mesh::MeshBuffer;
use crate::types::MeshBufferType;
use crate::util::required_handle;
type MeshBufferAllocatorCallbackFn =
dyn Fn(MeshBufferAllocatorEvent) -> MeshBufferAllocatorResponse + Send + Sync + 'static;
struct MeshBufferAllocatorCallback {
callback: Box<MeshBufferAllocatorCallbackFn>,
}
#[derive(Debug, Clone)]
pub enum MeshBufferAllocatorEvent {
NewZone { capacity: usize },
NewZoneForBuffers {
sizes: Vec<usize>,
types: Vec<MeshBufferType>,
},
NewBuffer {
length: usize,
buffer_type: MeshBufferType,
},
NewBufferWithData {
data: Vec<u8>,
buffer_type: MeshBufferType,
},
NewBufferFromZone {
zone: Option<MeshBufferZone>,
length: usize,
buffer_type: MeshBufferType,
},
NewBufferFromZoneWithData {
zone: Option<MeshBufferZone>,
data: Vec<u8>,
buffer_type: MeshBufferType,
},
}
#[derive(Debug, Clone)]
pub enum MeshBufferAllocatorResponse {
Zone(Option<MeshBufferZone>),
Buffer(Option<MeshBuffer>),
None,
}
fn callback_response(
context: *mut core::ffi::c_void,
event: MeshBufferAllocatorEvent,
) -> Option<MeshBufferAllocatorResponse> {
let context = (!context.is_null()).then_some(context.cast::<MeshBufferAllocatorCallback>())?;
std::panic::catch_unwind(AssertUnwindSafe(|| {
(unsafe { &*context }.callback)(event)
}))
.ok()
}
fn zone_from_retained_ptr(ptr: *mut core::ffi::c_void) -> Option<MeshBufferZone> {
unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MeshBufferZone::from_handle)
}
fn zone_ptr_from_response(response: Option<MeshBufferAllocatorResponse>) -> *mut core::ffi::c_void {
match response {
Some(MeshBufferAllocatorResponse::Zone(Some(zone))) =>
unsafe { ffi::mdl_object_retain(zone.as_ptr()) },
_ => ptr::null_mut(),
}
}
fn buffer_ptr_from_response(
response: Option<MeshBufferAllocatorResponse>,
) -> *mut core::ffi::c_void {
match response {
Some(MeshBufferAllocatorResponse::Buffer(Some(buffer))) =>
unsafe { ffi::mdl_object_retain(buffer.as_ptr()) },
_ => ptr::null_mut(),
}
}
fn mesh_buffer_type(raw: u32) -> Option<MeshBufferType> {
MeshBufferType::from_raw(raw)
}
#[no_mangle]
pub extern "C" fn mdlx_mesh_buffer_allocator_new_zone(
context: *mut core::ffi::c_void,
capacity: u64,
) -> *mut core::ffi::c_void {
zone_ptr_from_response(callback_response(
context,
MeshBufferAllocatorEvent::NewZone {
capacity: capacity as usize,
},
))
}
#[no_mangle]
pub extern "C" fn mdlx_mesh_buffer_allocator_new_zone_for_buffers_with_size(
context: *mut core::ffi::c_void,
sizes: *const u64,
types: *const u32,
count: u64,
) -> *mut core::ffi::c_void {
let sizes = if count == 0 {
Vec::new()
} else if sizes.is_null() {
return ptr::null_mut();
} else {
unsafe { std::slice::from_raw_parts(sizes, count as usize) }
.iter()
.map(|size| *size as usize)
.collect::<Vec<_>>()
};
let Some(types) = (if count == 0 {
Some(Vec::new())
} else if types.is_null() {
None
} else {
unsafe { std::slice::from_raw_parts(types, count as usize) }
.iter()
.copied()
.map(mesh_buffer_type)
.collect::<Option<Vec<_>>>()
}) else {
return ptr::null_mut();
};
zone_ptr_from_response(callback_response(
context,
MeshBufferAllocatorEvent::NewZoneForBuffers { sizes, types },
))
}
#[no_mangle]
pub extern "C" fn mdlx_mesh_buffer_allocator_new_buffer(
context: *mut core::ffi::c_void,
length: u64,
buffer_type: u32,
) -> *mut core::ffi::c_void {
let Some(buffer_type) = mesh_buffer_type(buffer_type) else {
return ptr::null_mut();
};
buffer_ptr_from_response(callback_response(
context,
MeshBufferAllocatorEvent::NewBuffer {
length: length as usize,
buffer_type,
},
))
}
#[no_mangle]
pub extern "C" fn mdlx_mesh_buffer_allocator_new_buffer_with_data(
context: *mut core::ffi::c_void,
bytes: *const u8,
count: u64,
buffer_type: u32,
) -> *mut core::ffi::c_void {
let Some(buffer_type) = mesh_buffer_type(buffer_type) else {
return ptr::null_mut();
};
let data = if count == 0 || bytes.is_null() {
Vec::new()
} else {
unsafe { std::slice::from_raw_parts(bytes, count as usize) }.to_vec()
};
buffer_ptr_from_response(callback_response(
context,
MeshBufferAllocatorEvent::NewBufferWithData { data, buffer_type },
))
}
#[no_mangle]
pub extern "C" fn mdlx_mesh_buffer_allocator_new_buffer_from_zone_length(
context: *mut core::ffi::c_void,
zone: *mut core::ffi::c_void,
length: u64,
buffer_type: u32,
) -> *mut core::ffi::c_void {
let Some(buffer_type) = mesh_buffer_type(buffer_type) else {
return ptr::null_mut();
};
buffer_ptr_from_response(callback_response(
context,
MeshBufferAllocatorEvent::NewBufferFromZone {
zone: zone_from_retained_ptr(zone),
length: length as usize,
buffer_type,
},
))
}
#[no_mangle]
pub extern "C" fn mdlx_mesh_buffer_allocator_new_buffer_from_zone_data(
context: *mut core::ffi::c_void,
zone: *mut core::ffi::c_void,
bytes: *const u8,
count: u64,
buffer_type: u32,
) -> *mut core::ffi::c_void {
let Some(buffer_type) = mesh_buffer_type(buffer_type) else {
return ptr::null_mut();
};
let data = if count == 0 || bytes.is_null() {
Vec::new()
} else {
unsafe { std::slice::from_raw_parts(bytes, count as usize) }.to_vec()
};
buffer_ptr_from_response(callback_response(
context,
MeshBufferAllocatorEvent::NewBufferFromZoneWithData {
zone: zone_from_retained_ptr(zone),
data,
buffer_type,
},
))
}
#[no_mangle]
pub extern "C" fn mdlx_mesh_buffer_allocator_release(context: *mut core::ffi::c_void) {
if context.is_null() {
return;
}
unsafe { drop(Box::from_raw(context.cast::<MeshBufferAllocatorCallback>())) };
}
fn release_callback_context(context: *mut core::ffi::c_void) {
mdlx_mesh_buffer_allocator_release(context);
}
#[derive(Debug, Clone)]
pub struct MeshBufferMap {
handle: ObjectHandle,
length: usize,
}
impl MeshBufferMap {
fn from_handle(handle: ObjectHandle, length: usize) -> Self {
Self { handle, length }
}
pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
self.handle.as_ptr()
}
#[must_use]
pub fn length(&self) -> usize {
self.length
}
#[must_use]
pub fn bytes(&self) -> Vec<u8> {
let mut bytes = vec![0_u8; self.length];
if bytes.is_empty() {
return bytes;
}
let written = unsafe {
ffi::mdl_mesh_buffer_map_copy_bytes(
self.as_ptr(),
self.length as u64,
bytes.as_mut_ptr(),
bytes.len() as u64,
)
} as usize;
bytes.truncate(written);
bytes
}
pub fn write(&self, offset: usize, bytes: &[u8]) -> usize {
unsafe {
ffi::mdl_mesh_buffer_map_write_bytes(
self.as_ptr(),
self.length as u64,
bytes.as_ptr(),
bytes.len() as u64,
offset as u64,
) as usize
}
}
}
#[derive(Debug, Clone)]
pub struct MeshBufferAllocator {
handle: ObjectHandle,
}
impl MeshBufferAllocator {
pub fn new<F>(callback: F) -> Result<Self>
where
F: Fn(MeshBufferAllocatorEvent) -> MeshBufferAllocatorResponse + Send + Sync + 'static,
{
let callback = Box::new(MeshBufferAllocatorCallback {
callback: Box::new(callback),
});
let callback_ptr = Box::into_raw(callback).cast::<core::ffi::c_void>();
let mut out_allocator = ptr::null_mut();
let mut out_error = ptr::null_mut();
let status = unsafe {
ffi::mdl_mesh_buffer_allocator_new_with_callback(
callback_ptr,
&mut out_allocator,
&mut out_error,
)
};
if let Err(error) = crate::util::status_result(status, out_error) {
release_callback_context(callback_ptr);
return Err(error);
}
match required_handle(out_allocator, "MDLMeshBufferAllocator") {
Ok(handle) => Ok(Self::from_handle(handle)),
Err(error) => {
release_callback_context(callback_ptr);
Err(error)
}
}
}
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_zone(&self, capacity: usize) -> Result<MeshBufferZone> {
let mut out_zone = ptr::null_mut();
let mut out_error = ptr::null_mut();
let status = unsafe {
ffi::mdl_mesh_buffer_allocator_new_zone(
self.as_ptr(),
capacity as u64,
&mut out_zone,
&mut out_error,
)
};
crate::util::status_result(status, out_error)?;
Ok(MeshBufferZone::from_handle(required_handle(
out_zone,
"MDLMeshBufferZone",
)?))
}
pub fn new_zone_for_buffers(
&self,
sizes: &[usize],
types: &[MeshBufferType],
) -> Result<MeshBufferZone> {
if sizes.len() != types.len() {
return Err(ModelIoError::new(
ffi::status::INVALID_ARGUMENT,
"mesh buffer sizes and types must have the same length",
));
}
let raw_sizes = sizes.iter().map(|size| *size as u64).collect::<Vec<_>>();
let raw_types = types
.iter()
.map(|buffer_type| buffer_type.as_raw())
.collect::<Vec<_>>();
let mut out_zone = ptr::null_mut();
let mut out_error = ptr::null_mut();
let status = unsafe {
ffi::mdl_mesh_buffer_allocator_new_zone_for_buffers_with_size(
self.as_ptr(),
raw_sizes.as_ptr(),
raw_types.as_ptr(),
raw_sizes.len() as u64,
&mut out_zone,
&mut out_error,
)
};
crate::util::status_result(status, out_error)?;
Ok(MeshBufferZone::from_handle(required_handle(
out_zone,
"MDLMeshBufferZone",
)?))
}
pub fn new_buffer(&self, length: usize, buffer_type: MeshBufferType) -> Result<MeshBuffer> {
let mut out_buffer = ptr::null_mut();
let mut out_error = ptr::null_mut();
let status = unsafe {
ffi::mdl_mesh_buffer_allocator_new_buffer(
self.as_ptr(),
length as u64,
buffer_type.as_raw(),
&mut out_buffer,
&mut out_error,
)
};
crate::util::status_result(status, out_error)?;
Ok(MeshBuffer::from_handle(required_handle(
out_buffer,
"MDLMeshBuffer",
)?))
}
pub fn new_buffer_with_data(
&self,
data: &[u8],
buffer_type: MeshBufferType,
) -> Result<MeshBuffer> {
let mut out_buffer = ptr::null_mut();
let mut out_error = ptr::null_mut();
let status = unsafe {
ffi::mdl_mesh_buffer_allocator_new_buffer_with_data(
self.as_ptr(),
data.as_ptr(),
data.len() as u64,
buffer_type.as_raw(),
&mut out_buffer,
&mut out_error,
)
};
crate::util::status_result(status, out_error)?;
Ok(MeshBuffer::from_handle(required_handle(
out_buffer,
"MDLMeshBuffer",
)?))
}
pub fn new_buffer_from_zone(
&self,
zone: Option<&MeshBufferZone>,
length: usize,
buffer_type: MeshBufferType,
) -> Result<Option<MeshBuffer>> {
let mut out_buffer = ptr::null_mut();
let mut out_error = ptr::null_mut();
let status = unsafe {
ffi::mdl_mesh_buffer_allocator_new_buffer_from_zone_length(
self.as_ptr(),
zone.map_or(ptr::null_mut(), MeshBufferZone::as_ptr),
length as u64,
buffer_type.as_raw(),
&mut out_buffer,
&mut out_error,
)
};
crate::util::status_result(status, out_error)?;
Ok(unsafe { ObjectHandle::from_retained_ptr(out_buffer) }.map(MeshBuffer::from_handle))
}
pub fn new_buffer_from_zone_with_data(
&self,
zone: Option<&MeshBufferZone>,
data: &[u8],
buffer_type: MeshBufferType,
) -> Result<Option<MeshBuffer>> {
let mut out_buffer = ptr::null_mut();
let mut out_error = ptr::null_mut();
let status = unsafe {
ffi::mdl_mesh_buffer_allocator_new_buffer_from_zone_data(
self.as_ptr(),
zone.map_or(ptr::null_mut(), MeshBufferZone::as_ptr),
data.as_ptr(),
data.len() as u64,
buffer_type.as_raw(),
&mut out_buffer,
&mut out_error,
)
};
crate::util::status_result(status, out_error)?;
Ok(unsafe { ObjectHandle::from_retained_ptr(out_buffer) }.map(MeshBuffer::from_handle))
}
}
#[derive(Debug, Clone)]
pub struct MeshBufferZone {
handle: ObjectHandle,
}
impl MeshBufferZone {
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()
}
#[must_use]
pub fn capacity(&self) -> usize {
unsafe { ffi::mdl_mesh_buffer_zone_capacity(self.as_ptr()) as usize }
}
#[must_use]
pub fn allocator(&self) -> Option<MeshBufferAllocator> {
let ptr = unsafe { ffi::mdl_mesh_buffer_zone_allocator(self.as_ptr()) };
unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MeshBufferAllocator::from_handle)
}
#[must_use]
pub fn as_default(&self) -> Option<MeshBufferZoneDefault> {
(unsafe { ffi::mdl_mesh_buffer_zone_is_default(self.as_ptr()) != 0 })
.then(|| MeshBufferZoneDefault::from_handle(self.handle.clone()))
}
}
#[derive(Debug, Clone)]
pub struct MeshBufferZoneDefault {
handle: ObjectHandle,
}
impl MeshBufferZoneDefault {
fn from_handle(handle: ObjectHandle) -> Self {
Self { handle }
}
pub fn new() -> Result<Self> {
let mut out_zone = ptr::null_mut();
let mut out_error = ptr::null_mut();
let status =
unsafe { ffi::mdl_mesh_buffer_zone_default_new(&mut out_zone, &mut out_error) };
crate::util::status_result(status, out_error)?;
Ok(Self::from_handle(required_handle(
out_zone,
"MDLMeshBufferZoneDefault",
)?))
}
#[must_use]
pub fn capacity(&self) -> usize {
unsafe { ffi::mdl_mesh_buffer_zone_capacity(self.handle.as_ptr()) as usize }
}
#[must_use]
pub fn allocator(&self) -> Option<MeshBufferAllocator> {
let ptr = unsafe { ffi::mdl_mesh_buffer_zone_allocator(self.handle.as_ptr()) };
unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MeshBufferAllocator::from_handle)
}
#[must_use]
pub fn as_mesh_buffer_zone(&self) -> MeshBufferZone {
MeshBufferZone::from_handle(self.handle.clone())
}
}
#[derive(Debug, Clone)]
pub struct MeshBufferData {
handle: ObjectHandle,
}
impl MeshBufferData {
fn from_handle(handle: ObjectHandle) -> Self {
Self { handle }
}
pub fn new(length: usize, buffer_type: MeshBufferType) -> Result<Self> {
let mut out_buffer = ptr::null_mut();
let mut out_error = ptr::null_mut();
let status = unsafe {
ffi::mdl_mesh_buffer_data_new(
length as u64,
buffer_type.as_raw(),
&mut out_buffer,
&mut out_error,
)
};
crate::util::status_result(status, out_error)?;
Ok(Self::from_handle(required_handle(
out_buffer,
"MDLMeshBufferData",
)?))
}
pub fn from_bytes(data: &[u8], buffer_type: MeshBufferType) -> Result<Self> {
let mut out_buffer = ptr::null_mut();
let mut out_error = ptr::null_mut();
let status = unsafe {
ffi::mdl_mesh_buffer_data_new_with_bytes(
data.as_ptr(),
data.len() as u64,
buffer_type.as_raw(),
&mut out_buffer,
&mut out_error,
)
};
crate::util::status_result(status, out_error)?;
Ok(Self::from_handle(required_handle(
out_buffer,
"MDLMeshBufferData",
)?))
}
#[must_use]
pub fn data(&self) -> Vec<u8> {
let info = self.as_mesh_buffer().info().ok();
let mut bytes = vec![0_u8; info.map_or(0, |buffer| buffer.length)];
if bytes.is_empty() {
return bytes;
}
let written = unsafe {
ffi::mdl_mesh_buffer_data_copy_data(
self.handle.as_ptr(),
bytes.as_mut_ptr(),
bytes.len() as u64,
)
} as usize;
bytes.truncate(written);
bytes
}
pub fn map(&self) -> Result<MeshBufferMap> {
self.as_mesh_buffer().map()
}
#[must_use]
pub fn as_mesh_buffer(&self) -> MeshBuffer {
MeshBuffer::from_handle(self.handle.clone())
}
}
#[derive(Debug, Clone)]
pub struct MeshBufferDataAllocator {
handle: ObjectHandle,
}
impl MeshBufferDataAllocator {
pub fn new() -> Result<Self> {
let mut out_allocator = ptr::null_mut();
let mut out_error = ptr::null_mut();
let status =
unsafe { ffi::mdl_mesh_buffer_data_allocator_new(&mut out_allocator, &mut out_error) };
crate::util::status_result(status, out_error)?;
Ok(Self {
handle: required_handle(out_allocator, "MDLMeshBufferDataAllocator")?,
})
}
#[must_use]
pub fn as_mesh_buffer_allocator(&self) -> MeshBufferAllocator {
MeshBufferAllocator::from_handle(self.handle.clone())
}
pub fn new_default_zone(&self, capacity: usize) -> Result<MeshBufferZoneDefault> {
self.as_mesh_buffer_allocator()
.new_zone(capacity)?
.as_default()
.ok_or_else(|| {
ModelIoError::new(
ffi::status::NULL_RESULT,
"MDLMeshBufferDataAllocator zone was not MDLMeshBufferZoneDefault",
)
})
}
}
impl MeshBuffer {
pub fn fill_data(&self, data: &[u8], offset: usize) {
unsafe {
ffi::mdl_mesh_buffer_fill_data(
self.as_ptr(),
data.as_ptr(),
data.len() as u64,
offset as u64,
);
}
}
pub fn map(&self) -> Result<MeshBufferMap> {
let length = self.info()?.length;
let ptr = unsafe { ffi::mdl_mesh_buffer_map(self.as_ptr()) };
Ok(MeshBufferMap::from_handle(
required_handle(ptr, "MDLMeshBufferMap")?,
length,
))
}
#[must_use]
pub fn allocator(&self) -> Option<MeshBufferAllocator> {
let ptr = unsafe { ffi::mdl_mesh_buffer_allocator(self.as_ptr()) };
unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MeshBufferAllocator::from_handle)
}
#[must_use]
pub fn zone(&self) -> Option<MeshBufferZone> {
let ptr = unsafe { ffi::mdl_mesh_buffer_zone(self.as_ptr()) };
unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MeshBufferZone::from_handle)
}
#[must_use]
pub fn as_data_buffer(&self) -> Option<MeshBufferData> {
if unsafe { ffi::mdl_mesh_buffer_is_data(self.as_ptr()) == 0 } {
return None;
}
let retained = unsafe { ffi::mdl_object_retain(self.as_ptr()) };
unsafe { ObjectHandle::from_retained_ptr(retained) }.map(MeshBufferData::from_handle)
}
}