🧲 Snap and Merge¶
Two separate steps that work together:
Snap is a one-shot alignment: it translates object A in world space so that its closest vertex lines up with the nearest vertex on object B, leaving just enough contact gap to avoid interpenetration. In the same pass it also captures stitch anchors for every A-vertex within reach of B, not just the single closest one.
Merge is a solver-side stitch constraint between two objects. At transfer time it becomes a cross-object constraint that keeps the two meshes joined during the simulation.
You typically snap and merge in sequence (the snap operator creates the merge pair automatically), but the two concepts are independent and can be used separately.
Important
Snap is a set-level operation, not a single-vertex operation. The feature is designed for meshes whose geometry already coincides (fully or partly) so that many A-vertices have a matching counterpart on B. A canonical example is duplicating a subdivided square patch and snapping the copy onto the original: every edge (or interior) vertex on the copy has a one-to-one counterpart on the original, so the whole set stitches in a single operation.
If only one isolated vertex on A happens to be close to B while the rest of the mesh is far away, only that one pair is recorded. The solver won’t magically stitch meshes whose topologies don’t align. Author the meshes so the intended stitch region coincides before snapping.
Snap records a stitch pair for every A-vertex already within B’s contact gap, and nothing else. Left: matching subdivided patches produce a full set of stitches in one pass. Right: a patch whose only one corner coincides with B yields a single stitch; the rest of the mesh stays unattached even after Merge. Author the meshes so the intended stitch region coincides before snapping.¶
Snap and Merge Panel¶
Open the sidebar (N) in the 3D viewport and switch to the add-on tab.
The Snap and Merge panel sits below Scene Configuration and
Dynamics Groups, and is collapsed by default. Click the header
to expand it.
When expanded, the panel contains a single box titled Snap To Nearby Vertices:
An Object A dropdown (the source; this is the object that will move) with an eyedropper button on the right for picking from the viewport.
An Object B dropdown (the target; this stays put) with its own eyedropper.
A Snap A to B button with a snap icon.
To snap two objects together, pick A and B with the dropdowns or eyedroppers, then press Snap A to B. The add-on finds the closest pair of vertices between A and B, translates A (in world space, parent-safe) so they line up, applies the contact-gap rules below, and records per-vertex barycentric anchor data for a later stitch. It also registers a merge pair automatically.
Snap and Merge panel with the two plane sheets below picked as A and B. PatchA is the one that will move (magenta); PatchB is the target that stays put (blue). Clicking Snap A to B runs the operator on whatever pair you’ve set here.¶
Before. Two identically-subdivided shell patches assigned to Shell groups: PatchB (blue, at the origin, stays put) and PatchA (magenta, shifted along the +X axis only; same Y, same Z). There is a clear gap along the shared edge; the two patches are not yet joined.¶
After Snap A to B. PatchA has translated along the X axis in
world space until its nearest vertex lands on PatchB. Because both
patches are Shell, the gap rule is “merge exactly”: PatchA’s
left edge now coincides with PatchB’s right edge and every seam
vertex is shared, so the two patches read as a single seamless 2x1
rectangle. The merge pair is registered automatically and the
per-vertex stitch anchors are captured behind the scenes.¶
As soon as at least one merge pair exists, a second box labeled Merge Pairs appears below the snap box, containing:
A UIList showing each pair, with both object names per row.
A Remove Merge Pair button below the list (disabled unless a row is selected).
A Stitch Stiffness slider, only shown when the selected pair involves a Solid. Sheet-sheet (Shell-Shell) and rod-rod pairs merge vertices exactly, so stiffness has no meaning for them.
A separate Visualization panel further down the sidebar exposes a Hide all snaps toggle that hides or shows the merge-pair / stitch overlay in the viewport.
Gap Rules¶
The solver needs a small separation between the two meshes at rest, otherwise contact barriers start flagging penetration on frame 1. The snap operator picks the gap based on the group types of A and B:
A type ↔ B type |
Applied gap |
|---|---|
Shell ↔ Shell |
No gap. Vertices merge exactly. |
Rod ↔ Rod |
No gap. Vertices merge exactly. |
Any other pair |
The larger of the two groups’ Contact Gap values plus both groups’ Contact Offset. |
Cross-Stitch Anchors¶
For every snap, the add-on also stores per-vertex barycentric anchor data on the resulting merge pair. Every A-vertex that ends up within a small reach-threshold of B after alignment becomes its own anchor, so a single snap typically produces many stitches, one per coincident vertex pair, not just one. Conceptually each source vertex is tied to a target triangle (or a single target vertex for rod pairs) with barycentric weights, so the stitch survives later mesh edits until the topology itself changes.
The reach-threshold is derived from the applied gap (roughly 2 × gap),
which is why coinciding geometries work best: vertices that are already
on top of each other are well within threshold, while distant vertices
are ignored. You can see the captured set as yellow dots connected by
thin yellow lines in the viewport overlay (toggle with Hide all
snaps in the Visualization panel).
After Snap A to B, every A-vertex within reach-threshold of B
becomes a stitch anchor. For this X-axis-only shift, the entire
shared seam qualifies, so the overlay draws one yellow stitch per
seam-vertex pair. PatchA has been pulled straight down in Z after
the snap to make the pairs visible; the stitches themselves remain
attached to the original coincident seam positions.¶
Merge Pairs Without Snapping¶
A merge pair alone (without snapping) registers two objects as stitched during the solve. Each pair has:
UI label |
Python / TOML key |
Description |
|---|---|---|
Object A / B |
|
The two mesh objects. |
Stitch Stiffness |
|
Per-pair stiffness. Only shown for pairs involving a Solid. Sheet-sheet (Shell-Shell) and rod-rod pairs merge vertices exactly, so stiffness has no meaning. |
Show Stitch |
|
Overlay toggle for the viewport stitch preview. |
Merge pairs referencing deleted or unassigned objects are cleaned up automatically on the next depsgraph update.
Blender Python API¶
The same workflow is available from Python:
from zozo_contact_solver import solver
# Make the shirt hug the mannequin at its closest pair of verts, with the
# right contact gap for the SHELL <-> STATIC pairing. Also registers a
# merge pair with cross-stitch anchor data.
solver.snap("Shirt", "Mannequin")
# Or register a merge pair without moving anything.
solver.add_merge_pair("Shirt", "Mannequin")
# Iterate existing pairs.
for a, b in solver.get_merge_pairs():
print(f"{a} <-> {b}")
# Remove a specific pair, or all of them.
solver.remove_merge_pair("Shirt", "Mannequin")
solver.clear_merge_pairs()
Note
Snapping and merging both reject library-linked (non-writable) objects because the solver needs to persist UUIDs on them. Make them local first if you hit that error.
Under the hood
What Snap does
Snap is a one-shot alignment:
Finds the closest pair of vertices between objects A and B.
Translates A in world space (parent-safe) along the approach direction so the two vertices line up, plus the gap-rule distance and a small float32 safety margin.
Records per-vertex barycentric anchor data for every A-vertex close enough to B to participate in a stitch.
For the no-gap pairings (Shell-Shell, Rod-Rod) the anchor-capture
threshold falls back to max(gap_a, gap_b) so nearby vertices still
enter the stitch even though the final separation is zero.
Cross-stitch anchor data
Each merge pair carries the captured anchor payload:
Source and target object UUIDs.
Per source vertex: target triangle indices and barycentric weights
[1.0, α, β, γ].Target positions at snap time.
Vertex counts at snap time, so stale entries can be detected if you edit the topology later.
When the pair is sent to the solver, the target vertex with the highest barycentric weight is picked per stitch. For rod-to-rod / rod-to-shell the target degenerates to a single vertex with weight 1.
Merge-pair encoding
Merge pairs are tracked by UUID, so renaming either object preserves the link. Pairs referencing an object that has never been snapped or merged (no UUID yet) are skipped at transfer. An empty cross-stitch payload means snap has not run or the pair is not eligible for a stitch.