Saturday, September 7, 2013

lpDynamicChains

This a reference page for the lpDynamicChains tool.

DESCRIPTION

This tool allows for the creation of dynamic joint chains. User can specify joints to become dynamic. Can also turn any geometry into a collision object. Simulations can be cached to a file or baked as animation onto joints and dynamic nodes can be removed when done.

Suggested use: for tails, appendages, for secondary delayed movement - ex: jiggle


File:             lpDynamicChains.py

Versions:     1.0  -  06/28/2013  -  first version created
                   1.1  -  07/02/2013  -  changed curve degree to work with 2-3 joints chain. fixed
                                                        complete removal of dynamic nodes for delete dynamic
                   2.0  -  07/13/2013  -  added create collision object option
                   2.1  -  08/15/2013  -  updated collision feature and entire tool to use nDynamics
                                                        and the nucleus solver unifying all simulations under one solver
                   2.2  -  08/19/2013  -  added option to bake sim onto joint

USAGE NOTES
The UI
        1.     Place script in
                    Windows:     C:/Program Files/Autodesk/Maya2013/Python/lib/site-packages/ 
                    Mac:            Users/userName/Library/Preferences/Autodesk/maya/2013
                                        -x64/scripts/
        2.   To open the UI:
import lpDynamicChains
reload(lpDynamicChains)
lpDynamicChains.UI()
The UI allows for the creation and interaction of dynamic chains in the scene. It also allows access to lpDynamicChains commands

Create Dynamic joints

Select the start joint, shift select the end joint and click the 'Make Joints Dynamic' button under the Main section.

Commands:
from lpDynamicChains import *

dynChain = lpDynamiChain(startJoint, endJoint)
dynChain.create()

def lpDyanmicChain(startJoint="", endJoint="", node=""):
    '''
    Create the custom node

    Parameters:
        startJoint        - Name of the first joint in the chain
        endJoint          - Name of the last joint in the chain
        node              - Name of the node to manage by the class

    Returns:
        Nothing
    '''

def create(self):
    '''
    Create the actual dynamic joint chain node network

    Parameters:
        Nothing

    Returns:
        List of all new created nodes
    '''
A custom locator node will be created at the start joint's position holding the parameters to be tweaked to customize the dynamic behavior of the joint chain.

All nodes created will be stored as meta data on the nodes themselves to allow for the tool to keep track of the nodes associated with every newly created dynamic chain.

You will get a window prompting which nucleus to connect the dynamicChain to.

A log window will display at the end of creation, listing all nodes created with the new dynamicChain.

Remove Dynamic Nodes

Select any node created for the dynamic chain to be deleted or the dynamic chain locator at the start joint, and click the 'Remove Dynamic' button under the Main section.

Commands:
from lpDynamicChains import *

dynChain = lpDynamicChain(node=node)
dynChain.delete()
del(dynChain)
Note: Removing dynamic nodes will also check for any unused nucleus nodes and remove them as well.

All nodes created for the dynamic chain to be deleted will be removed from the scene returning it to its original state before the creation of the dynamic chain.

Iterate Through All Dynamic Chain Nodes

Commands:
# create instance
dynChain = lpDynamicChain()
# create iterable for all dynamic nodes in the scene
dynChains = dynChain.Iter()
for d in dynChains:
    print d
Adding Collision Objects

Select the locator for the dynamic chain to be edited, shift select the geometry to be turned into a collision object and click the 'Create Collision Object' button under the Options section.

Geometry object will be turned into a passive nRigid object to interact with the dynamic joints. A nRigid node will be created and stored in an array attribute keeping track of all collider nodes interacting with it.

Creating nCache

Select the locator or multiple locators for the dynamic chain(s) to be cached and click the 'Cache Simulation' button under the Advanced -> nCache section.

The nCache option window will display so you can choose how to save out the cache file (or multiple files) for the dynamic simulations. For more information please refer to the Autodesk Maya Help Docs

Removing nCache

Select the locator for the dynamic chain to be cached and click the 'Delete Cache' button under Advanced -> nCache section.

Note: Removing dynamic nodes for a given dynamic chain will not delete the cache file on disk.

The Delete nCache option window will display. If there are multiple cache nodes for the dynamic chain, you will be able to select which ones to delete.

The option to delete or keep the cache files on disk will also be available.

Baking Simulation to Joints

Select the locator for the dynamic chain to be bake the simulation for and click the 'Bake Simulation' button under Advanced -> Animation

Utility Commands

In addition to the UI functions, you can also access utility commands available with the script. Below are some examples.

Get and Set the Active Nucleus
from lpDynamicChains import *

nucleus = getActiveNucleus()
setActiveNucleus(nucleus)

def getActiveNucleus():
    '''
    Query the active nucleus node
    
    Parameters:
        Nothing
    
    Returns:
        Name of active nucleus node
    '''

def setActiveNucleus(nucleus):
    '''
    Set the active nucleus node
    
    Parameters:
        nucleus        - Name of nucleus node to set as current active nucleus
    
    Returns:
        Nothing
    '''
Get Connected

Get commands to find the connected DynamicChain node or the nucleus node connected to the dynamicNode
from lpDynamicChains import *

# get selected object
obj = cmds.ls(sl=True)[0]
dNode = getConnectedDynamicChain(obj)
nucleus = getConnectedNucleusNode(dNode)

def getConnectedDynamicChain(node):
    '''
    Look for a valid DynamicChain node connected to the given node
    
    Parameters:
        node             - Name of the node to find connection to DynamicChain
    
    Returns:
        Name of DynamicChain node connected to given node
    '''

def getConnectedNucleusNode(node):
    ''' 
    Look for the nucleus node connected to the given node
    
    Parameters:
        node             - Name of the node to find connection to nucleus
    
    Returns:
        Name of the nucleus node connected to the given node
    '''
Checks

Check commands to verify if node is a DynamicChain node or node is a nucleus compatible nDynamics node.
from lpDynamicChains import *

# get selected object
obj = cmds.ls(sl=True)[0]
if isDynamicChain(obj):
    dynChain = lpDynamicChain(obj)

    # access attribute
    hairShape = dynChain.hairSystemShape[0]
    return isNType(hairShape, "hairSystem")

def isDynamicChain(node):
    '''
    Look for the identifier to check if node is 'dynamic' 
    
    Parameters:
        node         -  Name of node to check for 'nodeType'
    
    Returns:
        Boolean value if node is dynamic or not, True or False
    '''

def isNType(node, nodeType):
    '''
    Check if the given node is a nucleus compatible nDynamics node
    
    Parameters:
        node        - Name of node to check for 'NType' compatibility
        nodeType    - Node type to check
    
    Returns:
        Boolean value if node is compatible with nDynamics solver
    '''
Number of Colliders

Check how many collision objects are attached to the node
from lpDynamicChains import *

# get selected object
obj = cmds.ls(sl=True)[0]
if isDynamicChain(obj):
    numColliders = findNumberOfCollisionObjects(obj)

def findNumberOfCollisionObjects(node):
    '''
    Find the number of collision objects attached to the node
    
    Parameters:
        node         - Name of the node to check for collision objects
    
    Returns:
        Integer number of collision objects attached to the node
    '''
Delete Unused Nucleus Nodes

Check nucleus nodes in the scene for their connections and remove any that are not being used.
from lpDynamicChains import *

deletedNodes = deleteUnusedNucleusSolvers()

def deleteUnusedNucleusSolvers():
    '''
    Delete all nucleus nodes not being used
    
    Parameters:
        Nothing
    
    Returns:
        List of nucleus node deleted
    '''
Create and Connect

Creation and connection commands to create new nucleus nodes, nRigid nodes, connect DynamicChain node to nucleus or to connect a nRigid node to a nucleus.
from lpDynamicChains import *

# get selected objects
selected = cmds.ls(sl=True)[0]

dNode = selected[0]
mesh = selected[1]

nucleus = createNucleus()
if isDynamicChain(dNode):
    nucleus = connectToNucleus(dNode, nucleus)
nRigid = createNRigid(mesh, nucleus)
index = connectNRigidToNucleus(nRigid, nucleus)

def createNucleus(name="", setActive=True):
    '''
    Create nucleus node
    
    Parameters:
        name             - Name for the new nucleus node
        setActive        - Boolean to set the new nucleus as the current active nucleus
        
    Returns:
        Name of new nucleus node
    '''

def createNRigid(obj, nucleus=""):
    '''
    Create a nRigig node from the given obj
    
    Parameters:
        obj              - Name of the geo to create nRigid from
        nucleus          - Name of the nucleus to connect nRigid to
        
    Returns:
        Name of new nRigid node
    '''

def connectToNucleus(node, nucleus):
    '''
    Connect the given node to the nucleus node
    
    Parameters:
        node             - Name of the node to connect to the nucleus solver
        nucleus          - Name of nucleus solver to connect to
    
    Returns:
        Name of nucleus node (debug)
    '''

def connectNRigidToNucleus(nRigid, nucleus, newNucleus=True):
    '''
    Connect the given nRigid node to the nucleus node, maintaining prior connections to
    other nucleus nodes
    
    Parameters:
        nRigid         - Name of nRigid node to connect to nucleus
        nucleus        - Name of nucleus node to connect
        newNucleus     - Boolean to create a new nucleus node if the specified nucleus doesn't exist
    
    Returns:
        Integer index of next available passive nRigid for the given nucleus
    '''
Access connections

Command to get all nodes connected to custom node, like hairSystem, or the ikHandle. Access custom attributes through class _getattr_ method

from lpDynamicChains import *

# get selected object
obj = cmds.ls(sl=True)
if isDynamicChain(obj):
    dNode = lpDynamicChain(node=obj)
    nodes = dNode.listConnections()
# get hairSystemShape node
hairShape = dNode.hairSystemShape[0]

def listConnections(self):
    '''
    Wrapper to get all connections to the node to message attributes
        
    Parameters:
        Nothing
        
    Returns:
        List of connected nodes to all custom message attributes
    '''

2 comments:

  1. Olá Luiz Moreira, parabéns pelo excelente trabalho e por disponibilizar esses scripts =)

    Estou tentando colocar no Maya 2014 porém está dando esse erro:

    # Error: SyntaxError: file line 1: invalid syntax #

    Sabe o que pode ser?

    Obrigado!

    ReplyDelete
    Replies
    1. Ola Gustavo. Verifique primeiro que o script esta no folder certo.
      Na hora de chama-lo dentro do Maya, verifique tambem que esta chamando o script corretamente

      import lpDynamicChains
      reload(lpDynamicChains)
      lpDynamicChains.UI()

      Ele foi testado em versoes 2011 ate 2014, entao nao devera ter nenhum problema.

      Boa sorte! :)

      Delete