Vector math and Maya

Hello Tech Artists community. This is my 1st post here. I’ve found so many helpful posts on this site and want to thank this community for your help.

I have a Python / Maya / Vector question for the guys who know their linear algebra / matrices math out there.

When considering a 3d plane, all you need to define is a point on the plane and the normal. In Python, I have a simple class that defines a plane in 3d space.


class Vector(object):
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

class Plane(object):
    def __init__(self, point, normal):
        self.point = point
        self.normal = normal

    def createMesh(self):
        pass

p1 = Plane(Vector(0,0,0), Vector(0,1,0))
p2 = Plane(Vector(0,0,0), Vector(0.447, 0.894, 0))

When creating a poly plane in Maya, it’s starting position is (0,0,0) and it’s normal is (0,1,0), so in this instance, p1 represents the starting position of a polygon plane.

Now, lets say I want to create a poly plane for p2. I could create a plane for p1, but now I need to figure out the rotation values so that p1 rotates to p2. I want to write this into the createMesh def so I can create a polygon plane and move it to the position of the point and rotate it so it’s normal matches. I know I could break this into x, y, and z angles between and figure out the Pythagorean theorem, then use trig to figure out the angles per axis, but that is a lot of code, and I’m sure there’s a simpler way to do this with linear algebra.

I know this would probably be easiest to figure out with a matrix, however I’m one of those guys who is still trying to wrap his head around linear transformations with matrices. If you’re so inclined, I would LOVE an explanation of how to go about this with matrices.

Thanks guys!

tylerART

This is quite easy with a matrix.

The main rows in a maya matrix are the x, y, and z axes of the local coord system. In an identity matrix they line up with world X, world y, and world Z:


x y z  w
1 0 0 0  local x
0 1 0 0  local y
0 0 1 0  local z
0 0 0 1  translation

in your case you want your second plane to have a normal that doesn’t line up with world Y, but with the vector (.488, .894,0). There tricky bit is to make sure that your other two vectors are still at right angles to the normal vector. I’m going to cheat and simplify the problem by asking for two vectors instead of one: the ‘up’ vector and the ‘side’ vector:


from pymel.core.datatypes import Vector, Matrix, Point
def matrix_from_normal(up_vect, front_vect):
    # normalize first!
    up_vect.normalize()
    front_vect.normalize()

    #get the third axis with the cross vector    
    side_vect = Vector.cross(up_vect, front_vect)
    #recross in case up and front were not originally orthoganl:
    front_vect = Vector.cross(side_vect, up_vect )
    
    #the new matrix is 
    return Matrix (
        side_vect.x, side_vect.y, side_vect.z, 0,
        up_vect.x, up_vect.y, up_vect.z, 0,
        front_vect.x, front_vect.y, front_vect.z, 0,
        0,0,0,1)     


Any point at Y=0 in this coord system is on the plane, so it’s easy to glue things to the plane by setting their ‘plane space’ y to 0. The meaning of X and Z is dictated by your choice of the side vector (which would have to be true of any system: there are an infinite number of matrices with a given up vector). You can manipulate the position of the plane by setting items [12, 13, 14] of the matrix - the lower left corner - which is the translation.

To go from a point in this matrix’s space to the world, multiply the point by the matrix :


# matrix at 45, 0 , 45 degrees...
q = matrix_from_normal ( Vector(1,1,0), Vector(0,0,1))
z = Point( 1, 0, 5)
print z * q
// [0.707106781187, -0.707106781187, 5.0]

to go from world space to the plane, you multiply by the inverse of the matrix:


p = Point( 10, 0, 0)
print  p * q.inverse()
// [7.07106781187, 7.07106781187, 0.0]
# setting the y of the result to 0 would be projecting the point onto your plane

z = Point( 0, 0, 0)
print  z * q.inverse()
// [0.0, 0.0, 0.0]
# since y = 0, this point lies on the plane....

 

It’s helpful to think of the matrix as if it were a maya scene node with a given position, rotation and scale. Multiplying by the matrix goes from the space of the matrix to the the world, and multiplying by the inverse goes from world to local space.

That extra column labeled ‘w’ is the ‘Homogenous coordinate’ . It basically tells you if the row is a point or a vector; the first three rows are vectors, so they have a W of 0, while the tranlation is a point so it has a W of 1. When you multiply a vector by a matrix, you get a vector back, so you’ll get correct angles but no translation. If you multiply a point by a matrix you get the translation too…

1 Like

This is very helpful, thank you!

How would I go about getting the rotate values?

[QUOTE=tylerART;23091]This is very helpful, thank you!

How would I go about getting the rotate values?[/QUOTE]

There’s the TransformationMatrix if you want to use an already existing datatype and as you can see it has getRotation(), getTranslation(), getScale() methods. If you want to know how it’s done behind the scenes look at the source code. But if you don’t want to reinvent the wheel, just use the existing datatypes that comes with pymel or OpenMaya.

Don’t overlook what blaisebundle said, though his exact example requires that you import PyMel which some people don’t want to do for a number of reasons, not the least of which is the nasty import time. The built in math functions are going to be much faster than anything you can do in Python code unless you imported a compiled math module (IE opencv, gameobjects, etc.). Take a look at http://download.autodesk.com/us/maya/2010help/API/group___open_maya.html and specifically the matrix and vector stuff Maya API: MTransformationMatrix Class Reference good reference around this stuff can be hard to find. Here’s one example: Maya Python API Scaling a matrix about another matrix - Stack Overflow

Thank you all for your comments, in the script I’m using, I can’t use pymel due to the speed slowdown, so I typically use the OpenMaya vector classes, however I’ve written some vector math scripts outside of Maya, so I wrote my own vector class and typically use that. For my purposes this all works, but I would like to understand the math behind getting rotations from vectors. Agreed that I should be using a compiled lib for this, but understanding it all is my 1st step.

tylerART

Some basic things to exploit:

  1. the dot product of two normalized vectors is the COSINE of the angle between them; arccos(dot(a,b)) would give you the angle (in radians)
  2. the cross product of two normalized vectors is the normal of the plane defined by the two vectors.

so, the “rotation between the two” is an angle -axis rotation of arccos(dot(a,b)) around (cross(a,b)).

A different way to express the same idea is to use the matrix-from-vectors technique above, and create a matrix for each vector (you’d want to remove the translation from these matrices for simplicity). The rotation from a to b is encoded in the matrix you get from a.inverse() * b and vice-versa. As in the earlier case, the extra orientations in that case are arbitrary (this is sort of like picking a world up vector for a lookat constraint or an IK twist controller).

1 Like