## Monday, 16 January 2012

### Pipeline Stuff

As we are starting the group project section of the Masters term I thought it would be a good idea to do some basic pipeline stuff.

This example is going to write some simulation data created in a C++ program and then we will write some simple Python scripts to load this data into Maya and Houdini.

The following video shows the program in action as well as the Maya and Houdini versions.

As this is an ad-hoc system the output file format is very simple, the C++ program writes out the following data :-

NumParticles 500
Frame 0
P0 64.301 -57.7284 49.8516
....
Frame 1
....

Where the particle data consists of The name of the Particle (in this case P0,P1....Pn) and the x,y,z positions of the particle per frame.

Maya Version
The maya version of the program will popup a dialog box to prompt the user to select an input file, it will then parse the file and create a locator for each of the particle names found.
Then for every frame a keyframe is created for the locator replicating the animation from the C++ simulation.
import maya.OpenMaya as OM
import maya.OpenMayaAnim as OMA
import maya.cmds as cmds

The code above loads in the maya elements we need, OpenMaya contains the core maya elements we need, the OpenMayaAnim namespace has the controls for all the animation transports such as setting the current frame and maya.cmds gives us access to all the maya cmds similar to the mel commands printed in the console when we generate things.

def createLocator(_name,_x,_y,_z) :
cmds.spaceLocator( name=_name)
cmds.move(_x,_y,_z)
cmds.setKeyframe()


The createLocator function creates a simple space locator then moves it to the current x,y,z position we then set the keyframe to make the initial key at the currently set frame (more on this later)

def moveLocator(_name,x,y,z) :
cmds.select(_name)
cmds.move(x,y,z)
cmds.setKeyframe()


The move locator function, first selects the locator (based on the name passed in), it then calles the move command which will move the currently selected object, finally we set the keyframe.
def importParticleFile() :
basicFilter = "*.out"

fileName=cmds.fileDialog2(caption="Please select file to import",fileFilter=basicFilter, fm=1)
if fileName[0] !=None :

file=open(str(fileName[0]))
frame=0
numParticles=0
#set to frame 0
animControl=OMA.MAnimControl()
animControl.setCurrentTime(OM.MTime(frame))

for line in file :
line=line.split(" ")
if line[0]=="NumParticles" :
numParticles=int(line[1])
elif line[0]=="Frame" :
frame=int(line[1])
animControl.setCurrentTime(OM.MTime(frame))
else :
name=line[0]
x=float(line[1])
y=float(line[2])
z=float(line[3])
if frame==0 :
#we need to create our initial locators
createLocator(name,x,y,z)
else :
moveLocator(name,x,y,z)
Houdini Version
The houdini version of the script will generate a null which is the houdini equivalent of a locator. By default a houdini null doesn't contain any geometry so we also need to parent this to some axis (houdini call these controls).

To start with we need to get the name of the file, this is done using the getAbsoluteFilename function described here http://jonmacey.blogspot.com/2011/01/houdini-python-ascode.html

def createNull(parent,_name,x,y,z) :
#create a null this will set loads of default values
null = parent.createNode("null", _name, run_init_scripts=False, load_contents=True)
# set the x,y,z values
null.parm("tx").set(x)
null.parm("ty").set(y)
null.parm("tz").set(z)
# now add a control to the null so we have something to visualise
# now grab the keyframe
setKey = hou.Keyframe()
# set to frame 0
setKey.setFrame(0)
# now key the tx/y and z values
setKey.setValue(x)
null.parm("tx").setKeyframe(setKey)
setKey.setValue(y)
null.parm("ty").setKeyframe(setKey)
setKey.setValue(z)
null.parm("tz").setKeyframe(setKey)
# now add to our network node

This function is passed the parent node, which in this case will be the houdini path "/obj/" from this we create a new node called a "null" and set it's parameters. In Houdini all parameters can be access using the parm(...) method of the object passing in the name of the parameter we wish to access, in this case "tx/ty/tz". Next we set the keyframes using the hou.keyframe class.
def moveNull(_name,frame,x,y,z) :
null=hou.node("/obj/"+_name)
setKey = hou.Keyframe()
setKey.setFrame(frame)
setKey.setValue(x)
null.parm("tx").setKeyframe(setKey)
setKey.setValue(y)
null.parm("ty").setKeyframe(setKey)
setKey.setValue(z)
null.parm("tz").setKeyframe(setKey)

In this function we grab the object by name then set the keyframes using the same method as above. Finally we are going to create our nodes and import the file, the file reading code is exactly the same, however we create a subNetwork first to make the houdini scene neater.
def importParticleFile() :

fileName=GetAbsoluteFileName("Select particle File","*.out",hou.fileType.Any)
# get the the object leve as our parent
if locals().get("hou_parent") is None:
parent = hou.node("/obj")

# make sure we got a filename
if fileName !=None :
## @brief we now copy this to a new string
subNetName=subNetName[1]

#open the file (could do a proper check here)
file=open(fileName)
frame=0
numParticles=0
# now process the file and create the nulls
for line in file :
line=line.split(" ")
if line[0]=="NumParticles" :
numParticles=int(line[1])
elif line[0]=="Frame" :
frame=int(line[1])
else :
name=line[0]
x=float(line[1])
y=float(line[2])
z=float(line[3])
if frame==0 :
#we need to create our initial locators
createNull(subNet,name,x,y,z)
else :
moveNull(subNetName+"/"+name,frame,x,y,z)


To grab the full code use the following

DemoProgram (requires NGL)
particles.out (the output of the program which the python files read)
Houdini Script
Maya Script