subdiv-kernels
Subdivision surface kernels — Catmull–Clark, Loop, √3, and Doo–Sabin.
Subdivision refines a coarse polygon control mesh into a finer, smoother one: each step splits the faces and places the new points as weighted averages of their neighbors, converging to a smooth limit surface.
This crate computes that refinement's connectivity and weights and returns
StencilTables — sparse maps where each output point is a weighted sum of
a few input points. Supply a topology::Mesh (the control cage) and apply
the stencils to your own per-vertex data (positions, UVs, colors, …); the
crate holds no geometry and needs no host mesh type.
Example
Refine a tetrahedron and exercise the main pieces of the API — one-shot interpolation, composed-table re-evaluation, sparse-edit queries, face-varying (UV) channels, the cached refinement handle, and limit stencils.
use NonZeroU8;
use ;
// A tetrahedron: 4 vertices, 4 triangular faces, 6 edges (closed surface).
let face_vertex_indices = vec!;
let mesh = Mesh ;
let positions: =
vec!;
let refiner = new?;
let req = from;
// One-shot: interpolate any per-vertex data through all levels.
let result = refiner.refine_uniform?;
let refined = result.interpolate;
assert_eq!;
// Animation: compose the per-level stencils once, re-evaluate each frame.
// Same surface as the chained path (up to f32 rounding).
let composed = result.compose_stencils;
let composed_positions = composed.interpolate;
assert!;
// Sparse edits: which refined outputs move when control point 0 moves?
assert!;
// Face-varying UVs, smooth interior with linear island boundaries.
let uvs: = .map.collect;
let uv_channel = FaceVaryingChannel ;
let fvar_tables = refiner.face_varying_stencils?;
let refined_uvs = fvar_tables.iter.fold;
assert_eq!;
// Cached handle: query per level without recomputing topology, then take
// the owned final mesh + adjacency.
let refinement = refiner.refine_topology?;
let parts = refinement.into_final_parts;
assert_eq!;
// Limit surface: stencils for limit positions and tangents/normals.
let _limit = result.limit_stencils?;
// Write the refined surface as a Wavefront OBJ (vertices, then faces).
let mut obj = Stringnew;
for in &refined
let mut corner = 0;
for &n in &result.topology.face_vertex_counts
// std::fs::write("surface.obj", &obj)?; // ← persist to disk
assert_eq!;
Performance
RefinementResult::interpolate chains the per-level stencils — the same
algorithmic cost as direct subdivision, best for a one-shot refine. For
animation (static topology, changing data),
RefinementResult::compose_stencils precomputes a single table mapping
control points straight to the final level, so each frame is one
StencilTable::interpolate call. Either path applies to any number of
data buffers (positions, UVs, …) that share the topology.
Implementing Interpolatable
Any type with a weighted add can be subdivided. The crate ships impls for
f32, f64, and [f32; N] / [f64; N]; for your own types:
use Interpolatable;
License
Licensed under any of
- Apache License, Version 2.0 (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)
- zlib License (LICENSE-ZLIB or https://opensource.org/licenses/Zlib)
- BSD 3-Clause License (LICENSE-BSD or https://opensource.org/licenses/BSD-3-Clause)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions.