xform matching

Become a member of the CGSociety

Connect, Share, and Learn with our Large Growing CG Art Community. It's Free!

Thread Tools Search this Thread Display Modes
Old 02 February 2012   #1
xform matching not like parent constraint

I have an A to B match script that uses xform to query the world space rotate pivot and world space rotation of object B and applies the translation and rotation values to object A. This fails when object A has a different rotate order than object B (just the rotation of course). If I use a parent constraint without offset, the position can be matched perfectly regardless of the rotation orders. I don't want to use the parent constraint method of matching because it will break any preexisting parent constraints on object A. I was wondering what was happening behind the scenes with a parent constraint and if it can be replicated without actually using one.


Last edited by gonzalimator : 02 February 2012 at 09:09 AM. Reason: changed title
Old 02 February 2012   #2
the constraint node has a hidden attribute for each target called targetRotateOrder as well as an attribute called constraintRotateOrder, which you can see in the AE.
I suppose what the node does is convert euler angles from the input targetRotateOrder into the constrained object's rotateOrder before plugging rotation values.

Here's a plugin that does some conversion math, http://morganloomis.com/wiki/tools....rtRotationOrder. Googling the topic gives lots of results too (but I suck at algebra/matrices and red mist shortly covers my sight when I read those posts too long)
Old 02 February 2012   #3
Are you trying to snap an object that is already parent constrained? It'll snap back to its space position after you move it.

Old 02 February 2012   #4
You know that you can set keys on constrained objects, right? A blend channel appears on the object when it's keyed that allows you to choose when the object is constrained or not. There's also weight channels of the constraint's shape node that can also be keyed to control whether the constraint is active or not. So a constrained object won't always snap back to it's constrained position. Happens all the time.

Thank you for the parent constraint information. I will investigate further to see if this can help me achieve a clean snap script. By the way, I've actually written my own script to change rotation orders while preserving animation, but thanks so much for the link. Mine uses constraints and temporary groups to achieve it, but I'd like to see how others have done it, especially if it's mathematical.

For my question, I guess I can try using a temporary transform that gets its rotation order set the same as object A, then gets constrained to object B, then object A can xform to it, then it gets deleted. Seems messy, but that's similar to my rotation order script. Thanks for inspiring a solution!
Old 02 February 2012   #5
Actually your last suggestion is what I was going to suggest myself after I posted my original post.

Simply group the object first and constrain the group node via parent constraint, then ungroup the object and delete the group node (group node = transform node).

Doesn't seem like too much of a hassle to be honest.
Old 02 February 2012   #6
Couldn't that get sketchy if the object you're trying to move is buried in some rig's hierarchy? Also, I don't think you can group children of references if you use a referencing pipeline. That's why I kept it outside any hierarchy. Here it is if anyone wants it:
// mgMatcher.mel
// Author:
// Marcos Gonzalez
// http://gonzalimation.com
// Description:
// Moves object A to match the position and rotation of object B.
// Uses xform, won't be affected by different rotation orders.
// Usage:
// Source the script and run the command mgMatcher .

global proc mgMatcher () {
	// get selected objects and perform error check
	string $sel[] = `ls -sl`;
	if (size($sel)!=2){
		error "Select two objects. \n";

	string $A = $sel[0];
	string $B = $sel[1];
	// get object A's rotate order
	int $ro = `getAttr ($A+"\.rotateOrder")`;

	// create temp group to help with the match
	string $temp = `group -em`;

	// set the rotate order of the temp group to match object A
	setAttr ($temp+"\.rotateOrder") $ro;
	// constrain temp group to object B
	parentConstraint $B $temp;

	// get the position of position and rotation of temp group
	float $pos[] = `xform -q -ws -rp $temp`;
	float $rot[] = `xform -q -ws -ro $temp`;
	// move and rotate object A to temp group position/rotation
	move -rpr $pos[0] $pos[1] $pos[2] $A;
	rotate -a -ws $rot[0] $rot[1] $rot[2] $A;
	// delete temp group
	delete $temp;
	// select object A (to be nice)
	select  $A;
	// print message
	print "// mgMatcher \- match completed.\n";

Thanks again Nyro! It really takes sharing questions to hear about all of the potential solutions and I always appreciate learning new things.

Last edited by gonzalimator : 02 February 2012 at 06:17 PM.
Old 02 February 2012   #7
You're right, good thinking. References are a special case that are hard to deal with.

On a side note: while it may look more "elegant" to figure out a purely mathematical solution to the problem, using "hacks" with temporary nodes and whatnot is absolutely legit. The user won't care as long as it works.
Old 10 October 2012   #8
use xform matrix!

Wow, I just read a post on this guy's blog that showed me the answer (Check out the excellent comments on that blog post as well!). Instead of querying the world space pivot and rotation, use the object's matrix instead. It doesn't matter if you have different rotation orders on the objects or their local spaces are wacky (like mirrored controls using the parent transform scaleX -1 technique) It always works. Even a match via a parentConstraint without offset fails when trying to match to an object that is mirrored using the technique mentioned earlier. Xform matrix, why did I not know about you?! Check it out:

     # mgMatcher NEW AND IMPROVED - now with xform matrix goodness!!  
        import maya.cmds as cmds
           def mgMatcher():
                 # get selection
                  sel = cmds.ls( selection=True )
                  if len( sel ) != 2:
                           cmds.error( 'Please select two objects' )
                 A = sel[0]
                  B = sel[1]
                 # get matrix of B and apply it to A
                  Bmatrix = cmds.xform( B, query=True, worldSpace=True, matrix=True )
                  cmds.xform( A, worldSpace=True, matrix=Bmatrix )
                 # select A to be nice
                  cmds.select( A )

If I'm mistaken about how reliable this appears to be, please tell me. I'm about to rewrite all of my scripts! Hope this enlightens someone.
Old 10 October 2012   #9
Hello Marcos,

From my comment on chris blog you can query matrix information directly as this is a standard attribute of every dagNode in maya.

Your xform command is doing it for you but its easier with:
matrixValue = cmds.getAttr(‘%s.worldMatrix[0]’%jointToBake).

I write nodes for this kind of stuff, but I often prototype things with regular maya matrix node(multMatrix, fourbyfour, decomposeMatrix ). Or i can also quickly slap a python script that use the API MPoint and MMatrix.
Old 10 October 2012   #10
so, you are setting two objects to the same transform?

you can just parent A to B, zero the transforms of A and unparent it!
Old 10 October 2012   #11
@addster: parenting and zeroing channel seems like a obvious solution but what if you have input connection or cant change hierarchy?( or what will happen with 7000 frame and 25 character with 180 bones that needs to be bake down?)

I was talking about a robust way of doing this ,that can be used to bake transform on time range, or even doing space switching.
Old 10 October 2012   #12
Thanks for chiming in Cedric! I really liked your facial rigging post on your blog, great stuff.

I've always found the perfect matching tool to be elusive. I always find a situation where my tools have failed. In this case, I just discovered that the xform matrix method breaks joint orientation. Usually, the FK controls on our rigs are actually joints with nurbs curve shapes parented to them, so any matching I do with those will break the rig. Argh!

I wrote an animation recorder tool that I had to jump through hoops to get to work in all situations. The tool can be used as means for non destructive space switching (among other uses), you record an object's animation, change the parent space, and re-apply the recorded animation. It's gets ridiculously slow with mocap, so I was hoping that this matrix method would speed things up a bit.

I'm not sure how to apply the queried matrix. I get that I can store the matrix with getAttr at a defined time, but how can I apply it to another object at a specific time? What attr do I set with the matrix? Since I can't xform at a specified time, what can I do? Do I absolutely need to use the DecomposeMatrix node to get translate and rotate values that can be used to set object values at a specific time? I want to avoid advancing the time slider so that Maya isn't wasting time drawing the baking process. (getAttr at a frame, set values/setKeyframe on intermediate node at that frame, repeat. No time slider change.) Any insight would be greatly appreciated.
Old 10 October 2012   #13
Hello Marcos,
The decomposeMatrix is just another way to bind object :you can do the same stuff with a regular parent constraint.( if you dont want to use the API it can extract transform channel for you )

The idea was to recycle maya good old bakeResults command : it can process time range, attributes list and object list out of the box.

Also did a crazy node once(in python) (for import/export between max and maya ) which from a world matrix inputArray attribute will build one "transform pyramid" per input and output one merge mesh: in the and you export one Obj and regular pc2 files.

if you want to set keyframe manually you can look at this code
import maya.cmds as cmds
     import maya.OpenMaya as OpenMaya
     #outside loop variable
     util = OpenMaya.MScriptUtil()
     WorldTM = OpenMaya.MMatrix()
     INVMAT = OpenMaya.MMatrix()
     matFn = OpenMaya.MTransformationMatrix()
     worldState = cmds.getAttr('%s.worldMatrix[0]'%holderNode,t=requestTime )
     inverseParentState = cmds.getAttr('%s.parentInverseMatrix'%drivenNode,t  =requestTime  )
     util.createMatrixFromList(inverseParentState,INVMA  T)
     defaultRot = matFn.eulerRotation() #--> in radians and the rotation order is XYZ so dont forget to change it ...
     xValue = math.degrees(defaultRot.x)
     defaultTR = matFn.getTranslation(OpenMaya.MSpace.kObject)
     # set your keyframe and repeat for the next object
    cmds.setKeyframe(drivenNode, attribute='translateX',v=defaultTR.x, t=[requestTime] )

I havent check it for error( doing it only from the online documentation ) but you get the idea.
If you go the API way MFnTransform::set ( const MTransformationMatrix & transform ) can use an input matrix and apply it to your object ( beware as out of the box there is no undo with this )

Last edited by tontonsuspect : 10 October 2012 at 11:03 AM.
Old 02 February 2013   #14
Hi gonzalimator,

For the most part, that code was working really well for querying and applying a matrix to snap positions.

But for some weird reason, I really don't know why, it wouldn't work on a few controls on a rig I was using. Orientation, and scale transferred ok, although on a few stubborn controllers it wasn't setting the translation correctly. Could be because frozen transforms, or flipped parents (negatively scaled), I couldn't see why some worked, and some didnt.

note: I also tried using xform to query rotations as well (and not using a matrix), but this failed when rotations orders weren't the same, or if a control was flipped (negative scale value)

Anyways, I added a second level of translation snapping that fixed my problem. Wish there was a more elegant solution than this, but at least its working.

If anyone has any better ideas, Im all ears.

 def matchAtoB(objectA, objectB):
 	#dont know why this is so painfully complex
 	#in some rare cases, this is the only thing that works
 	#do this first, takes care of matching the correct scale, orientation
 	matrix = cmds.xform(objectB, query=True, ws=True, m=True)
 	cmds.xform(objectA, ws=True, m=matrix)
 	#fix the translations, this seems to work for objects with frozen transforms
 	pos = cmds.xform(objectB, query=True, ws=True, rp=True)
 	cmds.move(pos[0], pos[1], pos[2], objectA, a=True, rpr=True)
Old 02 February 2013   #15

Just found another interesting method for object position/orientation snapping. This seems to work in all situations Ive tested, even in the few cases that the above scripts were failing.

I'm curious if it works for other people as well.
Thread Closed share thread

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Society of Digital Artists

Powered by vBulletin
Copyright ©2000 - 2006,
Jelsoft Enterprises Ltd.
Minimize Ads
Forum Jump

All times are GMT. The time now is 09:13 PM.

Powered by vBulletin
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.