For more elegance, have you considered storing the local matrix, instead of
the world? Or better yet, store the translate/rotate values? Is it not safe
to assume that wherever you export these from will have a similar - if not
identical - hierarchy?

@marcus, you will have to share your secret :)

Hehehehe, sure. It’s called MarkdownHere <https://markdown-here.com/>

On Mon, 15 Feb 2021 at 14:33, Eric Bates <backwheelba...@gmail.com> wrote:

> My code formatting didnt quite work, @marcus, you will have to share your
> secret :)
>
> On Monday, February 15, 2021 at 3:31:20 PM UTC+1 Eric Bates wrote:
>
>> Thanks so much Marcus for the really thoughtful and complete reply!
>>
>> I managed to get it working through a combination of approaches.
>>
>> I adapted your code Marcus to my script, and it nearly worked! There were
>> a few controls that weren't hierarchically logical, meaning that some of
>> the controls were structured through groups that were constrained. So there
>> were some parallel hierarchy structures, that were actually functionally
>> hierarchical (through constraints). So list relatives didn't give enough
>> information here.
>>
>> For the most part on this particular rig, "allDescendents" order was
>> pretty close.
>> [image: Screenshot 2021-02-15 140655.png]
>>
>> So I tried a brute force approach using the allDescendents order as a
>> starting place and this worked. But I can't help but think there is a more
>> elegant solution!
>> [image: Screenshot 2021-02-15 135833.png]
>>
>>
>>
>> from maya import cmdsfrom maya.api import OpenMaya as om
>>
>> from maya import OpenMaya as om1import math  # degreesimport copy
>>
>>
>> # Make something to export and import, you'll already have these
>> shoulder, _ = cmds.polyCube()
>> shoulder = cmds.rename(shoulder, "shoulder")
>> cmds.move(1, 3, 6)
>> cmds.rotate(15)
>> pinky, _ = cmds.polyCube()
>> pinky = cmds.rename(pinky, "pinky")
>> cmds.parent(pinky, shoulder)
>> cmds.move(0, 5, 0)
>> cmds.rotate(45, 20)
>> # Next, serialise them. You'll have these too
>> matrices = {
>>     "pinky": cmds.getAttr("pinky.worldMatrix[0]"),
>>     "shoulder": cmds.getAttr("shoulder.worldMatrix[0]"),
>> }
>> # Now, anything below will restore the above to their original world matrix 
>> transformations
>>
>> selection = pinky
>> full_path = cmds.listRelatives(selection, fullPath=True)
>> selected_root = filter(None, full_path[0].split('|'))[0] # get parent node 
>> from full path
>>
>>
>> hierarchy = cmds.listRelatives(selected_root, allDescendents=True)
>> hierarchy += [selected_root] # This one isn't included in the above
>> # allDescendents returns not only transforms, but shapes too. We don't need 
>> those
>> hierarchy = cmds.ls(hierarchy, type="transform")
>> # allDescendents returns the order reversed
>> hierarchy.reverse()
>>
>>
>>
>>
>>
>> def is_object_matching_matrix(control, target_matrix_list):
>>     #
>>     current_matrix_list = cmds.xform(control, worldSpace=True, query=True, 
>> matrix=True)
>>     current_mMatrix = om1.MMatrix()
>>     om1.MScriptUtil.createMatrixFromList(current_matrix_list, 
>> current_mMatrix)
>>
>>     # comparison matrix
>>     target_mMatrix = om1.MMatrix()
>>     om1.MScriptUtil.createMatrixFromList(target_matrix_list, target_mMatrix)
>>
>>     # get diff and decompose
>>     diff_matrix = target_mMatrix * current_mMatrix.inverse()
>>     diff_MTMatrix = om1.MTransformationMatrix(diff_matrix)
>>     translate = diff_MTMatrix.translation(om.MSpace.kTransform)
>>     rotate = diff_MTMatrix.rotation()
>>     #scale = diff_MTMatrix.scale(om.MSpace.kTransform)
>>
>>
>>
>>     # API rotations are in radians
>>     rotate = [math.degrees(r) for r in
>>
>>  rotate]
>>
>>     # sum errors
>>     translation_error = sum(translate)
>>     rotation_error = sum(rotate)
>>
>>     # check error
>>     threshold_pos = .0001
>>     threshold_rot = .001
>>     if translation_error < threshold_pos or rotation_error < rotation_error:
>>         return True
>>     else:
>>         return False
>> # brute force
>> max_iterations = len(matrices)
>> previous_match_count = max_iterations
>> iteration_count = 0
>> hierarchy_copy = copy.deepcopy(hierarchy)
>> for i in range(max_iterations):
>>     # track iterations
>>     iteration_count += 1
>>
>>     # initial matching
>>     for control in hierarchy_copy:
>>         matrix = matrices.get(control)
>>
>>
>>
>> # If there's something in the hierarchy not present in your list of 
>> matrices, that's fine
>>         if not
>>
>>  matrix:
>>             # do come cleanup along the way
>>             #hierarchy_exec.remove(control)
>>             continue
>>
>>         # apply matrix directly
>>         print('applying matrix to ',control )
>>         cmds.xform(control, worldSpace=True, matrix=matrix)
>>
>>     # check that the matching was successful
>>     for control in list(hierarchy_copy): # use a copy of list
>>
>>
>>         matrix = matrices.get(control)
>>
>>         # If there's something in the hierarchy not present in your list of 
>> matrices, that's fine
>>         if not matrix:
>>           continue
>>
>>         # check that the match is correct
>>         is_matching = is_object_matching_matrix(control, matrix)
>>         if is_matching:
>>             # remove if all good
>>             hierarchy_copy.remove(control)
>>
>>     # if not getting any better, hit the TV with a broom to see if it works
>>     if not len(hierarchy) < previous_match_count:
>>         hierarchy_copy.reverse()
>>
>>     # all matches correct, done
>>     if not len(hierarchy_copy):
>>         break
>>
>>     # keep track of matching improvements per iteration
>>     previous_match_count = len(hierarchy_copy)
>> print('Matching Positions in {0} iterations'.format(iteration_count))
>>
>>
>> On Mon, Feb 15, 2021 at 1:26 PM vince touache <fruit...@gmail.com> wrote:
>>
>>> you can get rid of all the matrix part by setting directly the matrix in
>>> world space, using
>>> cmds.xform(obj, matrix=your_mat, worldSpace=True)
>>>
>>>
>>> Le lundi 15 février 2021 à 02:42:49 UTC-5, Marcus Ottosson a écrit :
>>>
>>>> If I understand you correctly:
>>>>
>>>>    1. You’ve got a dictionary of worldMatrices associated to names of
>>>>    controls
>>>>    2. They are unordered, e.g. could be pinky and then shoulder.
>>>>    3. You’d like these assigned to their associated control
>>>>
>>>> If so, then what you’re looking for isn’t evaluation order per-se, but
>>>> hierarchical order. By assigning to shoulder first, you change where pinky
>>>> is. So what you need to do is assign your worldMatrices in the order they
>>>> are parented.
>>>>
>>>> from maya import cmdsfrom maya.api import OpenMaya as omimport math  # 
>>>> degrees
>>>> # Make something to export and import, you'll already have these
>>>> shoulder, _ = cmds.polyCube()
>>>> shoulder = cmds.rename(shoulder, "shoulder")
>>>> cmds.move(1, 3, 6)
>>>> cmds.rotate(15)
>>>> pinky, _ = cmds.polyCube()
>>>> pinky = cmds.rename(pinky, "pinky")
>>>> cmds.parent(pinky, shoulder)
>>>> cmds.move(0, 5, 0)
>>>> cmds.rotate(45, 20)
>>>> # Next, serialise them. You'll have these too
>>>> matrices = {
>>>>     "pinky": cmds.getAttr("pinky.worldMatrix[0]"),
>>>>     "shoulder": cmds.getAttr("shoulder.worldMatrix[0]"),
>>>> }
>>>> # Now, anything below will restore the above to their original world 
>>>> matrix transformations
>>>> selected_root = cmds.ls(selection=True)[0]
>>>> hierarchy = cmds.listRelatives(selected_root, allDescendents=True)
>>>> hierarchy += [selected_root] # This one isn't included in the above
>>>> # allDescendents returns not only transforms, but shapes too. We don't 
>>>> need those
>>>> hierarchy = cmds.ls(hierarchy, type="transform")
>>>> # allDescendents returns the order reversed
>>>> hierarchy.reverse()
>>>> for control in hierarchy:
>>>>     matrix = matrices.get(control)
>>>>
>>>>     # If there's something in the hierarchy not present in your list of 
>>>> matrices, that's fine
>>>>     if not matrix:
>>>>       continue
>>>>
>>>>     # A world matrix can't be applied directly, because every control is 
>>>> relative their parent
>>>>     # So we'll convert world to local by multiplying it with the parent 
>>>> inverse matrix
>>>>     parent_inverse_matrix = cmds.getAttr(control + 
>>>> ".parentInverseMatrix[0]")
>>>>
>>>>     # To multiply, we need to call on the API
>>>>     matrix = om.MMatrix(matrix)
>>>>     parent_inverse_matrix = om.MMatrix(parent_inverse_matrix)
>>>>     local_matrix = matrix * parent_inverse_matrix
>>>>
>>>>     # Next we need translate/rotate/scale from this matrix
>>>>     local_matrix = om.MTransformationMatrix(local_matrix)
>>>>     translate = local_matrix.translation(om.MSpace.kTransform)
>>>>     rotate = local_matrix.rotation()
>>>>     scale = local_matrix.scale(om.MSpace.kTransform)
>>>>
>>>>     # API rotations are in radians
>>>>     rotate = [math.degrees(r) for r in rotate]
>>>>
>>>>     # Now we're got the local values ready to apply
>>>>     cmds.setAttr(control + ".translate", *translate)
>>>>     cmds.setAttr(control + ".rotate", *rotate)
>>>>     cmds.setAttr(control + ".scale", *scale)
>>>>
>>>> [image: applymatrices]
>>>>
>>>>
>>>> On Sun, 14 Feb 2021 at 22:50, Eric Bates <backwhe...@gmail.com> wrote:
>>>>
>>>>> Hi all,
>>>>>
>>>>> Really great group you guys have here! Some awesome stuff :)
>>>>>
>>>>> I'm looking for some advice about a posing script I'm working on.
>>>>> Thanks for any ideas!
>>>>>
>>>>> I have world space matrices that represent control positions on a rig,
>>>>> lots of them. My goal is to snap all of the rig controls to these points.
>>>>>
>>>>> Unfortunately, since I don't know the evaluation order ahead of time,
>>>>> when the positions are aligned out of order, things get ugly!
>>>>>
>>>>> This terrifying pose should look like a hand, the locators above show
>>>>> the "correct" points.
>>>>>
>>>>> [image: Screenshot 2021-02-14 234510.png]
>>>>> I was thinking that if there was a way to lookup the evaluation order
>>>>> of nodes, that took into account hierarchy, constraints, etc, it would
>>>>> solve the problem. But so far, I havent had much luck.
>>>>>
>>>>> Of if there is another approach out there, Id love to hear.
>>>>>
>>>>> Thanks for your thoughts everyone!
>>>>>
>>>>> Eric
>>>>>
>>>>> --
>>>>> You received this message because you are subscribed to the Google
>>>>> Groups "Python Programming for Autodesk Maya" group.
>>>>> To unsubscribe from this group and stop receiving emails from it, send
>>>>> an email to python_inside_m...@googlegroups.com.
>>>>> To view this discussion on the web visit
>>>>> https://groups.google.com/d/msgid/python_inside_maya/b8af2735-f81a-4991-af84-8b12448b06d8n%40googlegroups.com
>>>>> <https://groups.google.com/d/msgid/python_inside_maya/b8af2735-f81a-4991-af84-8b12448b06d8n%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>> .
>>>>>
>>>> --
>>>
>> You received this message because you are subscribed to a topic in the
>>> Google Groups "Python Programming for Autodesk Maya" group.
>>> To unsubscribe from this topic, visit
>>> https://groups.google.com/d/topic/python_inside_maya/7gqUkM2-IT8/unsubscribe
>>> .
>>> To unsubscribe from this group and all its topics, send an email to
>>> python_inside_m...@googlegroups.com.
>>> To view this discussion on the web visit
>>> https://groups.google.com/d/msgid/python_inside_maya/75933eb6-6bc5-4511-a09d-fe0e84fbfac0n%40googlegroups.com
>>> <https://groups.google.com/d/msgid/python_inside_maya/75933eb6-6bc5-4511-a09d-fe0e84fbfac0n%40googlegroups.com?utm_medium=email&utm_source=footer>
>>> .
>>>
>> --
> You received this message because you are subscribed to the Google Groups
> "Python Programming for Autodesk Maya" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to python_inside_maya+unsubscr...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/python_inside_maya/b53df90d-ee5c-4c30-9758-cde4f7e3d72cn%40googlegroups.com
> <https://groups.google.com/d/msgid/python_inside_maya/b53df90d-ee5c-4c30-9758-cde4f7e3d72cn%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
>

-- 
You received this message because you are subscribed to the Google Groups 
"Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to python_inside_maya+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/python_inside_maya/CAFRtmODK3widYoLJn3W_bDG6nS%3DefDX--5UydB%2BHTvZxq0WF%3DQ%40mail.gmail.com.

Reply via email to