Enabling/Disabling Markers Automatically

Hello!

I have a rig that is bound to several character meshes at once; they all use the same core joints (pelvis, spine, head, etc) but have several unique feature controls (hair, ears, tails etc) that get turned off/on via an enum on the world control. I’ve set up one big ragdoll for the rig, enabling/disabling character-unique markers via the world control enum- I’m using SDKs to set the ‘enabled’ marker channel. However the enabled channel does not always update when I change the enum; the enabled channel will say 0, but the marker is still visible and colliding with other objects. It’s especially bad when the rig is referenced in. I’ve found that they’ll update after I open the rMarker settings in the channel box, but to do that for every marker every time I want to switch characters is very time-consuming.
Is there a better way to set up automatic enable/disable?

I’m also interested in the LOD channel in the rMarker settings- is this a potential way to have one marker with multiple meshes? Or possibly a better way to enable/disable markers?

Thanks!

Sorry for the delay on this one, we’ve seen it and one of us will tend to this first thing tomorrow morning.

1 Like

Hi @Hallie, I’ve had a look at this and it’s true; the Enabled attribute does not respect changes happening via a connection, only changes made by directly editing the attribute.

This is an oversight, as we did not expect this attribute to be connected, which typically means animated, which this attribute cannot be. But wiring things together with higher level logic is a solid usecase.

This can’t be addressed without a change to the software, so you’ll need a workaround. Something that involves directly setting the Enabled attribute.

So how about this:

from maya import cmds

# Something to set the Enabled attribute directly
def script():
    value = cmds.getAttr(attr)

    for marker in cmds.ls(type="rdMarker"):
        cmds.setAttr(marker + ".enabled", value)

# An attribute to monitor, e.g. the same one driving your SDKs
attr = "head_jnt_rPin.visibility"

# A script job to trigger our script when this attribute changes
job_id = cmds.scriptJob(attributeChange=[attr, script])

# Do delete it, run this
#cmds.scriptJob(kill=job_id, force=True)

With this, you could monitor an attribute on your high-level controller, and within the script iterate over connected Markers - similar to what the SDKs would do - and set the Enabled attribute for those Markers.

Finally, to have this script job always be available when using this rig, you could put the above into a script node set to Script Type: Open/Close.

Hope it helps, we’ll look into making this attribute able to respond to connections for the next release.

1 Like

Thanks so much!! This was extremely helpful, and works perfectly. Luckily we’re not looking to actually set keys on the enabled attribute, so I didn’t need to worry about that; we just needed the markers to update instantly.

For anyone else looking to do this, here’s what I did:

  1. Create a Script node with Source Type Python and Script Type GUI Open/Close (I found GUI to be better for my situation because I needed the script to fire whenever the scene itself is opened/closed, not the whole of maya).

  2. In the Before section of the script node, I imported my python script and ran my ‘create script job’ function, which takes three parameters: the file name (for checking and adding reference namespaces to control and rMarker names), the watched object.attribute, and a dictionary of the rMarker names as keys, the enum value they’re enabled on as keyvalue. Being able to pass these custom parameters to the script allowed me to reuse the code for all the rigs that needed this.

  3. Script job gets made that triggers every time the object.attribute I inputted is changed. The scriptjob script looks at the value of the watched attribute, and changes all the rMarker enabled values based on that value.

  4. In the After section of the script node, I called my ‘kill scriptjob’ function.

Super simple!! Thanks for your help!

1 Like

Another question occurs to me however: Does the same apply for enabling/disabling the rSolver itself, and rGround? Should I connect those via a script as well, or will direct connection work in that case?

Every node would have this limitation, except for the solver as it’s a special case.

1 Like

Howdy y’all! Reopening this thread because I’ve encountered an interesting bug with the script solution that I could use some help understanding.

This doesn’t happen every time, but occasionally an animator will open the rig to find that some of the markers have been removed from the solver. There’s only one rSolver in the scene, so the removed markers aren’t a part of a new solver, they’re kinda just existing invisibly until I use the ‘Move To Solver’ function to return them to the original rSolver. I believe it’s somehow tied to the enabling/disabling script I’ve written because I’ve seen if occur after the scriptjob switches markers on/off; they’ll suddenly be removed from the solver. The script just sets the ‘Enabled’ attribute on the marker to On/Off contextually:

#The integer represents on which enum the script should turn the marker to 'On' 
markers = {"rMarker_foot": 0, "rMarker_elbow": 1}

def enablefunction(markers={}):

    for marker in markers:
        if markers[marker] == value:
            mc.setAttr(marker + ".enabled", 1)
        else:
            mc.setAttr(marker + ".enabled", 0)

    return

^^ Super-reduced example of my script with a good chunk missing, but this is all the script does to the rMarker, is setting the ‘enabled’ attribute. I’m not sure why that’s causing the markers to be removed from the rSolver.
How would one manually remove a marker from a solver without deleting it? Maybe that would lend some insight into how this is occurring?
Thank you!

Hm, “removed” can have a few meanings.

For starters, when a Marker is Enabled = Off it is technically removed from simulation. It’s as if it was deleted; except its node and attribute remains such that it can be re-enabled non-destructively. But it isn’t removed as a member of the solver, for that you can double-check the rMarker.startState connection into rSolver.inputStart.

So as a step 1, confirm that this connection is still there, that’ll sort out one potential cause.

Does the scene always open in the same state, with the same Markers removed? If so, would it be possible to reduce this scene to the bare essential 2 Markers; one working and one not working, such that it could be uploaded here? That would help narrow things down significantly.

It would be surprising if Markers were randomly present and not present when opening the same scene, but if so that would still be a clue of a different sort.

These are Markers with Enabled = On, that do have a connection to rSolver.inputStart? And the solver you move to is the same solver they are already connected to?

It’s unlikely related to the script itself, but it could be related to when it’s called. Ragdoll cares deeply about the current frame you are on; especially whether that is the (1) start frame or a (2) later frame. It’s possible that if the Enabled attribute is modified on a later frame and then the scene is saved, that Ragdoll would get confused when it comes time to open that scene.

That would be another clue; try any scene that breaks, fix it (e.g. Move to Solver) and save on the start frame (or any frame before it) and open it again to see whether you can reproduce the problem.

That could make it hard to spot indeed…

You can remove the connection between Marker and Solver. This is what Move to Solver does, followed by re-adding it again.

Sorry for the loose definition of ‘removed’. I’m honestly not sure what to call it. But you’re right, it totally has to do with the frame ‘enable’ is set on! However I’ve encountered two different cases when looking deeper into this:

  1. I am able to easily reproduce the ‘removed’ state when using my script on any frame other than the start frame. Saving this file and reopening, the marker is still ‘removed’, but going to the first frame successfully updates the marker, and the problem is fixed. Looking at the node editor the marker is still connected to rSolver.inputStart and rSolver.inputCurrent, so it probably just needed the start frame to update it’s state.

  2. I opened one of my animators’ files, where the entire right arm had been ‘removed’. It opens this way every time, and going to the start frame does not successfully update the right arm markers. Digging in the node editor, the right arm markers are still connected to their rGroup node, but the rGroupShape is not connected to rSolver.inputState or rSolver.inputCurrent. It’s not outputting to anything. Interestingly, the right arm is not set up to be effected by the Enable/Disable script, so somehow the animator got the arm into this state manually. Maybe they set the rGroup’s enable attribute on a frame other than the start frame?
    I can select all the right arm controls and use ‘Move To Solver’ to reconnect the markers to the original solver (the one they had previously been a part of- there are no other solvers in the scene), but this removes the markers from their rGroup, and I can’t reconnect the rGroup this way. Saving and reopening the file after using ‘Move To Solver’ results in the right arm markers remaining connected to the solver, and the rGroup connected to nothing.

To fix case #01 using the script, I’m thinking of adding a line of code that sets the scene to the starting frame before allowing it to set the markers’ ‘enable’ attributes. However I’m not sure how case #02 occurred or how to approach it so it doesn’t happen again. Thank you for your help so far!!

This is a clue. Ragdoll doesn’t alter connections based on any attribute, especially not on scene-open. One of the ways this could happen would be if the group was extracted into a new solver, and for that new solver to then be deleted prior to saving. I would monitor exactly when that connection breaks, I expect it is not related to that Enabled attribute.

Edit: Also, for completeness, you mention “Move to Solver” a few times which sounds like it’s commonly used; to move markers that are part of a group, you would only move the group. Markers belong to that group, the group belongs to the solver. If you move the markers (or worse yet, and group) from one solver to another, it’s likely they would be added individually, with Markers then belonging to the solver and not the group, alongside an empty group.

So it’s sounding like this is a case of user error on a case-by-case basis, and not something I can guard against in the future, unfortunately. Thanks for troubleshooting with me, you’re always a great help!! :heart:

Per your edit: I did try to use ‘Move To Solver’ on just the rGroup, then the rGroupShape, and both cases gave me an error:

select -cl  ;
select -r arm_r_rGroup ;
select -add rSolver ;
from ragdoll import interactive as ri
ri.move_to_solver()
# Warning: ragdoll.format_exception_wrapper() - Traceback (most recent call last):
  File "~\Documents\maya\modules\Ragdoll-2024_07_01\scripts\ragdoll\interactive.py", line 159, in format_exception_wrapper
    return func(*args, **kwargs)
  File "~\Documents\maya\modules\Ragdoll-2024_07_01\scripts\ragdoll\internal.py", line 520, in _undo_chunk
    return func(*args, **kwargs)
  File "~\Documents\maya\modules\Ragdoll-2024_07_01\scripts\ragdoll\interactive.py", line 2000, in move_to_solver
    "Select one or more markers to group."
ragdoll.internal.UserWarning: Select one or more markers to group.
 # 

Ah, yes I see it.

Looks like an oversight. The code is simple enough to copy/reuse in your own codebase should you need it; it just connects Marker to Solver and that’s it. You could edit it to also find Groups, they have the same attribute names and would work out of the box.