use opensubdiv_petite_sys as sys;
use std::convert::TryInto;
use crate::far::TopologyDescriptor;
use crate::{Error, Index};
type Result<T, E = Error> = std::result::Result<T, E>;
pub struct TopologyRefiner(pub(crate) sys::topology_refiner::TopologyRefinerPtr);
impl TopologyRefiner {
pub fn new(descriptor: TopologyDescriptor, options: TopologyRefinerOptions) -> Result<Self> {
let sdc_options = sys::sdc::Options {
_vtxBoundInterp: match options.boundary_interpolation {
Some(interp) => interp as _,
None => sys::far::topology_refiner::VTX_BOUNDARY_NONE,
},
_fvarLinInterp: match options.face_varying_linear_interpolation {
Some(interp) => interp as _,
None => sys::far::topology_refiner::FVAR_LINEAR_NONE,
},
_creasingMethod: options.creasing_method as _,
_triangleSub: options.triangle_subdivision as _,
};
let mut sys_options: sys::far::topology_refiner::TopologyRefinerFactoryOptions =
unsafe { std::mem::zeroed() };
sys_options.schemeType = options.scheme as _;
sys_options.schemeOptions = sdc_options;
#[cfg(feature = "topology_validation")]
sys_options.set_validateFullTopology(true as _);
let ptr = unsafe {
sys::far::topology_refiner::TopologyRefinerFactory_TopologyDescriptor_Create(
&descriptor.descriptor as _,
sys_options,
)
};
if ptr.is_null() {
Err(Error::CreateTopologyRefinerFailed)
} else {
Ok(Self(ptr))
}
}
#[inline]
pub fn options(&self) -> TopologyRefinerOptions {
let options = unsafe { &(*self.0)._subdivOptions };
TopologyRefinerOptions {
scheme: unsafe { (*self.0)._subdivType }
.try_into()
.expect("invalid subdivision scheme from C++"),
boundary_interpolation: if options._vtxBoundInterp
== sys::far::topology_refiner::VTX_BOUNDARY_NONE
{
None
} else {
Some(
options
._vtxBoundInterp
.try_into()
.expect("invalid boundary interpolation from C++"),
)
},
face_varying_linear_interpolation: if options._fvarLinInterp
== sys::far::topology_refiner::FVAR_LINEAR_NONE
{
None
} else {
Some(
options
._fvarLinInterp
.try_into()
.expect("invalid face-varying interpolation from C++"),
)
},
creasing_method: options
._creasingMethod
.try_into()
.expect("invalid creasing method from C++"),
triangle_subdivision: options
._triangleSub
.try_into()
.expect("invalid triangle subdivision from C++"),
}
}
#[inline]
pub fn is_uniform(&self) -> bool {
unsafe { (*self.0)._isUniform() != 0 }
}
#[inline]
pub fn refinement_levels(&self) -> usize {
unsafe { sys::far::topology_refiner::TopologyRefiner_GetNumLevels(self.0) as _ }
}
#[inline]
pub fn max_valence(&self) -> usize {
unsafe { (*self.0)._maxValence as _ }
}
#[inline]
pub fn has_holes(&self) -> bool {
unsafe { (*self.0)._hasHoles() != 0 }
}
#[inline]
pub fn vertex_count_all_levels(&self) -> usize {
unsafe { sys::far::topology_refiner::TopologyRefiner_GetNumVerticesTotal(self.0) as _ }
}
#[deprecated(since = "0.3.0", note = "Use `vertex_count_all_levels` instead")]
#[inline]
pub fn vertex_total_count(&self) -> usize {
self.vertex_count_all_levels()
}
#[deprecated(since = "0.3.0", note = "Use `vertex_count_all_levels` instead")]
#[inline]
pub fn vertices_total_len(&self) -> usize {
self.vertex_count_all_levels()
}
#[inline]
pub fn edge_count_all_levels(&self) -> usize {
unsafe { sys::far::topology_refiner::TopologyRefiner_GetNumEdgesTotal(self.0) as _ }
}
#[deprecated(since = "0.3.0", note = "Use `edge_count_all_levels` instead")]
#[inline]
pub fn edge_total_count(&self) -> usize {
self.edge_count_all_levels()
}
#[deprecated(since = "0.3.0", note = "Use `edge_count_all_levels` instead")]
#[inline]
pub fn edges_total_len(&self) -> usize {
self.edge_count_all_levels()
}
#[inline]
pub fn face_count_all_levels(&self) -> usize {
unsafe { sys::far::topology_refiner::TopologyRefiner_GetNumFacesTotal(self.0) as _ }
}
#[deprecated(since = "0.3.0", note = "Use `face_count_all_levels` instead")]
#[inline]
pub fn face_total_count(&self) -> usize {
self.face_count_all_levels()
}
#[deprecated(since = "0.3.0", note = "Use `face_count_all_levels` instead")]
#[inline]
pub fn faces_total_len(&self) -> usize {
self.face_count_all_levels()
}
#[inline]
pub fn face_vertex_count_all_levels(&self) -> usize {
unsafe { sys::far::topology_refiner::TopologyRefiner_GetNumFaceVerticesTotal(self.0) as _ }
}
#[deprecated(since = "0.3.0", note = "Use `face_vertex_count_all_levels` instead")]
#[inline]
pub fn face_vertex_total_count(&self) -> usize {
self.face_vertex_count_all_levels()
}
#[deprecated(since = "0.3.0", note = "Use `face_vertex_count_all_levels` instead")]
#[inline]
pub fn face_vertices_total_len(&self) -> usize {
self.face_vertex_count_all_levels()
}
#[inline]
pub fn max_level(&self) -> usize {
unsafe { (*self.0)._maxLevel() as _ }
}
#[inline]
pub fn level(&self, level: usize) -> Option<TopologyLevel<'_>> {
if level > self.max_level() {
None
} else {
let ptr = unsafe {
sys::far::topology_refiner::TopologyRefiner_GetLevel(
self.0,
level.min(i32::MAX as usize) as i32,
)
};
if ptr.is_null() {
None
} else {
Some(TopologyLevel {
ptr,
refiner: std::marker::PhantomData,
})
}
}
}
#[inline]
pub fn refine_uniform(&mut self, options: UniformRefinementOptions) {
let mut sys_options: sys::far::topology_refiner::UniformRefinementOptions =
unsafe { std::mem::zeroed() };
sys_options._bitfield_1 =
sys::far::topology_refiner::UniformRefinementOptions::new_bitfield_1(
options.refinement_level.min(u32::MAX as usize) as u32,
options.order_vertices_from_faces_first as _,
options.full_topology_in_last_level as _,
);
unsafe {
(*self.0).RefineUniform(sys_options);
}
}
#[inline]
pub fn refine_adaptive(
&mut self,
options: AdaptiveRefinementOptions,
selected_faces: &[Index],
) {
let mut sys_options: sys::far::topology_refiner::AdaptiveRefinementOptions =
unsafe { std::mem::zeroed() };
sys_options._bitfield_1 =
sys::far::topology_refiner::AdaptiveRefinementOptions::new_bitfield_1(
options.isolation_level.min(u32::MAX as usize) as u32,
options.secondary_level.min(u32::MAX as usize) as u32,
options.single_crease_patch as _,
options.infintely_sharp_patch as _,
options.consider_face_varying_channels as _,
options.order_vertices_from_faces_first as _,
);
let const_array = sys::topology_refiner::ConstIndexArray {
_begin: selected_faces.as_ptr() as _,
_size: selected_faces.len().min(i32::MAX as usize) as i32,
_phantom_0: std::marker::PhantomData,
};
unsafe {
(*self.0).RefineAdaptive(sys_options, const_array);
}
}
#[inline]
pub fn unrefine(&mut self) {
unsafe {
(*self.0).Unrefine();
}
}
pub(crate) fn as_ptr(&self) -> sys::topology_refiner::TopologyRefinerPtr {
self.0
}
}
impl Drop for TopologyRefiner {
#[inline]
fn drop(&mut self) {
unsafe {
sys::far::topology_refiner::TopologyRefiner_destroy(self.0);
}
}
}
pub use sys::far::topology_refiner::{
BoundaryInterpolation, CreasingMethod, FaceVaryingLinearInterpolation, Scheme,
TriangleSubdivision,
};
use super::topology_level::TopologyLevel;
#[derive(Copy, Clone, Debug)]
pub struct TopologyRefinerOptions {
pub scheme: Scheme,
pub boundary_interpolation: Option<BoundaryInterpolation>,
pub face_varying_linear_interpolation: Option<FaceVaryingLinearInterpolation>,
pub creasing_method: CreasingMethod,
pub triangle_subdivision: TriangleSubdivision,
}
impl Default for TopologyRefinerOptions {
fn default() -> Self {
Self {
scheme: Scheme::CatmullClark,
boundary_interpolation: None,
face_varying_linear_interpolation: Some(FaceVaryingLinearInterpolation::All),
creasing_method: CreasingMethod::Uniform,
triangle_subdivision: TriangleSubdivision::CatmullClark,
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct UniformRefinementOptions {
pub refinement_level: usize,
pub order_vertices_from_faces_first: bool,
pub full_topology_in_last_level: bool,
}
impl Default for UniformRefinementOptions {
fn default() -> Self {
Self {
refinement_level: 4,
order_vertices_from_faces_first: true,
full_topology_in_last_level: true,
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct AdaptiveRefinementOptions {
pub isolation_level: usize,
pub secondary_level: usize,
pub single_crease_patch: bool,
pub infintely_sharp_patch: bool,
pub consider_face_varying_channels: bool,
pub order_vertices_from_faces_first: bool,
}
impl Default for AdaptiveRefinementOptions {
fn default() -> Self {
Self {
isolation_level: 4,
secondary_level: 15,
single_crease_patch: false,
infintely_sharp_patch: false,
consider_face_varying_channels: false,
order_vertices_from_faces_first: false,
}
}
}