Home Website Youtube GitHub

Parasitic Values Limbs and Twisters

One issue that’s erked and plagued me is the way that mgear uses the roleSpline calculation to work out the rotations for the final joints.

Suppose we take a standard EPIC leg component. The knee joint isn’t calculated with a clean rotation (4); that’s because the root of the leg is counter-rotating (1) as part of the twist calculation. It’s also common to only have one twist joint in game development. But because the U value is only halfway through the rollSpline U value, it never makes the 100% twist (2). So the skinned knee will never point at the pole vector. (3)

It’s important in games that we keep the skeleton as clean as possible. It offers benefits for animation compression, blending poses, and running post-process rigs. Just in animation compression alone, this can add up if there are lots of characters on screen.

In game development, we strip joints out of the rig at lower LODs. In the current setup, if I strip the twisters, the twist shearing happens at the knee as the skinning of the twisters propagates to the leg root. (1) So the candy wrap effect at the knee is more prominent at extreme poses. So far, tho, I’m only stripping joints out at lower LODs, but I know this will be a problem on consoles where we offset the LODs for performance, so LOD2 becomes LOD0 on lower spec. I’m waiting for those skinning bugs to appear on lower-end devices.

What’s the solution?

Right now, I can post-edit the rig to fix the twister and achieve the correct rotation at the proper position by running another roleSpline and merging the result to the div_loc.

I can’t fix the parasitic knee rotations without rolling my own component.

1 Like

Hello!
I’ve been having some issues with parasitic rotations, and I haven’t found a solution yet. I agree with you — parasitic transforms can have a noticeable impact on the game, and I could provide some other examples.

I add a question for mGear developers:
Is there a reason why the first joint of the limb is aimed toward the front of the character instead of lying in the same plane as the leg?

I mean, if each joint of the limb has the same up vector and aims toward its child, then we would have only one axis of rotation, and all roll joints would have clean transforms (assuming there is no translation except along the forward axis for any joint in the chain).

I’m really curious about the explanation behind this choice.:blush:

I’m with you. I think that mgear is excellent, but it comes from a film and VFX background where the skeleton is just a result of the rig stack, a byproduct that isn’t carefully managed enough to meet the demands of game production. In games, the skeleton is a 1st class object; it needs to be highly managed and carefully thought out.

Internally, we are already considering switching to a custom rigging pipeline, where the skeleton is at the front of the chain, and rigs are defined by adding metadata to the skeleton. Meaning we can uplift any historic skeleton to an internal rig.

The other option is to stick to mGear and work with them to solve these issues. Just because it has EPIC in the component name doesn’t mean best practices are followed for game development.

2 Likes

Hello @remicc and @Dave_Moulder
Thanks again for the feedback :heart:
I have a ticket open since May to fix this. but I couldn’t tackle it yet. My apologies for that.

I have moved it to WIP and mark it as High priority

One posible workaround is to use the snap functionallity to attach the rig to an existing joints. It is not ideal but maybe it can aliviate the situation until I fix the issue.

I imagine the following steps:

  1. Build as usual
  2. Delete the rig with the command “Delete + keep Joints”
  3. fix the rotations (script or manually)
  4. Build again with the “connect with exiting joints” activated

@Dave_Moulder @remicc
Do you think we could schedule a call between the three of us to review this together?
I can send an email to arrange it if that works for you – just let me know.

2 Likes

Thank you so much for your support ! It means a lot !
No worries for the delay, i would be glad to help and have a call about it !

Your suggestion could work, but it would slower the execution and on my side I still have issues with the delete rig + keep joints and reconnect workflow.
# Joint from group CharacterRoot_deformers_grp has been disconnected
# Error: curveInfo4 (Curve Info): No valid NURBS curve

# CharacterRoot deleted.
The function ends without building my right hand. No PRE / POST scripts used in the guide.
I can create a github task for that.

About parasiric values, I inspected my main template and the best solution that worked for me is to have a straight spine, straight neck, and use the following guides :
EPIC_arm_02
EPIC_spine_02
EPIC_neck_02
EPIC_leg_02
EPIC_chain_01 (for fingers and meta)
EPIC_foot_01
I gave a chance to other guides with no luck, issues exists in multiple other guides.

Also:
EPIC_spine_02 should have the root at the same XZ position than the first yellow guide of the spine.
EPIC_leg_02 have tiny parasitic values when the bend of the leg is important. More the angle is wide, more the parasitic is noticeable in joint orients. And this issue doesn’t exists in EPIC_arm_02.

I started with the Game Biped Tempalte, which is very usefull to speed up the process, and it seems a lot of parasitic values has been fixed since I did my first test in 2022 - 2023. But some stills there in particular on spine and neck. I can say it is getting better :blush:

I could prepare a scene where is built all the guides to list those who needs improvements.
I will send you my email in private message, so you will be able to schedule a call :+1:

1 Like

I tried connecting to existing joints, but at that time, I was getting errors with that method. Instead, I extracted the skeleton to a fresh file, referenced it into my rig, and then bound it to the mgear skeleton via constraints. I have several bind methods I can choose from. I have a mGear Custom Step Tools Box (UI) that lets me set up all manner of things, including binding skeletons, default settings, hidden controls, and channel groups in the channel box. So much more. It’s a pretty well-defined pipeline now. That UI stores the data on the guide. I have 1 custom step that reads it and acts upon the data.

However, I think the root of the problem needs to be addressed in the components themselves. Especially in the EPIC components.

Probably best to break this idea out into another post, but food for thought. See below…

I also have a mgear_meta.py file that can objectify any mgear rig post-build. We place all our code for the pipeline in the designated area. We’ve extended the RMB for the mgear menu, for example. Have our exporter logic and publishing logic. Pose saving logic, all in the mGear-aware code.

I do think that adding network nodes for each component, along with message links and a way to grab data from a rig, is powerful. Something I did a long time ago, inspired by Seth Gibson (Halo). Became our backbone of custom rigs and eventually went from an internal studio rig to Red9 once the company folded.

See metadata

Example of current studio code wrapping mGear rigs. Not network node-based as above. Just building up the appearance of a meta node network by parsing the rig and its attributes and connections.

import jam.dcc.jam_maya.mgear_meta as mgm

rig = mgm.get_one_rig()
# Result: MetaRig(root: M_MED_Male_01)
face = rig.get_face_system() # Our custom face rig component
rig.get_controls() # all rig controls
arm_comp = rig.find_component_by_type("EPIC_arm_01", side="l")[0]
# ComponentBlock(node : clavicle_l_orbit|arm_L0_root, type : EPIC_arm_01)
arm_comp.get_controls()
# Result: [nt.Transform('arm_l_mid'),
#  nt.Transform('arm_l_fk1'),
#  nt.Transform('arm_l_fk0'),
#  nt.Transform('arm_l_ikcns'),
#  nt.Transform('arm_l_ik'),
#  nt.Transform('arm_l_fk2'),
#  nt.Transform('arm_l_roll'),
#  nt.Transform('arm_l_upv')]
arm_comp.deformers()
# Result: [nt.Joint('lowerarm_twist_01_l'),
#  nt.Joint('upperarm_twist_01_l'),
#  nt.Joint('hand_l'),
#  nt.Joint('lowerarm_l'),
#  nt.Joint('upperarm_l')]
arm_comp.get_length()
arm_comp.flip_pose()
4 Likes

Thanks for the idea and feedback @Dave_Moulder

If you have time, lets aim for next week to do the call with @remicc
I will contact again both of you on Friday. Have a great week!

2 Likes