Jump to content

[PYTHON] Mocap Retarget Problem?


Atom

Recommended Posts

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
Link to comment
Share on other sites

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
Link to comment
Share on other sites

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
Link to comment
Share on other sites

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
Link to comment
Share on other sites

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
Link to comment
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

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...