Skip to main content

Module stretch

Module stretch 

Source
Expand description

Adaptive splitting: triangles a deformation stretched split on their own, and nothing else changes.

capture_rest_sizes records each vertex’s rest size, the longest edge on it, in the REST_SIZE_CHANNEL channel, before the deformation starts. split_stretched then splits an edge only when it has grown past StretchOptions::limit times the smaller rest size of its two endpoints.

An edge’s rest length is at most either endpoint’s rest size, so an edge that did not stretch can never reach the limit, however long it is next to the rest of the mesh. A mesh with fine and coarse areas keeps both: each area is measured against itself, not against a mesh-wide mean.

§What changes

  • Existing vertices keep their ids and positions, so anything that stores vertex ids stays valid.
  • The triangles on a split edge are bisected. A triangle no split edge touches keeps its slot and its corners: the only unstretched triangles that change are neighbours of a stretched edge, to stay conforming.
  • Nothing collapses and nothing flips.

The longest edge splits first, which keeps the bisected triangles from degrading into slivers.

§Where new vertices go

Existing vertices cannot move, which rules out Loop’s edge rule: its 3/8 (a + b) + 1/8 (c + d) relies on the vertex rule moving a and b too. Alone it pulls a new vertex below the midpoint on a convex surface and measures no better than a midpoint against the true normal. A new vertex takes the middle of the cubic Hermite curve between the edge’s endpoints and their normals instead, which needs only the edge and is exact to second order on a smooth surface: (a + b) / 2 - (((b - a) . na) na + ((a - b) . nb) nb) / 8. An endpoint’s normal averages only the triangles around it that face like the edge’s own (within 45 degrees), so a flat face stays flat next to a hard edge. A gentler crease reads as a curve and rounds off: a flat face beside one bulges by up to about 5% of its edge.

An exact midpoint is used on a boundary or non-manifold edge, on a sharp edge (owners more than 45 degrees apart), and wherever the curved point would fold a child triangle. The boundary rule is what keeps a UV seam closed: its two sides are separate vertices at one position, and both must compute the same new point.

§Seams

Two vertices at exactly one position take the larger of their rest sizes, so both sides of a seam use one limit and split together. Boundary edges also split before any interior edge and are exempt from StretchOptions::max_splits, so a seam cannot open at the cap.

§Cost

One pass tests the vertices against the region and then touches only the edges around vertices inside it, using the mesh’s vertex index (Mesh::ensure_incidence). It never copies or sorts the whole mesh. Without a cached index (inside a transaction that has already changed topology, for example) it builds a private one first, which costs a pass over every triangle.

Structs§

StretchOptions
Options for split_stretched.
StretchReport
Outcome of split_stretched.

Constants§

DEFAULT_MAX_SPLITS
Default for StretchOptions::max_splits.
DEFAULT_STRETCH_LIMIT
Default for StretchOptions::limit.
REST_SIZE_CHANNEL
The source channel capture_rest_sizes writes: one value per vertex slot, interpolated for new vertices. Zero means no rest size is known.

Functions§

capture_rest_sizes
Records every vertex’s rest size for split_stretched: the longest edge on it, or on any vertex at exactly its position. Replaces an earlier capture, and builds the mesh’s vertex index if needed.
ensure_rest_sizes
capture_rest_sizes unless the mesh holds valid rest sizes. Returns whether it captured.
split_stretched
Splits the edges under options.region that have stretched past StretchOptions::limit, until none is left or StretchOptions::max_splits runs out. See the module docs for what changes and where new vertices go. New vertices interpolate source channels, so UVs follow. The plan is computed first and committed through tx at the end: either every split lands or nothing does.