#![allow(non_snake_case)]
use lazy_static::lazy_static;
use std::{
ffi::{CStr, CString},
os::raw::{c_char, c_float, c_int},
path::Path,
slice,
};
use thiserror::Error;
trait Api {
fn DlVDBGetFileBBox(&self, filename: *const c_char, bbox: *mut f64) -> bool;
fn DlVDBGetGridNames(
&self,
filename: *const c_char,
num_grids: *mut c_int,
grid_names: *mut *const *const c_char,
) -> bool;
fn DlVDBFreeGridNames(&self, grid_names: *const *const c_char);
fn DlVDBGeneratePoints(
&self,
filename: *const c_char,
densitygrid: *const c_char,
num_points: *mut usize,
points: *mut *const c_float,
);
fn DlVDBFreePoints(&self, points: *const c_float);
}
#[cfg(not(feature = "link_lib3delight"))]
mod dynamic;
#[cfg(not(feature = "link_lib3delight"))]
use self::dynamic as api;
#[cfg(feature = "link_lib3delight")]
mod linked;
#[cfg(feature = "link_lib3delight")]
use self::linked as api;
lazy_static! {
static ref DL_OPENVDB_API: api::ApiImpl = api::ApiImpl::new().unwrap();
}
pub type Bounds = [f64; 6];
pub struct DlOpenVdbQuery {
file: CString,
}
#[derive(Error, Debug)]
pub enum Error {
#[error("OpenVDB file does not exist")]
MissingVdbFile,
#[error("bounding box could not be read")]
BoundsCouldNotBeRead,
#[error("grid names could not be read")]
GridNamesCouldNotBeRead,
}
type Result<T> = std::result::Result<T, Error>;
impl DlOpenVdbQuery {
pub fn new<P: AsRef<Path>>(file: P) -> Result<Self> {
if file.as_ref().exists() {
Ok(Self {
file: CString::new(file.as_ref().to_string_lossy().into_owned()).unwrap(),
})
} else {
Err(Error::MissingVdbFile)
}
}
pub fn bounding_box(&self) -> Result<Bounds> {
let mut bounds = std::mem::MaybeUninit::<Bounds>::uninit();
match DL_OPENVDB_API
.DlVDBGetFileBBox(self.file.as_ptr(), bounds.as_mut_ptr() as *mut _ as _)
{
true => Ok(unsafe { bounds.assume_init() }),
false => Err(Error::BoundsCouldNotBeRead),
}
}
pub fn grid_names(&self) -> Result<Vec<String>> {
let mut num_grids = std::mem::MaybeUninit::<c_int>::uninit();
let grid_names = std::mem::MaybeUninit::<*const *const c_char>::uninit();
match DL_OPENVDB_API.DlVDBGetGridNames(
self.file.as_ptr(),
num_grids.as_mut_ptr(),
grid_names.as_ptr() as *const *const _ as _,
) {
true => unsafe {
let grid_names = grid_names.assume_init();
let result = slice::from_raw_parts(grid_names, num_grids.assume_init() as usize)
.iter()
.map(|n| CStr::from_ptr(*n).to_string_lossy().into_owned())
.collect();
DL_OPENVDB_API.DlVDBFreeGridNames(grid_names);
Ok(result)
},
false => Err(Error::GridNamesCouldNotBeRead),
}
}
pub fn density_to_points(&self, density_grid_name: impl Into<Vec<u8>>) -> Option<Vec<f32>> {
let mut num_points = std::mem::MaybeUninit::<usize>::uninit();
let points = std::mem::MaybeUninit::<*const c_float>::uninit();
let grid_name = CString::new(density_grid_name).unwrap();
DL_OPENVDB_API.DlVDBGeneratePoints(
self.file.as_ptr(),
grid_name.as_ptr(),
num_points.as_mut_ptr(),
points.as_ptr() as *const *const _ as _,
);
unsafe {
let num_points = num_points.assume_init();
if num_points != 0 {
let points = points.assume_init();
let points_vec = slice::from_raw_parts(points, num_points * 3).to_vec();
DL_OPENVDB_API.DlVDBFreePoints(points);
Some(points_vec)
} else {
None
}
}
}
}