use opensubdiv_petite_sys as sys;
use crate::far::stencil_table::InterpolationMode;
use crate::far::{PatchTable, StencilTable, TopologyRefiner};
use crate::Index;
#[derive(Debug, Clone)]
pub struct LocationArray<'a> {
pub ptex_index: usize,
pub s: &'a [f32],
pub t: &'a [f32],
}
#[derive(Debug, Clone)]
pub struct LimitStencilTableOptions {
pub interpolation_mode: InterpolationMode,
pub generate_1st_derivatives: bool,
pub generate_2nd_derivatives: bool,
pub face_varying_channel: usize,
}
impl Default for LimitStencilTableOptions {
fn default() -> Self {
Self {
interpolation_mode: InterpolationMode::Vertex,
generate_1st_derivatives: true,
generate_2nd_derivatives: false,
face_varying_channel: 0,
}
}
}
pub struct LimitStencilTable {
ptr: sys::far::LimitStencilTablePtr,
has_1st_derivs: bool,
has_2nd_derivs: bool,
}
impl Drop for LimitStencilTable {
#[inline]
fn drop(&mut self) {
unsafe { sys::far::limit_stencil_table::LimitStencilTable_destroy(self.ptr) }
}
}
unsafe impl Send for LimitStencilTable {}
unsafe impl Sync for LimitStencilTable {}
impl LimitStencilTable {
pub fn new(
refiner: &TopologyRefiner,
locations: &[LocationArray<'_>],
cv_stencils: Option<&StencilTable>,
patch_table: Option<&PatchTable>,
options: LimitStencilTableOptions,
) -> crate::Result<Self> {
for loc in locations {
if loc.s.len() != loc.t.len() {
return Err(crate::Error::InvalidTopology(format!(
"LocationArray for ptex face {}: s.len()={} != t.len()={}",
loc.ptex_index,
loc.s.len(),
loc.t.len()
)));
}
}
let ffi_descs: Vec<sys::far::limit_stencil_table::LocationArrayDesc> = locations
.iter()
.map(|loc| sys::far::limit_stencil_table::LocationArrayDesc {
ptex_idx: loc.ptex_index as i32,
num_locations: loc.s.len() as i32,
s: loc.s.as_ptr(),
t: loc.t.as_ptr(),
})
.collect();
let mut opts = sys::far::limit_stencil_table::LimitStencilTableFactoryOptions::new();
opts.set_interpolation_mode(options.interpolation_mode as u32);
opts.set_generate_1st_derivatives(options.generate_1st_derivatives);
opts.set_generate_2nd_derivatives(options.generate_2nd_derivatives);
opts.fvar_channel = options.face_varying_channel as u32;
let cv_ptr = cv_stencils
.map(|s| s.0 as *const std::ffi::c_void)
.unwrap_or(std::ptr::null());
let patch_ptr = patch_table.map(|p| p.as_ptr()).unwrap_or(std::ptr::null());
let ptr = unsafe {
sys::far::limit_stencil_table::LimitStencilTableFactory_Create(
refiner.as_ptr() as *const _,
ffi_descs.as_ptr(),
ffi_descs.len() as i32,
cv_ptr,
patch_ptr,
opts.bitfield,
opts.fvar_channel,
)
};
if ptr.is_null() {
return Err(crate::Error::StencilTableCreation);
}
Ok(Self {
ptr,
has_1st_derivs: options.generate_1st_derivatives,
has_2nd_derivs: options.generate_2nd_derivatives,
})
}
#[inline]
fn as_base_ptr(&self) -> sys::far::StencilTablePtr {
self.ptr as sys::far::StencilTablePtr
}
#[inline]
pub fn len(&self) -> usize {
unsafe { sys::far::stencil_table::StencilTable_GetNumStencils(self.as_base_ptr()) as _ }
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn control_vertex_count(&self) -> usize {
unsafe {
sys::far::stencil_table::StencilTable_GetNumControlVertices(self.as_base_ptr()) as _
}
}
#[inline]
pub fn sizes(&self) -> &[i32] {
let vr = unsafe { sys::far::stencil_table::StencilTable_GetSizes(self.as_base_ptr()) };
if vr.data().is_null() || vr.size() == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts(vr.data() as _, vr.size()) }
}
}
#[inline]
pub fn offsets(&self) -> &[Index] {
let vr = unsafe { sys::far::stencil_table::StencilTable_GetOffsets(self.as_base_ptr()) };
if vr.data().is_null() || vr.size() == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts(vr.data() as *const Index, vr.size()) }
}
}
#[inline]
pub fn control_indices(&self) -> &[Index] {
let vr =
unsafe { sys::far::stencil_table::StencilTable_GetControlIndices(self.as_base_ptr()) };
if vr.data().is_null() || vr.size() == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts(vr.data() as *const Index, vr.size()) }
}
}
#[inline]
pub fn weights(&self) -> &[f32] {
let vr = unsafe { sys::far::stencil_table::StencilTable_GetWeights(self.as_base_ptr()) };
if vr.data().is_null() || vr.size() == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts(vr.data(), vr.size()) }
}
}
#[inline]
pub fn du_weights(&self) -> &[f32] {
let vr = unsafe { sys::far::limit_stencil_table::LimitStencilTable_GetDuWeights(self.ptr) };
if vr.data().is_null() || vr.size() == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts(vr.data(), vr.size()) }
}
}
#[inline]
pub fn dv_weights(&self) -> &[f32] {
let vr = unsafe { sys::far::limit_stencil_table::LimitStencilTable_GetDvWeights(self.ptr) };
if vr.data().is_null() || vr.size() == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts(vr.data(), vr.size()) }
}
}
#[inline]
pub fn duu_weights(&self) -> &[f32] {
let vr =
unsafe { sys::far::limit_stencil_table::LimitStencilTable_GetDuuWeights(self.ptr) };
if vr.data().is_null() || vr.size() == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts(vr.data(), vr.size()) }
}
}
#[inline]
pub fn duv_weights(&self) -> &[f32] {
let vr =
unsafe { sys::far::limit_stencil_table::LimitStencilTable_GetDuvWeights(self.ptr) };
if vr.data().is_null() || vr.size() == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts(vr.data(), vr.size()) }
}
}
#[inline]
pub fn dvv_weights(&self) -> &[f32] {
let vr =
unsafe { sys::far::limit_stencil_table::LimitStencilTable_GetDvvWeights(self.ptr) };
if vr.data().is_null() || vr.size() == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts(vr.data(), vr.size()) }
}
}
#[inline]
pub fn has_1st_derivatives(&self) -> bool {
self.has_1st_derivs
}
#[inline]
pub fn has_2nd_derivatives(&self) -> bool {
self.has_2nd_derivs
}
}
impl std::fmt::Debug for LimitStencilTable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LimitStencilTable")
.field("len", &self.len())
.field("control_vertex_count", &self.control_vertex_count())
.field("has_1st_derivatives", &self.has_1st_derivs)
.field("has_2nd_derivatives", &self.has_2nd_derivs)
.finish()
}
}