Jump to content
Sign in to follow this  
Atom

[PYTHON] Mocap Retarget Problem?

Recommended Posts

Posted (edited)

I am trying to use python to create a series of CHOP expressions to link a mocap/.bclip driven bone structure to a NULL based FBX rig.

While this CHOP technique works fine for translation (hips track mocap good), it does not seem to work for rotation.

I have tried all kinds of offsets and axis re-mappings but none of them produce a tracked result that I need to continue with this work.

Is there some kind of math that needs to happen to rotation values to transfer them from one node to another?

Here is my basic working expression, with 90 being an manual offset I tried.

(90+chop("/obj/crowd_mocap_SOURCE/mocap/data/calf_L:rx"))

 

The attached scene contains my entire working setup so far, including the fixup scripts I used to prepare the BVH data from mocapdata.com for .bclip conversion.

 

As you can see in the animated image I do get some tracking on the thigh, but it does not fully match the range of movement found in the mocap.

mocap_bclip.gif

mocap_retarget_issue.zip

Edited by Atom

Share this post


Link to post
Share on other sites
Posted (edited)

Had a small break through. I am using a Limit CHOP with Normalize enabled to force the MIN/MAX of each channel in to the known -1,1 range. This allows me to construct a fit from the CHOP to remap into the target range for the NULL rotations.

Untitled-1.jpg.b93b72bce96e2aa034b8bd3f223be0a7.jpg

The new CHOP expression looks like this for the lower leg.

fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/foot_L:rx"),-1,1, 10,(10-90))

The new CHOP expression looks like this for the upper leg.

fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/calf_L:ry"),-1,1, 25,(25-45))

 

I guess I have to determine each bone range by trial and error..?

mocap_bclip.gif

Edited by Atom

Share this post


Link to post
Share on other sites
Posted (edited)

It looks like the upper leg, on the right side, needs it's fit range reversed, while the lower leg does not...hmmm. A jumble of special cases.

Upper RIGHT leg expression.

fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/calf_R:ry"),-1,1, (25-45),25)

Lower RIGHT leg expression.

fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/foot_R:rx"),-1,1, 10,(10-90))

 

mocap_bclip.gif

Edited by Atom

Share this post


Link to post
Share on other sites
Posted (edited)

I managed to get the upper arms socket mapped to two axis from the mocap data. So on this particular joint, RZ is driven by RX data and RX is driven by RY.

RZ gets these expression.

// Left Side.
fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/lowerarm_L:rx"),-1,1, -70,(-70+90))

// Right Side.
fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/lowerarm_R:rx"),-1,1, 70,(70-90))

RX gets these expressions.

// Left Side.
fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/lowerarm_L:ry"),-1,1, 45,(45-90))

// Right Side.
fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/lowerarm_R:ry"),-1,1, (45-90),45)

 

bclip_driver.gif

Edited by Atom

Share this post


Link to post
Share on other sites
Posted (edited)

I created a def to link offset values to the expression. The slider callback is linked to the def.This makes it easier to dial in a specific bone section on the target rig.

exec(kwargs['node'].parm('python').eval());updateUpperArm() 
node_self  = hou.pwd()
bone_target = node_self.parm("rig_target").eval()

def updateUpperArm():
    value_X = hou.Node.evalParm(node_self, "rot_upper_arm_X")
    value_Z = hou.Node.evalParm(node_self, "rot_upper_arm_Z")
    # Right.
    target_candidate = "%s/%s" % (bone_target, "upperarm_R")
    t = hou.node(target_candidate)
    if t != None:
        expr = 'fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/lowerarm_R:ry"),-1,1, (%s-90),%s)' % (value_X,value_X)
        t.parm('rx').setExpression(expr)
        expr = 'fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/lowerarm_R:rx"),-1,1, %s,(%s-90))' % (value_Z,value_Z)
        t.parm('rz').setExpression(expr)
    # Left.
    target_candidate = "%s/%s" % (bone_target, "upperarm_L")
    t = hou.node(target_candidate)
    if t != None:
        expr = 'fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/lowerarm_L:ry"),-1,1, %s, (%s-90))' % (value_X,value_X)
        t.parm('rx').setExpression(expr)
        expr = 'fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/lowerarm_L:rx"),-1,1, -%s,(-%s+90))' % (value_Z,value_Z)
        t.parm('rz').setExpression(expr)

 

bclip_driver.gif

Edited by Atom

Share this post


Link to post
Share on other sites

I have spine neck and head installed. It looks like each expression set requires it's own def to update the expressions when the sliders change.

def updateSpine():
    value_X = hou.Node.evalParm(node_self, "rot_spine_X")
    value_X_range = hou.Node.evalParm(node_self, "rot_spine_X_range")   #45
    value_Y = hou.Node.evalParm(node_self, "rot_spine_Y")
    value_Y_range = hou.Node.evalParm(node_self, "rot_spine_Y_range")   #45
    value_Z = hou.Node.evalParm(node_self, "rot_spine_Z")
    value_Z_range = hou.Node.evalParm(node_self, "rot_spine_Z_range")   #45

    # Spine from pelvis up to head.
    target_candidate = "%s/%s" % (bone_target, "spine01")
    t = hou.node(target_candidate)
    if t != None:
        expr = 'fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/spine02:rx"),-1,1, %s,(%s+%s))' % (value_X,value_X_range,value_X)
        t.parm('rx').setExpression(expr)
        expr = 'fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/spine02:ry"),-1,1, (%s+%s),%s)' % (value_Y,value_Y_range,value_Y)
        t.parm('ry').setExpression(expr)
        expr = 'fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/spine02:rz"),-1,1, (%s-%s),%s)' % (value_Z,value_Z_range,value_Z)
        t.parm('rz').setExpression(expr)
    target_candidate = "%s/%s" % (bone_target, "spine02")
    t = hou.node(target_candidate)
    if t != None: 
        expr = 'fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/neck:rx"),-1,1, %s,(%s+%s))' % (value_X,value_X_range,value_X)
        t.parm('rx').setExpression(expr) 
        expr = 'fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/neck:ry"),-1,1, (%s+%s),%s)' % (value_Y,value_Y_range,value_Y)
        t.parm('ry').setExpression(expr) 
        expr = 'fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/neck:rz"),-1,1, (%s-%s),%s)' % (value_Z,value_Z_range,value_Z)
        t.parm('rz').setExpression(expr) 
    target_candidate = "%s/%s" % (bone_target, "spine03")
    t = hou.node(target_candidate)
    if t != None: 
        expr = 'fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/head:rx"),-1,1, %s,(%s+%s))' % (value_X,value_X_range,value_X)
        t.parm('rx').setExpression(expr) 
        expr = 'fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/head:ry"),-1,1, (%s+%s),%s)' % (value_Y,value_Y_range,value_Y)
        t.parm('ry').setExpression(expr) 
        expr = 'fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/head:rz"),-1,1, (%s-%s),%s)' % (value_Z,value_Z_range,value_Z)
        t.parm('rz').setExpression(expr) 
    target_candidate = "%s/%s" % (bone_target, "neck")
    t = hou.node(target_candidate)
    if t != None: 
        expr = 'fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/headEnd:rx"),-1,1, %s,(%s+%s))' % (value_X,value_X_range,value_X)
        t.parm('rx').setExpression(expr) 
        expr = 'fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/headEnd:ry"),-1,1, (%s+%s),%s)' % (value_Y,value_Y_range,value_Y)
        t.parm('ry').setExpression(expr) 
        expr = 'fit(chop("/obj/crowd_mocap_SOURCE/mocap/OUT/headEnd:rz"),-1,1, (%s-%s),%s)' % (value_Z,value_Z_range,value_Z)
        t.parm('rz').setExpression(expr) 

 

bclip_driver.gif

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×