Orienting a transform to a face with OpenMaya

Hello!

I feel like this should be simple and I’m struggling a little. I’m trying to write something that will orient a transform to a face. Ie. It’s normal. I’m currently using a closestPointOnPoly node to get the a position on a face and it’s normal. Obviously moving a transform to a position is trivial but I dunno how to use that normal vector to turn that into a euler angle / matrix to set on the transform. I think you’d need another vector to derive a rotation?

Honestly I thought it’d be easy to say “point object Z+ towards this vector and we can ignore the twist around the axis for now” (although if there is a clever way to get the twist too that’d be great).

I’m familiar with cmds but I’ve only used OpenMaya a little.

Cheers

something like this:

import maya.api.OpenMaya as om2

shapename = "pSphereShape1"
faceid = 278
dp = om2.MSelectionList().add(shapename).getDagPath(0)

verts = om2.MFnMesh(dp).getPolygonVertices(faceid)
normal = om2.MFnMesh(dp).getPolygonNormal(faceid, space=om2.MSpace.kWorld)

pos = om2.MVector()
for vert in verts:
    pos += om2.MVector(om2.MFnMesh(dp).getPoint(vert, space=om2.MSpace.kWorld))
    
pos /= len(verts)

my = om2.MVector(0,1,0)
mx = my ^ normal
my = normal ^ mx

m = list(mx) + [0] + list(my) + [0] + list(normal) + [0] + list(pos) + [1]

loc = cmds.spaceLocator()[0]
cmds.xform(loc, m=m, a=1, ws=1)

the transform up is aligned to the world, if you wanted to get fancy you could figure out which 2 verts are the “upmost” and get the vector from the face center to the center of those verts.

and if the transform you’re aligning isn’t in world space, multiply the matrix by the parent’s inverse:

locparent = cmds.listRelatives(loc, p=1)[0]
locparent_mat = cmds.xform(locparent, q=1, m=1, a=1, ws=1)
local_m = om2.MMatrix(locparent_mat).inverse() * om2.MMatrix(m)
cmds.xform(loc, m=m, a=1, ws=1)

rich

2 Likes

A transform is basically 3 orthonormal vectors (unit vectors - length of 1.0, perpendicular to each other - 90.0 degrees) and a forth offset vector.

The first 3 define its rotation the last defines its offset from a parent space i. position. The length of each of the first 3 define its scale in each axis. If the vectors are not perpendicular to each other you get a shear form.

With Maya you have a right-handed matrix which you need to incorporate else you’ll potentially get negative scales.

To fit an object to a face, get the faces vectors (a position of each face vertex) for example a, b, c.

Now you derive the first orthonormal vector of your transform.

X = (b-a).normal()

The second basis vector is a cross-product ‘^’ of the first and a derived normal from our a, b, c inputs.

Y = X ^ (c-a).normal()

The third basis vector is a cross of the first two.

Z = X ^ Y

Order matters here so you may need to do Y ^ X.

Finally the offset can just be the average if the input vectors:

W = (a + b + c) / 3

Your transform now is:

X Y Z W

This is not a square matrix though its 4 x 3. And cant be used as a transform until its square 4 x 4. Which will look like this:

1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1

Or:

x0 x1 x2 0
y0 y1 y2 0
z0 z1 z2 0
w0 w1 w2 1

You can build and mutate vectors/matrices/transforms with maya.api.openMaya (api 2.0)

MVector
MMatrix
MTransformationMatrix