Skip to main content

subdiv_kernels/
topology.rs

1//! Mesh topology types: the control cage [`Mesh`], refined [`Adjacency`], and
2//! face-varying channels.
3
4use crate::KernelError;
5
6/// The control mesh: faces, edges, and optional crease/corner sharpness — but
7/// **no positions**.
8///
9/// This is the input to [`Refiner`](crate::Refiner). Positions and any other
10/// per-vertex data are carried separately and applied with a
11/// [`StencilTable`](crate::StencilTable), so one refinement serves every
12/// attribute.
13#[derive(Debug, Clone, PartialEq)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15pub struct Mesh {
16    /// Number of vertices (needed since there is no positions array).
17    pub vertex_count: u32,
18
19    /// Number of corners for each face.
20    pub face_vertex_counts: Vec<u32>,
21
22    /// Flat face-vertex index list.
23    pub face_vertex_indices: Vec<u32>,
24
25    /// Canonical undirected edge endpoints.
26    pub edge_vertices: Vec<[u32; 2]>,
27
28    /// Per-edge crease values aligned with `edge_vertices`.
29    pub edge_creases: Vec<f32>,
30
31    /// Per-vertex corner values aligned by vertex index.
32    pub vertex_corners: Vec<f32>,
33}
34
35impl Mesh {
36    /// Validate basic array consistency.
37    pub fn validate(&self) -> Result<(), KernelError> {
38        let corner_count: usize = self.face_vertex_counts.iter().map(|v| *v as usize).sum();
39
40        (corner_count == self.face_vertex_indices.len())
41            .then_some(())
42            .ok_or(KernelError::InvalidTopology(
43                "face corner count does not match index buffer length",
44            ))?;
45
46        (self.edge_creases.len() == self.edge_vertices.len())
47            .then_some(())
48            .ok_or(KernelError::InvalidTopology(
49                "edge crease count does not match edge buffer length",
50            ))?;
51
52        (self.vertex_corners.len() == self.vertex_count as usize)
53            .then_some(())
54            .ok_or(KernelError::InvalidTopology(
55                "vertex corner count does not match vertex count",
56            ))?;
57
58        self.face_vertex_indices
59            .iter()
60            .all(|&idx| idx < self.vertex_count)
61            .then_some(())
62            .ok_or(KernelError::InvalidTopology("face index out of bounds"))?;
63
64        self.edge_vertices
65            .iter()
66            .flat_map(|e| e.iter())
67            .all(|&idx| idx < self.vertex_count)
68            .then_some(())
69            .ok_or(KernelError::InvalidTopology("edge endpoint out of bounds"))
70    }
71}
72
73/// Pre-built adjacency arrays in CSR format.
74///
75/// Produced by refinement and consumed by adapter-side mesh construction
76/// to avoid redundant topology analysis.
77#[derive(Debug, Clone, PartialEq)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
79#[non_exhaustive]
80pub struct Adjacency {
81    /// Per face-corner → edge index (same layout as `face_vertex_indices`).
82    pub face_edges: Vec<u32>,
83    /// Two incident faces per edge. `u32::MAX` = boundary.
84    pub edge_faces: Vec<[u32; 2]>,
85    /// CSR vertex → edge adjacency: per-vertex start offsets into `vertex_edges`.
86    pub vertex_edge_offsets: Vec<u32>,
87    /// Flattened incident-edge indices (sliced by `vertex_edge_offsets`).
88    pub vertex_edges: Vec<u32>,
89    /// CSR vertex → face adjacency: per-vertex start offsets into `vertex_faces`.
90    pub vertex_face_offsets: Vec<u32>,
91    /// Flattened incident-face indices (sliced by `vertex_face_offsets`).
92    pub vertex_faces: Vec<u32>,
93    /// Per-edge boundary flag.
94    pub edge_is_boundary: Vec<bool>,
95    /// Per-vertex boundary flag.
96    pub vertex_is_boundary: Vec<bool>,
97}
98
99/// Per-channel face-varying value-index topology.
100///
101/// At UV seams, face-corners sharing a geometric vertex can reference
102/// different FVar values. This struct holds the per-face-corner indices
103/// into the channel's own value array.
104#[derive(Debug, Clone, PartialEq)]
105#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
106pub struct FaceVaryingChannel {
107    /// Per face-corner index into the channel's value array.
108    /// Same length as `Mesh::face_vertex_indices`.
109    pub indices: Vec<u32>,
110
111    /// Number of distinct values in this channel.
112    pub value_count: u32,
113}