Reading a second mesh by pin name: positions, normals, attributes, nearest points and ray hits. Every donor, including one named Reference, is read through the pin-qualified functions below - there is no bare, no-pin-name overload of any of them.
Part of the Wrangle reference. Everything on this page goes in the node's Code field, and runs once per element of the current Run Over mode.
| Syntax | What it does | Example |
|---|---|---|
npoints("Pin") |
Vertex count of the named mesh. | wmap("Weights") = npoints("Reference") > 0 |
pointpos("Pin", i) |
Position of vertex i on the named mesh, as a vector. | vector q = pointpos("Donor", ptnum) |
pointnormal("Pin", i) |
Mean split-normal of vertex i on the named mesh, as a vector. | N = pointnormal("Donor", ptnum) |
nprims() / nprims("Pin") |
Triangle count of the subject mesh, or of the named donor. Legal in every run-over. | wmap("Weights") = nprims() |
primpoints(t, k) / primpoints("Pin", t, k) |
Vertex id of corner k (0..2) of triangle t. Out-of-range returns -1 and is counted. Legal in every run-over. | float a = primpoints(primnum, 0) |
materialid() / materialid("Pin", t) |
Triangle MaterialID (0 if none). Read-only. Current-face form needs Triangle or Corner domain (in Corner, the owning triangle). | wmap("Weights") = materialid() |
numpolygrouplayers() / numpolygrouplayers("Pin") |
Total layer count, always >= 1 (layer 0 always counts, even with no groups enabled). Scalar, every run-over. | float n = numpolygrouplayers() |
hitprim("Pin", p) |
Triangle id of the nearest surface point on the named donor, or -1. | float t = hitprim("Donor", P) |
hitbary("Pin", p) |
Barycentric coords of that nearest hit, as a vector. | vector b = hitbary("Donor", P) |
primuv("Pin", p) |
PrimaryUV barycentric blend at the nearest hit, as vec3(u,v,0). | vector u = primuv("Donor", P) |
uvbary("Pin", u, v[, layer]) |
Barycentric coords of the uvhitprim() hit, as vec3 - zero vector on a miss. | vector b = uvbary("Donor", uv.x, uv.y) |
neardist("Pin", p) |
Distance from p to the nearest point on the named mesh's surface. Unsigned - it does not know inside from outside. | wmap("Weights") = 1 - saturate(neardist("Reference", P) / 25) |
nearpoint("Pin", p) |
The nearest point on the named mesh's surface, as a vector. | P = lerp(P, nearpoint("Reference", P), mask) |
sdist("Pin", p) |
Signed distance to the named mesh: negative inside, positive outside (unsigned via the same query as neardist, sign via a winding-number test). Costs more than neardist - only ask for it when the sign matters. | wmap("Weights") = sdist("Reference", P) < 0 |
rayhitpos("Pin", origin, dir) |
The first hit's position along the same watertight ray raydist() casts - origin itself on a miss (raydist's own "a miss is a defined sentinel" convention, not a counted guard). Zero-length dir is guarded the same way raydist's is. | P = rayhitpos("Donor", P, -N) |
rayhitprim("Pin", origin, dir) |
The first hit's triangle id along the same ray, or -1 for a miss - raydist()'s own -1-for-miss convention. | float t = rayhitprim("Donor", P, -N) |
primarea(t) / primarea("Pin", t) |
Triangle area in cm^2 of an explicit triangle id t, on the subject mesh or the named donor - unlike materialid()/polygroup() this never reads the current face, so (like primpoints(t, k)) it is legal in every run-over. Invalid t returns 0 and is counted. | float a = primarea("Donor", hitprim("Donor", P)) |
primnormal(t) / primnormal("Pin", t) |
Triangle normal of an explicit triangle id, as a vector. Same shape and guard as primarea. | N = primnormal("Donor", hitprim("Donor", P)) |
primcentroid(t) / primcentroid("Pin", t) |
Triangle centroid of an explicit triangle id, as a vector. Same shape and guard as primarea. | vector c = primcentroid(primnum) |
maxprims() / maxprims("Pin") |
The highest triangle id ever allocated (MaxTriangleID()) - the primpoints()/materialid()-style twin of maxpoints(). | for (t = 0; t < maxprims(); t += 1) { s += primarea(t) } |
Mesh parameter
Add a Parameter of type Mesh and it becomes an input pin, exactly like Float/Vector/Int/Bool ones - see the Parameters page for the shared naming rules (case sensitive, no colliding-with-a-language-name rule) and primary for the subject/Reference distinction. Unlike the value types it has no bare-read or ch()-family form: a Mesh parameter is only ever read through the functions below, by its pin name in quotes.
Parameters list, type Mesh, name "Donor"
Pin names - "Mesh" / "Reference" / "YourParam"
Every function below takes the source mesh's pin name as its first argument, a string literal in quotes. "Mesh" always resolves to the subject - your primary Mesh parameter (the node's own alias for it) - even when you named it something other than "Mesh"; reading it is safe even while this same expression is writing P/N/Cd, because donor reads only ever see data from before this run, same as pointpos()/pointnormal() above. "Reference" resolves to whichever Mesh parameter you named Reference (an ordinary declared parameter, no different from "Donor" - just a name every doc example and the pin-editor's Add Classic Mesh Setup button agree on). Naming a pin that does not exist, or a Mesh parameter that is not currently wired, is a compile error: No mesh input named X is connected.
wmap("Weights") = npoints("Mesh")
pointattr("Pin", "name", i)
A per-vertex value of the named mesh at vertex i: "P" (position), "N" (mean normal), "Cd" (colour rgb) and "uv"/"uv0"/"uvN" (a UV channel mean as vec3(u,v,0) - "uv" and "uv0" both mean channel 0, "uvN" (N>=1) means channel N; an out-of-range N is a compile error) are built in; any other name must be a tagged Float, Int, Vector 2 (as vec3 x,y,0), Vector 3 or Vector 4 (xyz only) vertex attribute on that mesh - triangle attributes are a compile error naming what the attribute really is (use surfattr() instead). An out-of-range i returns 0 (or the zero vector) and is counted like every other point-index guard in this language.
wmap("Weights") = pointattr("Donor", "thickness", ptnum)
polygroup() / primgroup() / pin forms
Layer-0 group id, or 0 if none. Layer 0 is the mesh's own polygroups - the layer GenerateCageMesh/Retopologize/SelectFromMesh actually write - not a polygroup attribute layer; those are numbered from 1 upward, see polygroup(t, layer) below. Same shape as materialid.
wmap("Weights") = polygroup()
polygroup(t, layer) / polygroup("Pin", t, layer)
Explicit triangle id and a runtime scalar layer: layer 0 is the mesh's own groups (same value as polygroup()); layer k>=1 reads the mesh's k-1'th polygroup attribute layer. Legal in every run-over, self or donor - unlike polygroup()'s current-face form. An out-of-range layer or triangle returns 0 and is counted.
float g = polygroup(primnum, 1)
uvhitprim("Pin", u, v[, layer])
The triangle containing UV position (u, v) in the named donor's UV channel (layer, a constant whole number, defaults to 0) - lowest triangle id on an overlap (two UV islands sharing the same UV space sample deterministically regardless of which was appended to the mesh first), or -1 for a miss. A containment test, unlike hitprim()'s nearest-3D-point search. A miss counts nothing, the raydist() convention. No implicit wrap or clamp - write frac(u) yourself for tiling.
float t = uvhitprim("Donor", uv.x, uv.y)
uvsample("Pin", "name", u, v[, layer])
A value sampled at UV position (u, v) in the named donor's UV channel (layer, a constant whole number, defaults to 0) - the UV-space twin of surfattr(), same samplable set ("P" position, "N" normal, "Cd" colour, or a tagged Float/Int/Vector 3 attribute; bare "uv" is refused, same reason as surfattr's) and the same Vertex-blend/Triangle-direct domain rule. Legal in every run-over - Texel is the headline use, since a texel could not otherwise read the mesh it is texturing. A miss returns 0 (or the zero vector) and counts one OutOfRangeIndex guard (unlike uvhitprim/uvbary, which count nothing for a miss).
Cd = uvsample("Donor", "Cd", uv.x, uv.y)
surfattr("Pin", "name", p)
A value sampled on the surface of the named mesh at the point nearest to p - the headline donor query. "P" is the nearest surface point itself; "N" and "Cd" are barycentric-blended across the hit triangle's three corners (N is re-normalised after blending); "uv0"/"uvN" is a UV channel barycentric blend as vec3(u,v,0) (bare "uv" is refused - circular, which channel would it even mean); any other name must be a tagged Float, Int or Vector 3 attribute - a vertex one is blended the same way, a triangle one is taken directly from the hit triangle, since a face's own attribute has no corners to blend across.
vector surf = surfattr("Donor", "P", P); float thick = surfattr("Donor", "thickness", P)
raydist("Pin", origin, dir)
Distance along dir from origin to the first hit on the named mesh's surface, or -1 for a miss. dir is normalised for you; a zero-length dir is guarded to a miss. Uses a watertight ray test, so a ray that visibly grazes a triangle edge still registers a hit - the naive nearest-hit query this avoids is known to miss those.
wmap("Weights") = raydist("Donor", P, -N)
nearestpoint("Pin", p)
The donor vertex id nearest p (an expanding search: starts at bbox/64, doubles until it finds one or has covered the donor's own bounding-box diagonal) - -1 when the donor has no vertices, counted. This is the id every per-vertex donor transfer needed and neardist()/nearpoint() (nearest surface point) could not supply: feed it into pointpos()/pointattr()/boneweight-by-index and so on.
float srcId = nearestpoint("Donor", P); Cd = pointattr("Donor", "Cd", srcId)
foreach (i in nearpoints("Pin", p, r)) { ... }
Iterate every donor vertex id within r of p, sorted ascending so the visiting order is the same on every run (the underlying spatial search is not itself ordered - the sort exists so you never have to think about that). Vertex, Corner or Detail run-over, gathered fresh once per loop entry, not re-queried per iteration.
foreach (i in nearpoints("Donor", P, 5)) { Cd += pointattr("Donor", "Cd", i) }
maxpoints() / maxpoints("Pin")
The highest vertex id the subject mesh (or the named donor) has ever allocated (the highest id ever handed out) - not npoints()'s live count. The two agree only on a mesh that has never had a vertex deleted; once one has, a raw-id accessor like pointattr()/primpoints() needs this to know how far ids can run, not the live count.
for (i = 0; i < maxpoints("Donor"); i += 1) { if (pointpos("Donor", i) != vec3(0,0,0)) { s += 1 } }