Mirroring Posses

   3623   14   5
User Avatar
Member
505 posts
Joined: Dec. 2013
Offline
G'day,
Just wondering if anyone knows any good ways to mirror character poses for limbs?
I'm just having to manually do it all the time and it feels like there must be a better way.
Any ideas?

Thanks,
Pete
User Avatar
Staff
3448 posts
Joined: July 2005
Offline
something like this should be scripted.
get the parameters of the section of the rig (L_Arm), set those parameters of the corresponding section (R_Arm) etc
Michael Goldfarb | www.odforce.net
Senior Technical Director
SideFX
www.sidefx.com
User Avatar
Member
505 posts
Joined: Dec. 2013
Offline
Thanks actor,
That's something I could do in unity pretty quick but I have no idea how to do that in Houdini.
Is there a scripting API reference? I'm just struggling to find the methods and things I'll need.

Thanks,
Pete
User Avatar
Staff
3448 posts
Joined: July 2005
Offline
python is supported - http://www.sidefx.com/docs/houdini15.5/hom/_index [sidefx.com]
Michael Goldfarb | www.odforce.net
Senior Technical Director
SideFX
www.sidefx.com
User Avatar
Member
505 posts
Joined: Dec. 2013
Offline
Hey I got something working!

Made a little button out of this script -
def MirrorObject(InputName):
    FoundOpposite = False
    if "L_" in InputName:
        OpposideSide = InputName.replace("L_","R_")
        FoundOpposite = True
        
    if "R_" in InputName:
        OpposideSide = InputName.replace("R_","L_")
        FoundOpposite = True
    
    if FoundOpposite == True:
        #Store the Opposite Side
        OppositeObj = hou.node(OpposideSide)
        #Store Current Side
        thisSide = hou.node(InputName)
        
        #Rotations
        if OppositeObj.parm("rx").isLocked() == False:
            OppositeObj.parm("rx").set(thisSide.parm("rx").eval())
            OppositeObj.parm("ry").set(thisSide.parm("ry").eval())
            OppositeObj.parm("rz").set(thisSide.parm("rz").eval())
        
        #Transforms 
        if OppositeObj.parm("tx").isLocked() == False:
            OppositeObj.parm("tx").set(-thisSide.parm("tx").eval())
            OppositeObj.parm("ty").set(thisSide.parm("ty").eval())
            OppositeObj.parm("tz").set(thisSide.parm("tz").eval())         
        
#Mirror the selected bones
for node in hou.selectedNodes():   
   path = node.path()
   MirrorObject(path) 

Objects have to named with R_ or L_ for it to pick up on them.
Next I'm going to try and save that to disk so I can copy a pose from one frame and paste to another.

P.
Edited by peteski - Sept. 5, 2016 20:49:09
User Avatar
Member
505 posts
Joined: Dec. 2013
Offline
Okay I got the save to disk working! So now I can store a pose and restore it at another frame (mirrored or not). It's super handy! It might break, but I thought I'd post it anyway incase anyone else (or future me) need it. Arctor, maybe if you have a sec, could you have a quick look at the code, just in case I'm doing something obviously stupid

Store the currently selected object's poses -
#Store all Currently Selected Objects
ValuesToSave = ""
for node in hou.selectedNodes():
    #Node Path
    ValuesToSave += node.path()+','
    #Rotation
    ValuesToSave += str(node.parm("rx").eval())+","
    ValuesToSave += str(node.parm("ry").eval())+","
    ValuesToSave += str(node.parm("rz").eval())+","
    #Transform
    ValuesToSave += str(node.parm("tx").eval())+","
    ValuesToSave += str(node.parm("ty").eval())+","
    ValuesToSave += str(node.parm("tz").eval())+"\n"
#Save to disk
SaveFile = hou.hscriptExpression("$JOB")+'TempPose.txt'
file = open(SaveFile, "w")
file.write(ValuesToSave)
file.close()

Restore the same pose -
#Read the transforms from disk and Restore to selected objects
StoredFile = open(hou.hscriptExpression("$JOB")+'TempPose.txt',"r")
LoadedValues = StoredFile.readlines()
for Line in LoadedValues:
    LineSplit = Line.split(",")
    TargetItem = hou.node(LineSplit[0])
    
    #Load Rotations
    try:
        TargetItem.parm("rx").set( float(LineSplit[1]) )
        TargetItem.parm("ry").set(LineSplit[2])
        TargetItem.parm("rz").set(LineSplit[3])
    except:
        pass
            
    #Load Transforms
    try:    
        TargetItem.parm("tx").set(LineSplit[4])
        TargetItem.parm("ty").set(LineSplit[5])
        TargetItem.parm("tz").set(LineSplit[6])
    except:
        pass
        
        """the try is there to avoid issues with permissions on some parameters"""

Restore the pose Mirrored -
#Read the transforms from disk and Restore to selected objects
StoredFile = open(hou.hscriptExpression("$JOB")+'TempPose.txt',"r")
LoadedValues = StoredFile.readlines()
for Line in LoadedValues:
    LineSplit = Line.split(",")
    TargetItem = hou.node(LineSplit[0])
    
    MirrorMultiplier = 1#This is used to flip the rotations in certain cases 
    
    #Check for opposite side
    FoundOpposite = False
    if "L_" in LineSplit[0]:
        OpposideSide = LineSplit[0].replace("L_","R_")
        FoundOpposite = True
        
    if "R_" in LineSplit[0]:
        OpposideSide = LineSplit[0].replace("R_","L_")
        FoundOpposite = True    
    
    if FoundOpposite == True:
        #Change the target item if the opposite item was found
        TargetItem = hou.node(OpposideSide)
    
    #If there was no opposide set the mirror multiplyer to mirror this bone
    if FoundOpposite == False:
        MirrorMultiplier = -1;
    
    #This is a really hacky way of differentiating between objects that should have mirrored rotations...
    try:
        TargetItem.parm("tx").set(0.0)
        MirrorMultiplier = -1;
    except:
        pass
        
    #Load Rotations
    try:
        TargetItem.parm("rx").set(float(LineSplit[1]))
        TargetItem.parm("ry").set(float(LineSplit[2])*MirrorMultiplier)   
        TargetItem.parm("rz").set(float(LineSplit[3])*MirrorMultiplier)
    except:
        pass
        
    #Load Transforms
    try:
        TargetItem.parm("tx").set(-float(LineSplit[4])) 
        TargetItem.parm("ty").set(LineSplit[5])
        TargetItem.parm("tz").set(LineSplit[6])
    except:
        print ("Transform Not Active");
        pass
Edited by peteski - Sept. 6, 2016 00:34:46
User Avatar
Member
483 posts
Joined: Dec. 2006
Offline
Many thanks for sharing :-)
English is not my native language, sorry in advance for any misunderstanding :-)
User Avatar
Staff
3448 posts
Joined: July 2005
Offline
cool!
seems to work well.
my only concern would be that it's dependent on nodes rather than parameters. I'd have a button or something on the UI of the character that would send the pose to the opposite parts (arm, hand etc)

Michael Goldfarb | www.odforce.net
Senior Technical Director
SideFX
www.sidefx.com
User Avatar
Member
505 posts
Joined: Dec. 2013
Offline
Hey I managed to get the scripts working on a parameter level
(thanks @tamte for the help getting interface buttons workin'!)

It's a lot cleaner really and it looks like it can handle other parameters (like IK blend and such).
I'm just trying to figure out how to add things to a list now so I could perhaps store poses and stuff. Seems like it might be doable with ordered list?

P.
Edited by peteski - Sept. 28, 2016 04:38:27
User Avatar
Staff
3448 posts
Joined: July 2005
Offline
you can store poses in the Pose Library

we just write out the parameter values/channels into a JSON file
Michael Goldfarb | www.odforce.net
Senior Technical Director
SideFX
www.sidefx.com
User Avatar
Member
505 posts
Joined: Dec. 2013
Offline
Oh yeah
I'll jump on that, thanks!
User Avatar
Member
505 posts
Joined: Dec. 2013
Offline
Hey I had a mess with the pose library but I wanted to add a few things so I finished off the script I was working on and it's working pretty cool!
Figured you might like a look?

Here's the hand, also a pose stored on left or right can be restored from the other side.


The whole thing mirrors too! (That is a tiny bit slower)


I realised I could use this for saving and loading rig variations too


On to Animating some stuff
P.
User Avatar
Member
45 posts
Joined: Aug. 2012
Offline
Looks great! You learned to make its own scripting in Houdini? Have you watched a video tutorials or something like that? How long did it take you to find how the one or the other function, with which it works?
User Avatar
Member
505 posts
Joined: Dec. 2013
Offline
Hey thanks Felix,
It took a little while to get my head around but thankfully Python is so widely used that for any general question, you can just do a quick search online and you'll find an answer. For the more Houdini specific stuff I just keep a bookmark of the reference ( and hassled a lot of the good people here ).

This isn't bad for an into tute - https://www.youtube.com/watch?v=GubmnSEyKS8 [youtube.com] it goes over some of the basic things you can do with it pretty easily.
Also the docs are actually quite good, http://www.sidefx.com/docs/houdini15.5/hom/_index [sidefx.com]

I'll post up a little example file soon and you can look through that.
Pete
User Avatar
Member
45 posts
Joined: Aug. 2012
Offline
Yep… Actually Python language is very easy to understand. At given instant of time it became very popular in the world and a lot of information you can get. But in Houdini, this language is used as binding element. If you do not know the functions HOM and how they work - is a headache. Also, for the code editor, inconvenient to receive feedbacks and others issues. You can spend a lot of time to finally understand where you're wrong. This video tutorial I already looked. Perhaps this is the foundation to begin to do something. However, this is not enough to do something more complex. Thank you can share your code. It helps in understanding how some things are work.
  • Quick Links