Skip to content

2021.04.11

import

Highlight for this release is import of physics from one character to another!


Head's up Game Developers

NameComponent was changed, and entity values are now a fully-fledged type.

See Backwards Incompatibility for details.


Import onto Selected Character

ragdollimportall

Import onto the Ragcar

ragcarimport

Import limbs of Tiger

ragdollimporttiger


Import

Animators can now setup physics one character, export it, and then import onto another.

The usecase is having spent time setting up a perfect ragdoll and then wanting to reuse this setup across multiple scenes, on multiple instances of the same referenced character, or on characters with similar naming scheme and anatomy. It can also be used to import parts of a character or individual objects.

Demo

Here's an 18 second run-down of the complete workflow, from authoring to import.

ragdollimportquick2


Features

Anything you can apply physics to can be exported.

The nodes onto which physics is imported..

  • ✔️ Can have a different namespace
  • ✔️ Can have a different naming convention
  • ✔️ Can have a different pose
  • ✔️ Can have a different scale
  • ✔️ Can be animated
  • ✔️ Can be referenced
  • ✔️ Can be imported in pieces, based on what is currently selected

It will remember..

  • ✔️ All edited attributes, like Guide Strength
  • ✔️ All edited constraints, like their limits and frames

It will not remember..

  • ❌ The convex hulls
  • ❌ The original root of your chains
About those 'Convex Hulls'..

Convex hulls, those triangulated versions of your Maya shapes - the Mesh shape type - are re-generated onto whatever character you import onto. This is probably what you want, and enables you to apply physics onto characters with different geometry from when you originally authored the physics. Although sometimes it's not, which is why this we be augmented in a future release.

About the 'Original Root'..

The root in any chain is the first in your selection when creating the chain. If you build a network of chains - a "tree" - which is common for any character of more than 1 limb, the exported file will not remember which the original root was. It will figure out new roots procedurally based on their parent/child relationship which may or may not be the same as your original. For importing a full character, this makes no difference. Only for the advanced case of exporting a full character but then wanting to apply only the arm or leg of that character onto another character makes this problematic. This will be addressed in a future release.

And that's about it! It doesn't even have to be a "character", any combination of Maya nodes you can apply physics to can have their physics exported. Like a (rag)car, or just a (rag)box.


User Interface

In addition to import everything found in an exported file, there's a UI for more control.

The UI resembles native Maya and Ragdoll option dialogs, with two interesting bits.


1. File Browser

The top part displays other Ragdoll scene (.rag) files in the same directory as the selected file, along with the thumbnail stored during the time of export. The thumbnail currently isn't visible during export, it is captured from the currently active viewport. An Export UI with thumbnail preview (and more!) will be added in a future release.


2. Content Preview

This sections shows you the contents of the physics scene, ahead of actually importing it.

It will visualise a number of things.

  1. Which Maya nodes will be "physicalised"?
  2. Which nodes present during export are not present in the currently opened scene?
  3. What was the original path of a node during export?
  4. What is the destination path of the node being imported?
  5. Is there a destination node?
  6. Is the destination already physicalised?
  7. What was the original node icon, e.g. nurbsCurve or mesh?
  8. What is the Shape Type of the exported rigid, e.g Capsule?


Introduction

Did you see Snyder's Justice League? In it, they introduce and explain the "mother box" and how it is capable of turning the dust of a burnt house back into a house.

This Import feature is the Mother Box of Ragdoll.

The export format is identical to what game developers use to author physics in Maya and import it into their game engine. It contains all data managed by Ragdoll in full detail. Enough detail to reverse-engineer it back into a Maya scene, which is exactly what's going on here.


Example Files


Thumbnail

Each export captures the currently active 3d viewport for use as a thumbnail. So, whenever you export, remember to smile and wave! :D

thumbnail5


Context Sensitive

The visualisations will update as you select different nodes and edit the various options in the UI.

To illustrate this, let's import onto the same scene we exported.

Export

Only one character is physicalised and exported.

ragdoll_sacmescene_export

Import

Notice that importing is not possible, since the character is already physicalised. Unless we replace the namespace, by selecting another character.

ragdoll_sacmescene_autonamespace


Use Selection

Import onto selected nodes with Use Selection toggled (it's the default).

partialimport


Search and Replace

Every node is stored along with its full path, such as..

|root_grp|spine_grp|spine_ctrl

And in most cases can get quite long, with one or more namespaces and tens to hundreds of levels deep in hierarchy.

|_:Group|_:Main|_:DeformationSystem|_:Root_M|_:RootPart1_M|_:RootPart2_M|_:Spine1_M|_:Spine1Part1_M|_:Spine1Part2_M|_:Chest_M|_:Scapula_L|_:Shoulder_L|_:ShoulderPart1_L|_:ShoulderPart2_L|_:Elbow_L|_:ElbowPart1_L|_:ElbowPart2_L|_:Wrist_L|_:IndexFinger1_L

Here, the namespace is simply _:

The Search and Replace boxes of the UI can be used to replace parts of each path, to try and map the original path to whatever path is currently available in the scene.

ragdollsearchreplace2


Auto Namespace

One challenge with export/import it remapping names from the original scene onto your new scene. Ragdoll solves the typical case of only the namespace being different with "Auto Namespace".

autonamespace

"Auto Namespace" will replace any namespace in the original file with whatever namespace is currently selected. Neat! If there are multiple namespaces, it'll use the last namespace. Let me know how you find that, there's room left to explore here. Most often, you only ever have a single namespace, but Maya does allow you to tie yourself into a knot if you really wanted to.


Auto Scene

Locate and use the original physics scene from the original file, so as to preserve your multi-scene setups.

For example, if your one character has 3 physics scenes - one for the right arm, one for the left and a single one for both legs - then "Auto Scene" will preserve these scenes for you.

Performance Tip

Using more than one scene can improve performance significantly, as Ragdoll will parallelise each invidual scene. The caveat is that rigids in different scenes cannot interact with each other.


Ragdoll Clean

Here's a quick way you can use this feature to "clean" a physics scene.

  1. Export
  2. Delete All
  3. Import

The resulting scene will be "clean" in that it will have been broken down into its componens and reassembled again, not taking into account anything Ragdoll doesn't know about.

ragdoll_clean

(I may just add a menu item for this, called Clean to do this in one go :)


Roadmap

A few things became apparent as I rounded off this feature

  1. Export UI - You'll want control over what that thumbnail looks like, currently it'll take a snapshot but not show you what that snapshot looks like until you look at it from the importer
  2. Remember "root" of each chain - The importer will recognise what is a Rigid and what is a Chain, but it's having trouble distinguishing the root of each chain. For example, if you made the spine into a chain, followed by the two arms, odds are it'll think the hip leading out to the hand is one chain, and torso to head being another chain, and so on. This isn't an issue when importing a full character, but it'll keep you from being able to import only one of those chains. E.g. just the arm.


Import Python API

Anything the UI can do can be done via Python, using the new dump.Loader object.

from ragdoll import dump
loader = dump.Loader()
loader.read(r"c:\path\to\myRagdoll.rag")

# Search and replace these terms from the full node path
# E.g. |root_grp|arm_left -> |root_grp|arm_right
loader.set_replace((
    ("_left", "_right"),
    ("_ik", "_fk"),
))

# An automatic method of search-and-replace, that replaces
# any namespaces found in the file with this.
# E.g. |char1:root_grp -> |char2:root_grp
loader.set_namespace("char2:")

# Limit imported nodes to those with an absolute path 
# starting with *any* of these
loader.set_roots((
    "|char1:root_grp",
    "|char2:root_grp"
))

# Deconstruct the provided `.rag` file
# (This is what is visualised in the UI)
# (The exact layout of this data may change)
analysis = loader.analyse()
assert isinstance(analysis, dict)

# Print a brief human-readable summary of the current analysis
loader.report()

Heads up

Consider this a version 0.1 of the API, it will likely change in the future.


Ragdoll Stability

Implementing import put a lot of strain on Ragdoll.

Whereas before, authoring physics was a matter of calling one command at a time, playing around with the result, calling another. Maybe undoing every so often.

Import on the other hand calls tens to hundreds of commands at once, undoing them en masse, redoing them en masse. It exposed a ton of flaws in the system that had gone unnoticed in all but the rarest of occasions. Crashes, a ton of them. The worst kind, the kind that doesn't tell you anyhing about why it crashes.

ragdollstability

The above an example of:

  1. Authoring lots of physics in different ways
  2. Undoing all of it
  3. Redoing all of it
  4. With no problem!

I'm happy to say these have all been resolved, and the automated test-suite has grown 10x since the last release. Every command is tested, and tested again with undo, and again with redoing an undone redo. It is rock solid, and fast.

You can now undo any command as-one, any number of times, redo it any number of times, undo your redo any number of times. Ragdoll will not be the cause of any crashes.


Maya 2022 Stability

Maya 2022 in its current state has proven incapable of reliably supporting Ragdoll.

Maya 2022 may crash with Ragdoll

That's right. Maya 2022 isn't quite baked yet, and needs a Service Pack. Until then, Ragdoll will run reliably so long as you don't delete anything, or try and open a new scene.

In addition to that, the multiplier nodes didn't quite work with Maya 2022, or more specifically with Python 3.

from ragdoll import interactive as ri
ri.multiply_rigids()
# Error: 'filter' object is not subscriptable
# Traceback (most recent call last):
#   File "<maya console>", line 2, in <module>
#   File "C:\Users\marcus\Documents\maya\modules\Ragdoll\python\ragdoll\interactive.py", line 2112, in multiply_rigids
#     root = rigids[0].parent()
# TypeError: 'filter' object is not subscriptable #

There were also crashes happening on deleting rigid bodies from your scene, these got swept away alongside a number of other fixes to the handling of nodes.

So one step forward, one step back. :)


Edit Shape

A new menu item got added for manipulating shapes with a native Maya transform, as an alternative to fiddling with numbers in the Channel Box.

editshape


Proxy Attributes

In Maya 2018 and 2020, the attributes added to your original animation controls that mirror those of Ragdoll were "proxy attributes". That is, they could be edited from either their original attribute, or the one connected to by your control.

That's really convenient.

Turns out, it is also really unstable. Most of the crashes happening so far, especially on deleting physics or starting a new scene, has come from proxy attributes messing everything up. It should't be surprising, even Maya struggles with them.

node = cmds.createNode("transform")
shape = cmds.createNode("nurbsCurve", parent=node)
cmds.addAttr(node, ln="proxyVisibility", proxy=shape + ".visibility")
assert cmds.objExists(node + ".proxyVisibility")
assert cmds.getAttr(node + ".proxyVisibility") == 1

# What should happen to the proxy attribute? :O
cmds.delete(shape)

cmds.getAttr(node + ".proxyVisibility")
# RuntimeError: The value for the attribute could not be retrieved. # 

The same thing applies with access from the API. It just doesn't know what's going on. If we're lucky - which we have been so far - it'll just fail and tell you about it. Other times it'll fail and take Maya down with it. That's just bad.

In Maya 2019, the problem was so severe that proxy attributes were simply not used. With this release, no proxy attributes are used.

I hope to reintroduce them at a later date, once I discover is a safe method (read: workaround) to using them.


Python API Consistency

The good news is, the Python API is maturing. The bad news is, this release introduces backwards incompatible changes.

from maya import cmds
from ragdoll import api

cube, _ = cmds.polyCube()
cmds.move(0, 5, 0)
cmds.rotate(0, 45, 45)
scene = api.createScene()
rigid = api.createRigid(cube)

So far so good.

# Before
api.socketConstraint(parent, child, maintain_offset=False)

# After
api.socketConstraint(parent, child, opts={"maintainOffset": False})

Here's the change. These behavior-like arguments have been moved into an opts={} argument, and is now consistent across any commands that take "options". It's to faciliate a large number of options, both from the UI and scripting and enhance compatibility over time; with a dictionary, you can test for availability of arguments at run-time, as opposed to suffer the consequences of not being able to call an update function.

I'm still exploring ways of getting more options into commands, without polluting the argument signature, without changing their order when an argument is deprecated, or changing an argument name when jargon inevitably changes. Using a dictionary for options-like arguments enables us to pass arbitrary sized options to functions, they can also be passed to functions that don't necessarily need all contained options, meaning you can establish a single options dictionary up-front and pass that to all relevant functions.

It's too soon to tell whether the cons of this approach outweighs the pros. This is one reason for the API still going through changes.

The non-optional arguments are those that are never intended to change, like the createRigid(node) argument. Every rigid needs something to make rigid. (Or so you'd think, as you can now also create a rigid from a new empty transform).

So, the API has changed and will continue changing for a while longer.

Node/Attribute format

The Ragdoll scene format is stable and has been for months. It will remain compatible with future versions of Ragdoll, which means anything you build today (or months ago) will continue to work identically.

The Python API on the other hand is not yet refined and is still changing. So when you build tools ontop of Ragdoll, keep in mind that nodes, their attributes and their connections are stable, but the means of creating those connections are not. So if you need stability today, look at what nodes and connections are made by the API, and do it yourself.


Ragdoll Explorer

For developers

Explorer has gotten an update, inching its way towards Outliner-like behavior and feel. Eventually maybe even an integration with the Outliner, similar to how USD slots into Maya 2022. That's quite neat!

ragdollexplorer3


Logging Level

You can now tune the way Ragdoll communicates with you.

  • Off means it won't tell you anything, not even warnings
  • Default is what you've gotten used to so far
  • Less only shows you important messages that require you to take action
  • More is the full monty, performance metrics, detailed messages, you name it
Programmer Jargon

These are animator-friendly jargon for the native logging.INFO and logging.WARNING levels. "Off" means logging.CRITICAL since Ragdoll does not emit any critical messages.

image


Developer Updates

A few things has been improved for those using Ragdoll as an authoring platform for other software like Unreal and general game engines.


New Components

The export format has been graced with new components to accommodate for the import feature. As the name suggests, these are stricly related to UI and aren't required for reproducing the physics in another application or engine.

They are meant to cover user elements in Maya such that they can be accurately reproduced on re-import back into Maya.

New Components

  • RigidUIComponent
  • ConstraintUIComponent
  • LimitUIComponent
  • DriveUIComponent
  • RigidMultiplierUIComponent
  • ConstraintMultiplierUIComponent

Here's what the new components may look like in your exported file.

{
    "type": "RigidUIComponent",
    "members": {
        "shaded": false,
        "airDensity": 1.0,
        "shapeIcon": "transform",
        "multiplierEntity": {
            "type": "Entity",
            "value": 0
        }
    }
},
{
    "type": "ConstraintUIComponent",
    "members": {
        "multiplierEntity": {
            "type": "Entity",
            "value": 0
        },
        "childIndex": 2
  }
},
    "type": "LimitUIComponent",
    "members": {
        "strength": 1.0,
        "angularStiffness": 1000000.0,
        "angularDamping": 10000.0,
        "linearStiffness": 1000000.0,
        "linearDamping": 10000.0
  }
},
    "type": "DriveUIComponent",
    "members": {
        "strength": 0.5,
        "angularStiffness": 10000.0,
        "angularDamping": 1000.0,
        "linearStiffness": 0.0,
        "linearDamping": 0.0
  }
}


There's also an added section for "ui" related data, most interestingly a base64-encoded QPixmap of a thumbnail.

    "ui": {
        "description": "",
        "filename": "C:/scenes/demo/advancedskeleton5.rag",
        "thumbnail": "iVBORw0KGgoAAAAN ... lots more characters ..."
    }

That can be converted like this.

from ragdoll import ui
qpixmap = ui.base64_to_pixmap(data["ui"]["thumbnail"])


Backwards Incompatibility

The export format has changed slightly, here's what you need to know.

  1. NameComponent.path was changed from the full path + Ragdoll node to just full path.
  2. Entity values are now types instead of plain integers

Example

# Before
|root_grp|spine1_ctrl|upperArm_ctrl|rRigid3

# After
|root_grp|spine1_ctrl|upperArm_ctrl

Some values were entities themselves, but there wasn't any way of knowing unless you explicitly new that JointComponent.parent is in fact an entity. This has now been addressed, and all entities now carry a ["type"] signature.

# Before
{
    "type": "JointComponent",
    "members": {
        "disableCollision": true,
        "parent": 16
        "child": 15
    }
}

# After
{
    "type": "JointComponent",
    "members": {
        "disableCollision": True,
        "parent": {
            "type": "Entity",
            "value": 16
        },
        "child": {
            "type": "Entity",
            "value": 15
        }
    }
}