Stumbling Toward 'Awesomeness'

A Technical Art Blog

Thursday, April 12, 2018

Creating a Volume Slider in UE4 with Python


import unreal
from PySide import QtCore, QtGui, QtUiTools
class VolumeSlider(QtGui.QDialog):
    def __init__(self, parent=None):
        super(VolumeSlider, self).__init__(parent)
        self.setWindowTitle("Volume Slider")
        self.slider = QtGui.QSlider()
        layout = QtGui.QVBoxLayout()
        #load the master SoundClass
    def slider_fn(self):
        volume_float = self.slider.value()/100.00
APP = None
if not QtGui.QApplication.instance():
    APP = QtGui.QApplication(sys.argv)
main_window = VolumeSlider()
posted by Chris at 1:05 AM  

Friday, April 6, 2018

Vector Math Examples in UE4 Python


Creating Vectors


bbox = mesh.get_actor_bounds(False)


print v1


#(540.0733032226562, 32.02119445800781, 124.71086883544922)


my_vec3 = unreal.Vector(1,2,3)
Print my_vec3

Length / Distance / Magnitude


new_vec = v2-v1
Print new_vec.size()


print v1.dist(v2)



Using what we got, let’s play in the default scene:


for actor in static_meshes:
    mesh_component = actor.get_component_by_class(unreal.StaticMeshComponent)
    v1, v2 = mesh_component.get_local_bounds()
    if v1.dist(v2) < 150:
        print actor.get_name()




v1 = unreal.Vector(0,0,1)
v2 = unreal.Vector(0,1,1)
print v1 | v2
print v1 * v2


For the angle between, let’s import python’s math library:

import math
dot = v1|v2
print math.acos(dot) #returns 0.785398180512
print math.acos(dot) * 180 / math.pi
#returns 45.0000009806


dot = v1|v2
print unreal.MathLibrary.acos(dot) #returns 0.785398180512
print unreal.MathLibrary.acos(dot) * 180 / math.pi #returns 45.0000009806
print unreal.MathLibrary.radians_to_degrees(unreal.MathLibrary.acos(dot)) #returns: 45.0



fwd_vectors = {}
for actor in static_meshes:
    if 'Chair' in  actor.get_name():
        actor_vec = actor.get_actor_forward_vector()
        for stored_actor in fwd_vectors:
            if actor_vec | fwd_vectors[stored_actor] == 1.0:
                print actor.get_name(), 'is colinear to', stored_actor
        fwd_vectors[actor.get_name()] = actor_vec
#returns: Chair_dupe is colinear to Chair


posted by Chris at 1:23 AM  

Wednesday, July 5, 2017

Skin Weights Savior


Using GetWeights and SetWeights in the Maya Python API


posted by Chris at 11:20 PM  

Monday, November 21, 2016

The Eyes


Windows to the Soul


Talking About the Eyes



The Sclera – The ‘whites of your eyes’
The Cornea – This is the bulge over the iris



Eye Placement



Face It: A Visual Reference for Multi-ethnic Facial Modeling
Forensic Art and Illustration
Forensic Analysis of the Skull
Facial Geometry





tmp696744_thumb    tg-7-57c-modified    f2


Marius, Ryse 2011, Crytek

Abdenour Bachir, Ryse 2011, Crytek (click to enlarge)









Sometimes art direction would like ‘larger’ eyes, this is sometimes attempted by making the eyeball larger, but then it can feel weird when the eye is too large for the socket. This causes issues with the rotations of the eye. For reference, take a look at people who have been handed down a specific neanderthal gene for large eyes (or Axenfeld-Rieger syndrome), like the Ukrainian model Masha Tyelna. She has large eyeballs, but also the facial physiology to accept them:


Eye Movement


Range of Movement


Eye ‘Accommodation’ and Convergence


“Why Does My Character Look Cross-Eyed”?



Let’s go over some more terminology:

Pupillary Axis – A line drawn straight out the pupil.
Angle Kappa – the angle between the pupillary and visual axis.

The Hirschberg Test







“Why Don’t My Eyes Feel Real?”


Hanno Hagedorn, Crysis 2005, Crytek









Wetness Meniscus



Eye Rendering








This is an example of the iris displacement map ingame seen with a debug render view (Xbox One, 2011)

Abdenour Bachir, Ryse, Crytek 2011

Does the eye really need a corneal bulge?




Abdenour Bachir, Ryse 2012, Crytek

Abdenour Bachir, Ryse 2012, Crytek

Scanning/Acquiring Eyes





How You Review Eyes


posted by Chris at 2:01 AM  

Monday, September 26, 2016

Save/Load SkinWeights 125x Faster





#we import an xml parser that ships with python
import xml.etree.ElementTree
class SkinDeformerWeights(object):
    def __init__(self, path=None):
        self.path = path
        if self.path:


def parseFile(self, path):
    root = xml.etree.ElementTree.parse(path).getroot()
    #set the header info
    for atype in root.findall('headerInfo'):
        self.fileName = atype.get('fileName')
    for atype in root.findall('weights'):
        jnt = atype.get('source')
        shape = atype.get('shape')
        clusterName = atype.get('deformer')


class SkinnedShape(object):
        self.joints = joints
        self.shape = shape = skin
        self.verts = verts


def parseFile(self, path):
    root = xml.etree.ElementTree.parse(path).getroot()
    #set the header info
    for atype in root.findall('headerInfo'):
        self.fileName = atype.get('fileName')
    for atype in root.findall('weights'):
        jnt = atype.get('source')
        shape = atype.get('shape')
        clusterName = atype.get('deformer')
        if shape not in self.shapes.keys():
            s = self.shapes[shape]


def applyWeightInfo(self):
    for shape in self.shapes:
        #make a skincluster using the joints
        if cmds.objExists(shape):
            ss = self.shapes[shape]
            skinList = ss.joints
            cluster = cmds.skinCluster(, tsb=1)
            dir = self.path.replace(fname,'')


    t1 = time.time()
    #get skin clusters
    meshDict = {}
    for mesh in meshes:
        sc = mel.eval('findRelatedSkinCluster '+mesh)
        #not using shape atm, mesh instead
        msh =  cmds.listRelatives(mesh, shapes=1)
        if sc != '':
            meshDict[sc] = mesh
    dir = fpath.replace(fname,'')
    for skin in meshDict:
    elapsed = time.time()-t1


sdw = skinDeformerWeights()
>>>Exported skinWeights for 214 meshes in 2.433 seconds.


import os
path = "e:\\gadget\\"
files = 0
for file in os.listdir(path):
    if file.endswith(".skinWeights"):
        fpath = path + file
        sdw = skinDeformerWeights(path=fpath)
        files += 1
elapsed = time.time() - t1
>>> Loaded skinWeights for 214 meshes in 8.432 seconds.



posted by Chris at 1:27 AM  

Saturday, September 24, 2016

DeformerWeights Command, Cloaked Savior?











Export Deformer Weights: Broken and Backwards















Not Taking ‘Broken and Backwards’ for an Answer



<?xml version="1.0"?>









  • Exporting multiple skinClusters/shapes/deformers per XML file
  • Local and world space positions with a positional tolerance

“Someone is putting some thought into this”




The Documentation is a Lie.

So, first thing’s first, I did a speed test.

Importing with deformerWeights was about 125 times faster: Gold mine.


# Error: Invalid flag 'vc'
# Traceback (most recent call last):
# TypeError: Invalid flag 'vc' #







posted by Chris at 2:15 AM  

Wednesday, August 31, 2016

Calibrating the Alienware 13 R2 OLED Laptop















posted by Chris at 9:23 PM  

Saturday, May 7, 2016

Simple Runtime Rigging in UE4






Create a Look At Controller and integrate the blendspace:


posted by Chris at 7:40 AM  

Friday, October 17, 2014

Embedding Icons and Images In Python with XPM




Here’s an example XPM file:

/* XPM */
static char *heart_xpm[] = {
/* width height num_colors chars_per_pixel */
"7 6 2 1",
/* colors */
"` c None",
". c #e2385a",
/* pixels */


import sys
from PySide import QtGui, QtCore
def main():
    app = QtGui.QApplication(sys.argv)
    w = QtGui.QWidget()
    w.setWindowTitle('XPM Test')
    w.button = QtGui.QPushButton('XPM Bitmaps', w)
if __name__ == '__main__':



posted by Chris at 3:23 PM  

Thursday, October 16, 2014

Tracing and Visualizing Driven-Key Relationships



How The Mess is Made














Finding and traversing





  • animCurveUA – takes a double and outputs an angle
  • animCurveUT – takes a double and outputs a time


  • What is being driven by an attribute



The way I decided to go about it was:

  1. Find the driven-key curves
  7. Store all this as my sdk objects
  8. Loop across all my objects and generate the QTreeWidget


 #search down the dag for all future nodes
 #reverse the list order so that you get farthest first
 drivenAttr = None
 for node in futureNodes:
     if cmds.nodeType(node) == 'unitConversion':
     elif cmds.nodeType(node) == 'blendWeighted':
 if not drivenAttr:
 if drivenAttr:
     dk.drivenAttr = drivenAttr
     cmds.warning('No driven attr found for ' + c)



 What can you do?

Prune Unused Pasta


Pruned version of the DAG




def deleteUnusedDriverCurves():
        #delete unused driven curves


def deleteUnusedBlendNodes():
    #rewire blend nodes that aren't blending anything
    for blendNode in'blendWeighted'):
            curveOutput = None
            drivenInput = None
            #leap over the downstream unitConversion if it exists
            for conn in conns:
            if len(conns) == 1:
                drivenInput = conns[0]
            #connect the values, delete the blendWeighted
            cmds.connectAttr(curveOutput, drivenInput, force=1)


Here are the results running this code on an example rig:


Create a Tool To Track / Mirror / Select Driven Relationships



Write Your Own Driver Node







Give Autodesk Feedback!





posted by Chris at 3:16 PM  

Tuesday, September 30, 2014

Why Storing String Refs is Dangerous and Irresponsible








# Error: No object matches name: red_soft_turtle_hand
# Traceback (most recent call last):
#   File "", line 1, in 
# ValueError: No object matches name: red_soft_turtle_hand #

# Error: More than one object matches name: red_soft_turtle_hand
# Traceback (most recent call last):
#   File "", line 1, in 
# ValueError: More than one object matches name: red_soft_turtle_hand #



print hand_jnt
# >>: red_soft_turtle_hand1


To be super clear, this is more safe because:

  • Maya is resolving any name clashes for you automatically




Let’s look at our joint, its long path is:




  • “Oh, the hierarchy will never change”

The Maya DAG is the wild west.
You WILL have duplicate node names.
You WILL have hierarchy changes.
Professional tools don’t only work with a bunch of caveats,




Using The Maya Python API


import maya.OpenMaya as om
m_dg = om.MFnDagNode()
m_obj = m_dg.create("joint")
m_dagPath = om.MDagPath()
if m_obj.hasFn(om.MFn.kDagNode):
    om.MDagPath.getAPathTo( m_obj, m_dagPath )
    print m_dagPath.fullPathName()
# >>: |joint1

Using PyMel


myJoint = joint(name='doesnt_even_matter')
print myJoint.fullPath()


Using Message Attrs






posted by Chris at 3:28 PM  

Tuesday, August 26, 2014

Multi-Resolution Facial Rigging

At SIGGRAPH we discussed a bit about our facial pipeline that we haven’t talked about before. Namely, facial LODs and multi-platform facial rigging.

I would like to start by saying that we spent a _LOT_ of time thinking about facial levels of detail on Ryse, and put a lot of effort into the area. I know this is a long post, but it’s an important one.


Lowest Common Denominator

As the ‘next-generation’ seems to be largely defined by mult-platform titles it seems valuable to focus on ways to increase fidelity on next generation hardware while still being able to target older hardware specs. That said, I have yet to see a pipeline to do this. Most next gen games have skeletons and animations limited by the lowest common denominator of the last generation, often Playstation 3.

When you wonder why your awesome next gen game doesn’t seem to have character models and animation like next-gen only titles, this is why.

It’s very easy to increase texture resolution by having a pipeline where you author high and bake to lower targets.  It’s more complicated to author meshes high and publish to lower targets, we did this on Crysis 1 and 2, High end PC saw higher mesh resolution characters than Xbox 360. I would say it’s the hardest to make rigs, deformers, and animations for a high spec hardware target and create a process to publish lower fidelity versions. No one wants to have different character skeletons on each hardware platform.


You Deserve an Explanation

When we released the specs of our faces, people understandably were a bit taken aback.  Why on earth would you need 250 blendshapes if you have 260 joints? This above is actually a slide from our asset creation SIGGRAPH course that resonated very well with the audience.

Let’s take a look at some goals:

  1. Cut-scene fidelity in gameplay at any time- no cut-scene rigs
  2. Up to 70 characters on screen
  3. Able to run on multiple hardware specs

The only way to achieve the first two is through a very aggressive and granular level of detail (LOD) scheme. Once having that LOD system in place, the third item will come for free, as it did on our previous titles. However, as we have LODed meshes and materials, we had never LODed rigs.

On a feature film, we wouldn’t use joints, we would have a largely blendshape-only face.

But this doesn’t LOD well, we need to be able to strip out facial complexity in the distance and on other platforms.

Facial Level of Detail

So to achieve these goals, we must aggressively LOD our character faces.

Let’s generate some new goals:

  • Improve LOD system to allow the swapping or culling of skinned meshes per-mesh, each at hand-tailored distances per-character instance
  • Not only swap meshes, but skinning algorithms, materials, cull blendshapes, etc..
  • One skeleton – all levels of detail stored in one nested hierarchy, disable/reveal joints at different LOD levels, as I mention above, no one wants multiple skeletons
  • One animation set – drives all layers of detail because it’s one hierarchy, only the enabled joints receive animation
  • All facial animations shareable between characters
  • Faces snapped onto bodies at runtime – “Cry parent constraint” of sorts snaps head, neck, spine4, clavs, and upper arms of facial rig to body, allowing dynamic LODing of face irrespective of body.


One Hierarchy to Rule them All

Before going into the meshes, skinning algorithms, culling, etc.. it’s important to understand the hierarchy of the face. At any given mesh LOD level, there are many joints that are not skinned. Above you see three layers of joints, 9 LOD0, 3 LOD1, and 1 LOD2.

To use a single hierarchy, but have it drive meshes at different levels, you need to accomplish the following:

  • Make sure you have three layers that can drive different facial LODs, we had something like 260/70/15 on Ryse.
  • Each layer must be driven, and able to deform that LOD alone. Meaning when creating rig logic, you must start from the highest LOD and move down the chain. The LOD0 joints above would only be responsible for skinning the details of the face at LOD0, their gross movement comes from their parent, LOD1.

Here you can see the Marius example video from our slides. Notice the ORANGE joints are responsible for gross movement and the YELLOW or GREEN leaf joints just add detail.



Why blendshapes? Isn’t 260 joints enough?

The facial hierarchy and rig is consistent between all characters. The rig logic that drives those joints is changed and tweaked, the skinning is tweaked, but no two faces are identical. the blendshapes serve two main purposes:

1) Get the joint rig back on scan. Whatever the delta from the joint rig to the scan data that is associated with that solved pose from the headcam data, bridge that. This means fat around Nero’s neck, bags under his eyes, his eyebrow region, etc.

2) Add volume where it’s lost due to joint skinning. Areas like the lips, the cheeks, rig functions like lips together, sticky lips, etc, require blendshapes.


Look at the image above, there just aren’t enough joints in the brow to catch that micro-expression on Nero’s face. It comes through with the blendshape, and he goes from looking like you kicked his dog, to his accusatory surprise when he figures out that you are Damoclese.

A Look Under the Hood: Ryse Facial LODing

Thanks to the hard work of graphics engineer Jerome Charles we were able to granularly LOD our faces. These values are from your buddy Vitallion, who was a hero and could be a bit less aggressive. Many of the barbarians you fight en masse blew through all their blendshapes in 2m not 4m.

Assets / Technologies (LOD)
CPU skinning, 8 inf, 260 joints, 230 blendshapes, tangent update, 5k  tris across multiple meshes 0-4m
CPU skinning, 8 inf, 260 joints, 3-5k across multiple meshes with small face parts culled 4-7m
GPU skinning, 4 inf, 70 joints, 2k mesh with integrated eyes 7-10m
GPU skinning , 4 inf, <10 joints, <1k mesh 10m+


Here’s a different table showing the face mesh parts that we culled and when:

Distance Face parts
4m Eyebrow meshes replaced, baked into facial texture
3m Eyelash geometry culled
3m Eye AO ‘overlay’ layer culled
4m Eye balls removed, replaced with baked in eyes in head mesh
2m Eye ‘water’ miniscus culled
3m Eye tearduct culled
3m Teeth swapped for built-in mesh
3m Tongue swapped for built-in mesh

Why isn’t this standard?

Because it’s very difficult and very complicated, there aren’t so many people in that can pull something like this off. On Ryse we partnered with my friend Vlad at 3Lateral, after 4 months working on the initial Marius facial prototype, he and his team were able to deliver 23 more facial rigs at the same fidelity in just under three months!

But also, there’s the whole discussion about whether the time and effort spent on that last 5% really pays off in the end. Why not just use PS3 facial rigs on all platforms and spend a little more on marketing? It’s happening! And those guys probably aren’t going bankrupt any time soon..  ¬.¬

I am insanely proud of what the team accomplished on Ryse. Facial rigging is nothing without a great bunch of artists, programmers, animators, etc. Here’s some good moments where the performances really come through, these are all the in-game meshes and rigs:

DISCLAIMER: All of the info above and more is publicly available in our SIGGRAPH 2014 course notes.

posted by Chris at 4:40 AM  

Thursday, August 21, 2014

Adding Sublime ‘Build’ Support for KL


I have been dabbling with KL and that Fabric guys have some great introduction videos [here]. They have worked hard on some Sublime integration/highlighting, which you can find on GitHub [here].

While following these intro tutorials, instead of popping back and forth to your command line, you can actually compile/run your code in Sublime and see the results. To do this go to Preferences>Browse Packages… Then open that Sublime-KL folder, and inside create a new file called ‘KL.sublime-build‘, the contents of which should be:

"cmd": ["kl", "$file"]

Then just select KL from the ‘Build System’ menu as shown above, now press CTRL+B and it will show results inside the Sublime console!

posted by Chris at 2:13 AM  

Monday, June 30, 2014

Wasted Time, Sunken Cost, and Working In a Team



Let’s say that you want to do something, like watch a movie. When you arrive and open your wallet to purchase a 10 dollar ticket, you notice you have lost a 10 dollar bill, the majority of people buy a movie ticket anyway (88%).

Let’s examine a slightly different situation, where you arrive at the theater, but have misplaced your ticket, would you go buy another? Studies show that a majority of people (54%) would not re-purchase a ticket and watch the film. The situations are the same, but in the first, you lost 10 dollars, it wasn’t associated with the movie, in the second, you lost your ticket, 10 dollars that was specifically allotted to that task, and loss sucks.

This is a great example of the Sunk Cost Fallacy. Kahneman and Tversky are two researchers who have spent a lot of their careers looking at loss aversion and decision theory. The bottom line is, it’s human nature that the more you invest in something, the harder it is to abandon it. As a Technical Artist, you will find yourself in a position where you are the decision-maker, don’t let your ape ancestors trick you into making a poor decision.

..since all decisions involve uncertainty about the future the human brain you use to make decisions has evolved an automatic and unconscious system for judging how to proceed when a potential for loss arises. Kahneman says organisms that placed more urgency on avoiding threats than they did on maximizing opportunities were more likely to pass on their genes. So, over time, the prospect of losses has become a more powerful motivator on your behavior than the promise of gains. Whenever possible, you try to avoid losses of any kind, and when comparing losses to gains you don’t treat them equally. – You Are Not So Smart



As a Technical Artist in a position to lead or direct a team, you will often be the person signing off tools or features you and your team have requested. How many times have you been in the following situation:

A feature or tool is requested. Joe, a genius ‘lone wolf’ programmer receives the task, he is briefed and told to update the customers periodically or ask them in the case he needs any clarification. Now, sometimes what happens is what my brother likes to call ‘The Grand Reveal’. It’s where, for whatever reason, Joe sits in his corner working hard on a task, not involving anyone, and on the last day he valiantly returns from the mountain top and presents something that is unfortunately neither really what was requested or needed.

In this situation, you get together with his Lead and point out that what is delivered is not what was requested, he will more than likely reply “But Joe spent four weeks on this! Surely we can just use this until Joe can later rework it?”

No, you really can’t. Joe needs to know he works on a team, that people rely on his work. Nothing gets people to deliver what they are supposed to next time like being forced to redo their work. I guarantee you next time Joe will be at your teams desks any time he has a question about the tool or feature he is working on. You know the needs of your project or team, it’s important that you do not compromise those because someone wasted time running off in the wrong direction or has problems working in a team.

I’m sure Joe is a really smart guy, but he’s also now four weeks behind.



– Ted Engstrom


A Feature Brief is a one page document that serves as a contract between the person requesting a feature and the one implementing it. My Feature Briefs outline three main things:

  1. A short description of the feature or tool
  2. It’s function – how does it work, what are the expected results
  3. It’s justification – why is it needed? What is the problem that is needed to be solved.

It’s important that work not begin until both parties agree on all terminology and requests in the feature brief -again, treat it as a contract. And it’s worth mentioning that Feature Briefs aren’t always needed, but they’re a great way to make sure that goals are clearly defined, everyone’s on the same page, and leave zero wiggle room for interpretation. Here is an example Feature Brief for the first Pose Driver we developed at Crytek.


Work with Joe’s Lead or Manager to set up ‘Gates’, it’s important that he get the feedback as early as possible if he’s going down the wrong track. I understand that bothering people halfway through a task may not be kosher in Agile development, but never just assume that someone will deliver what you need on the last day of a sprint.


Break down the goal into tasks whose progress can be reviewed, it’s important that you, the primary stakeholder, are involved in signing off these gates. Any gated process is only as useful as the person signing off the work, the above comic may seem harsh, but it’s vitally important that the stakeholder is involved in reviewing work. Joe’s manager has a vested interest in Joe moving on to his next tasks, you have a vested interest in the tool or feature being what your team, the company, and whomever else needs.

Perhaps Joe will first present an outline, or maybe after taking a detailed look at the problem, Joe has a better solution he would like to pitch and you all agree to change the Feature Brief. The next gate would be to evaluate a working prototype. You probably know a lot about the feature as you requested it –are there any gotchas, any things that just wont work or have been overlooked? Last is usually a more polished implementation and a user interface.



If Joe has a Lead or Manager, check with them, no need to bother Joe, that’s what the others are there for. If you ask them details about where he’s at, more often than not they will offer for you to speak with him or get you an update. It’s just important to understand that if Joe delivers something that’s not what you need, it’s your fault too. Joe is only a genius in the trenches, it’s your job to make sure that he’s not barking up the wrong tree and wasting company time. It may be tempting, but never allow these guys to shoot themselves in the foot, if you think he’s not on the right track, you need to do something about it. Even without gated development, frequently check the progress of items that are important to you. The atmosphere should be that of a relay race, you are ready to accept the baton, and it needs to be what was agreed upon or you all fail.



More-often-than-not, whatever Joe did in the time he had allotted is going to be what you ship with. If you agree he will return to address the issues later, make sure that when this doesn’t happen, your team can still be successful. Nothing should be higher priority than a mistake that holds up another team. I am sure you feel this way when it’s your team, when a rig update from last week is causing all gun holster keys to be lost on animation export, it’s important to address that before new work. The same can be said for Joe’s work, don’t make it personal, he is now behind, your guys are relying on him, and it should be high priority for him to deliver the agreed upon work.

posted by Chris at 12:02 AM  

Sunday, May 11, 2014

Maya: Vector Math by Example


This post is about how to use vector math and trigonometric functions in Maya, it is not a linear algebra or vector math course, it should give you what you need to follow along in Maya while you learn with online materials. Kahn Academy is a great online learning resource for math, and Mathematics for Computer Graphics, and Linear Algebra and its Applications are very good books. Gilbert Strang, the Author of Linear Algebra, has his entire MIT Linear Algebra course lectures here in video form. Also, Volume 2 of Complete Maya Programming has some vector math examples in MEL and C++.



Think of the white vector above as a movement. It does have three scalar values (ax, ay, az), sure, but do not think of a vector as a point or a position. When you see a vector, I believe it helps to imagine it as a movement from 0,0,0 – an origin. We don’t know where it started, we only know the movement.

A vector has been normalized, or is considered a unit vector, when it’s length is one. This is achieved by dividing each component by the length.


There are many Python libraries dedicated to vector math, but none ship with Python itself. I have tried numPy, then pyEuclid, and finally piMath. It can definitely be a benefit to load the same vector class across multiple apps like Maya, MotionBuilder, etc.. But, I used those in a time when MotionBuilder had no vector class, and before Maya had the API. Today, I use the vector class built into the Maya Python API (2.0), which wraps the underlying Maya C++ code: MVector

I had to call out 2.0 above, as those of you using the old API, you have to ‘cast’ your vectors to/from, meaning that classes like MVector (Maya’s vector class) don’t accept python objects like lists or tuples, this is still the case with the 2014 SWIG implementation of the default API, but not API 2.0. One solution is to override the MVector class in a way that it accepts a Python lists and tuples, essentially automatically casting it for you:

class MVector(om.MVector):
    def __init__(self, *args):
        if( issubclass, args, list ) and len(args[0])== 3:
            om.MVector.__init__(self, args[0][0], args[0][1], args[0][2])
            om.MVector.__init__(self, args)

But that aside, just use Maya Python API 2.0:

#import API 2.0
import maya.api.OpenMaya as om
#import old API
import maya.OpenMaya as om_old



Let’s first create two cubes, and move them

import maya.cmds as cmds
import maya.api.OpenMaya as om
cube1, cube2 = cmds.polyCube()[0], cmds.polyCube()[0]
cmds.xform(cube2, t=(1,2,3))
cmds.xform(cube1, t=(3,5,2))

Let’s get the translation of each, and store those as MVectors

t1, t2 = cmds.xform(cube1, t=1, q=1), cmds.xform(cube2, t=1, q=1)
print t1,t2
v1, v2 = om.MVector(t1), om.MVector(t2)
print v1, v2

This will return the translation in the form [x, y, z], and also the MVector, which will print: (x, y, z), and in the old API: <__main__.MVector; proxy of <Swig Object of type ‘MVector *’ at 0x000000002941D2D0> >. This is a SWIG wrapped C++ object, API 2.0 prints the vector.

Note: I just told you to think of vectors as a movement, and not as a position, and the first example I give stores translation in a vector. Maybe not the best, but remember this translation, is really being stored as a movement in space from an origin.

So let’s start doing stuff and things.


We have two translations, both stored as vectors, let’s get the distance between them, to do this, we want to make a new vector that describes a ray from one location to the other and then find it’s length, or magnitude. To do this we subtract each component of v1 from v2:

v = v2-v1
print v

This results in ‘-2.0 -3.0 1.0’.

To get the length of the vector we actually get the square root of the sum of x,y,and z squared sqrt(x^2+y^2+z^2), but as we haven’t covered the math module yet, let’s just ask the MVector for the ‘length’:

print om.MVector(v2-v1).length()

This will return 3.74165738677, which, if you snap a measure tool on the cubes, you can verify:


Use Case: Distance Check

As every joint in a hierarchy is in it’s parent space, a joint’s ‘magnitude’ is it’s length. Let’s create a lot of joints, then select them by joint length.

import maya.cmds as cmds
import random as r
import maya.api.OpenMaya as om
root = cmds.joint()
jnts = []
for i in range(0, 2000):
    jnt = cmds.joint()
    trans = (r.randrange(-100,100), r.randrange(-100,100), r.randrange(-100,100))
    cmds.xform(jnt, t=trans)
cmds.parent(jnts, root)


So we’ve created this cloud of joints, but let’s just select those joints with a joint length of less than 50.

sel = []
for jnt in jnts:
    v = om.MVector(cmds.xform(jnt, t=1, q=1))
    if v.length() < 50: sel.append(jnt)



The dot product is a scalar value obtained by performing a specific operation on two vector components. This doesn’t make much sense, so I will tell you that the dot product is extremely useful in finding the angle between two vectors, or checking which general direction something is pointing.

dot = v1*v2
print dot

USE CASE: Direction Test


The dot product of two normalized vectors will always be between -1.0 and 1.0, if the dot product is greater than zero, the vectors are pointing in the same general direction, zero means they are perpendicular, less than zero means opposite directions. So let’s loop through our joints and select those that are facing the x direction:

sel = []
for jnt in jnts:
    v = om.MVector(cmds.xform(jnt, t=1, q=1)).normal()
    dot = v*om.MVector([1,0,0])
    if dot > 0.7: sel.append(jnt)

USE CASE: Test World Colinearity

This one comes from last week in the office, one of my guys wanted to know how to check which way in the world something was facing. I believe it was to derive some information from arbitrary skeletons. This builds on the above by getting each vector of a node in world space.

def getLocalVecToWorldSpace(node, vec=om.MVector.kXaxisVector):
    matrix = om.MGlobal.getSelectionListByName(node).getDagPath(0).inclusiveMatrix()
    vec = (vec * matrix).normal()
    return vec
def axisVectorColinearity(node, vec):
    vec = om.MVector(vec)
    x = getLocalVecToWorldSpace(node, vec=om.MVector.kXaxisVector)
    y = getLocalVecToWorldSpace(node, vec=om.MVector.kYaxisVector)
    z = getLocalVecToWorldSpace(node, vec=om.MVector.kZaxisVector)
    #return the dot products
    return {'x': vec*x, 'y':vec*y, 'z':vec*z}
jnt = cmds.joint()
print axisVectorColinearity(jnt, [0,0,1])

You can rotate the joint around and you will see which axis is most closely pointing to the world space vector you have given as an input.

USE CASE: Angle Between Vectors


When working with unit vectors, we can get the arc cosine of a dot product to derive the angle between the two vectors, but this requires trigonometric functions, which are not available in our vector class, for this we must import the math module. Scratching the code above, let’s find the angle between two joints:

import maya.cmds as cmds
import maya.api.OpenMaya as om
import math
jnt1 = cmds.joint()
jnt2 = cmds.joint()
cmds.xform(jnt2, t=(0,0,10))
cmds.xform(jnt1, t=(10,0,0))
root = cmds.joint()
cmds.parent([jnt1, jnt2], root)
v1 = om.MVector(cmds.xform(jnt1, t=1, q=1)).normal()
v2 = om.MVector(cmds.xform(jnt2, t=1, q=1)).normal()
dot = v1*v2
print dot
print math.acos(dot)
print math.acos(dot) * 180 / math.pi

So at the end here, the arc Cosine of the dot product returns the angle in radians (1.57079632679), which we convert to degrees by multiplying it by 180 and dividing by pi (90.0). To check your work, there is no angle tool in Maya, but you can create a circle shape and set the sweep degrees to your result.

Now that you know how to convert radians to an angle, if you store the result of the above in an MAngle class, you can ask for it however you like:

print om.MAngle(math.acos(dot)).asDegrees()

Now that you know how to do this, there is an even easier, using the angle function of the MVector class, you can ask it the angle given a second vector:

print v1.angle(v2)

There are also useful attributes v1.rotateBy(r,r,r) for an offset and v1.rotateTo(v2). I say (r,r,r) in my example, but the rotateBy attr takes angles or radians.

CHALLENGE: Can you write your own rad_to_deg and deg_to_rad utility methods?

USE CASE: Orient-Driver

Moving along, let’s apply these concepts to something more interesting. Let’s drive a blendshape based on orientation of a joint. Since the dot product is a scalar value, we can pipe this right into a blendshape, when the dot product is 1.0, we know that the orientations match, when it’s 0, we know they are perpendicular.


We will use a locator constrained to the child to help in deriving a vector. The fourByFourMatrix stores the original position of the locator. I tried using the holdMatrix node, which should store a cached version of the original locator matrix, but it kept updating. (?) We use the vectorProduct node in ‘dot product’ mode to get the dot product of the original vector and the current vector of the joint. We then pipe this value into the weight of the blendshape node.

Now, this simple example doesn’t take twist into account, and we aren’t setting a falloff or cone, the falloff will be 1.0 when the vectors align and the blendshape is on 100% and 0.0, when they’re perpendicular and the blendshape will be on 0%. I also don’t clamp the dot product, so the blendshape input can go to -1.


The cross product results in a vector that is perpendicular to two vectors. Generally you would do (v1.y*v2.z-v1.z*v2.y, v1.z*v2.x-v1.x*v2.z, v1.x*v2.y-v1.y*v2.x), ut luckily, the vector class manages this for us by using the ‘^’ symbol:

cross = v1^v2
print cross

USE CASE: Building a coordinate frame


If we get the cross product of v1^v2 above, and use this vector to now cross (v1 x v2)x v1, we will now have a third perpendicular vector to build a coordinate system or ‘orthonormal basis’. A useful example would be aligning a node to a nurbs curve using the pointOnCurveInfo node.


In the example above, we are using two cross products to build a matrix from the tangent of the pointOnCurveInfo and it’s position, then decomposing this matrix to set the orientation and position of a locator.

Many people put content like this behind a paywall.
If you found this useful, please consider buying me a beer.

posted by Chris at 11:49 PM  

Monday, February 11, 2013

Object Oriented Python in Maya Pt. 1

I have written many tools at different companies, I taught myself, and don’t have a CS degree. I enjoy shot-sculpting, skinning, and have been known to tweak parameters of on-screen visuals for hours; I don’t consider myself a ‘coder’; still can’t allocate my own memory.  I feel I haven’t really used OOP from an architecture standpoint. So I bought an OOP book, and set out on a journey of self improvement.

‘OOP’ In Maya

In Maya, you often use DG nodes as ‘objects’. At Crytek we have our own modular nodes that create meta-frameworks encapsulating the character pipeline at multiple levels (characters, characterParts, and rigParts). Without knowing it, we were using Object Oriented Analysis when designing our frameworks, and even had some charts that look quite a bit like UML. DG node networks are connected often with message nodes, this is akin to a pointer to the object in memory, whereas with a python ‘object’ I felt it could always easily lose it’s mapping to the scene.

It is possible now with the OpenMaya C++ API to store a pointer to the DG node in memory and just request the full dag path any time you want it, also PyMel objects are Python classes and link to the DG node even when the string name changes.

“John is 47 Years Old and 6 Feet Tall”

Classes always seemed great for times when I had a bunch of data objects, the classic uses are books in a library, or customers: John is 47 years old and likes the color purple. Awesome. However, in Maya, all our data is in nodes already, and those nodes have attributes, those attributes serialize into a Maya file when I save: so I never really felt the need to use classes.

Although, all this ‘getting’, ‘setting’ and ‘listing’ really grows tiresome, even when you have custom methods to do it fairly easily.

It was difficult to find any really useful examples of OOP with classes in Maya. Most of our code is for ‘constructing’: building a rig, building a window, etc. Code that runs in a linear fashion and does ‘stuff’. There’s no huge architecture, the architecture is Maya itself.

Class Warfare

I wanted to package my information in classes and pass that back and forth in a more elegant way –at all times, not just while constructing things. So for classes to be useful to me, I needed them to synchronously exist with DG nodes.

I also didn’t want to have to get and set the information when syncing the classes with DG nodes, that kind of defeats the purpose of Python classes IMO.

Any time I opened a tool I would ‘wrap’ DG nodes in classes that harnessed the power of Python and OOP. To do this meant diving into more of the deep end, but since that was what was useful to me, that’s what I want to talk about here.

To demonstrate, let’s construct this example:

#the setup
loc = cmds.spaceLocator()
cons = [[0],[0]]
meshes = [cmds.sphere()[0], cmds.sphere()[0], cmds.sphere()[0]]
cmds.addAttr(loc, sn='controllers', at='message')
cmds.addAttr(cons, sn='rigging', at='message')
for con in cons: cmds.connectAttr(loc[0] + '.controllers', con + '.rigging')
cmds.addAttr(loc, sn='rendermeshes', at='message')
cmds.addAttr(meshes, sn='rendermesh', at='message')
for mesh in meshes: cmds.connectAttr(loc[0] + '.rendermeshes', mesh + '.rendermesh')

So now we have this little node network:


Now if I wanted to wrap this network in a class. We are going to use @property to give us the functionality of an attribute, but really a method that runs to return us a value (from the DG node) when the ‘attribute’ is queried. I believe using properties is key to harnessing the power of classes in Maya.

class GameThing(object):
	def __init__(self, node):
		self.node = node
	def controllers(self):
		return cmds.listConnections(self.node + ".controllers")

So now we can query the ‘controllers’ attribute/property, and it returns our controllers:

test = GameThing(loc)
print test.controllers
##>>[u'nurbsCircle2', u'nurbsCircle1']

Next up, we add a setter, which runs code when you set a property ‘attribute’:

class GameThing(object):
	def __init__(self, node):
		self.node = node
	def controllers(self):
		return cmds.listConnections(self.node + ".controllers")
	def controllers(self, cons):
		#disconnect existing controller connections
		for con in cmds.listConnections(self.node + '.controllers'):
			cmds.disconnectAttr(self.node + '.controllers', con + '.rigging')
		for con in cons:
			if cmds.objExists(con):
				if not cmds.attributeQuery('rigging', n=con, ex=1):
					cmds.addAttr(con, longName='rigging', attributeType='message', s=1)
				cmds.connectAttr((self.node + '.controllers'), (con + '.rigging'), f=1)
				cmds.error(con + ' does not exist!')

So now when we set the ‘controllers’ attribute/property, it runs a method that blows away all current message connections and adds new ones connecting your cons:

test = GameThing(loc)
print test.controllers
##>>[u'nurbsCircle2', u'nurbsCircle1']
test.controllers = [cmds.nurbsSquare()[0]]
print test.controllers

To me, something like properties makes classes infinitely more useful in Maya. For a short time we tried to engineer a DG node at Crytek that when an attr changed, could eval a string with a similar name on the node. This is essentially what a property can do, and it’s pretty powerful. Take a moment to look through code of some of the real ‘heavy lifters’ in the field, like zooToolBox, and you’ll see @property all over the place.

I hope you found this as useful as I would have.

posted by Chris at 1:03 AM  

Friday, January 18, 2013

Moving to ‘Physically-Based’ Shading


At the SIGGRAPH Autodesk User Group we spoke a lot about our character technology and switch to Maya. One area that we haven’t spoken so much about is next-gen updates to our shading and material pipeline, however Nicolas and I have an interview out in Making Games where we talk about that in detail publicly for the first time, so I can mention it here. One of the reasons we have really focused on character technology is that it touches so many departments and is a very difficult issue to crack, at Crytek we have a strong history of lighting and rendering.

What is ‘Physically-Based’ Shading?

The first time I ever encountered a physically-based pipeline was when working at ILM. The guys had gotten tired of having to create different light setups and materials per shot or per sequence. Moving to a more physically-based shading model would mean that we could not waste so much time re-lighting and tweaking materials, but also get a more natural, better initial result -quicker. [Ben Snow’s 2010 PBR SIGGRAPH Course Slides]


image credit:

A physically based shading model reacts much more like real world light simulation, one of the biggest differences is that the amount of reflected light can never be more than the incoming amount that hit the surface, older lighting models tended to have overly bright and overly broad specular highlights. With the Lambert/Blinn-Phong model it was possible to have many situations where a material emitted more light than it received. An interesting caveat of physically-based shading is that the user no longer has control over the specular response (more under ‘Difficult Transition’ below). Because the way light behaves is much more realistic and natural, materials authored for this shading model work equally well in all lighting environments.

Geek Stuff:‘Energy conservation’ is a term that you might hear often used in conjunction with physically-based lighting, here’s a quote from the SIGGRAPH ’96 course notes that I always thought was a perfect explanation of reflected diffuse and specular energy:

“When light hits an object, the energy is reflected as one of two components; the specular component (the shiny highlight) and the diffuse (the color of the object). The relationship of these two components is what defines what kind of material the object is. These two kinds of energy make up the 100% of light reflected off an object. If 95% of it is diffuse energy, then the remaining 5% is specular energy. When the specularity increases, the diffuse component drops, and vice versa. A ping pong ball is considered to be a very diffuse object, with very little specularity and lots of diffuse, and a mirror is thought of as having a very high specularity, and almost no diffuse.”


It’s important to understand that everything is a hack, whether it’s V-Ray or a game engine, we are just talking about different levels of hackery. Game engines often take the cake for approximations and hacks, one of my guys once said ‘Some people just remove spec maps from their pipeline and all the sudden they’re ‘physically-based”. It’s not just the way our renderers simulate light that is an approximation, but it’s important to remember that we feed the shading model with physically plausible data as well, when you make an asset, you are making a material that is trying to mimic certain physical characteristics.


Once physics get involved, you can cheat much less, and in film we cheeeeeaaat. Big time. Ben Snow, the VFX Supe who ushered in the change to a physically-based pipeline at ILM was quoted in VFXPro as saying: “The move to the new [pipeline] did spark somewhat of a holy war at ILM.” I mentioned before that the artist loses control of the specular response, in general, artists don’t like losing control, or adopting new ways of doing things.


Aside from the more natural lighting and rendering, in an environment where the player determines the camera, and often the lighting, it’s important that materials work under all possible lighting scenarios. As the product Manager of Cinebox, I was constantly having our renderer compared to Mental Ray, PRMAN and others, the team added BRDF support and paved the way for physically-based rendering which we hope to ship in 2013 with Ryse.


General Overview for Artists

At Crytek, we have always added great rendering features, but never really took a hard focus on consistency in shading and lighting. Like ILM in my example above, we often tweaked materials for the lighting environment they were to be placed in.


Before we start talking about the different maps and material properties, you should know that in a physically-based pipeline you will have two slightly different workflows, one for metals, and one for non-metals. This is more about creating materials that have physically plausible values.


  • The specular color for metal should always be above sRGB 180
  • Metal can have colored specular highlights (for gold and copper for example)
  • Metal has a black or very dark diffuse color, because metals absorb all light that enters underneath the surface, they have no ‘diffuse reflection’


  • A good clean diffuse map is required



At Crytek, we call the map that determines the roughness the ‘gloss map’, it’s actually the inverse roughness, but we found this easier to author. This is by far one of the most important maps as it determines the size and intensity of specular highlights, but also the contrast of the cube map reflection as you see above.  A good detail normal map can make a surface feel like it has a certain ‘roughness’, but you should start thinking about the gloss map as adding a ‘microscale roughness’. Look above at how as the roughness increases, as does the breadth of the specular highlight. Here is an example from our CryENGINE documentation that was written for Ryse:

click to enlarge

click to enlarge

click to enlarge

click to enlarge


Your diffuse map should be a texture with no lighting information at all. Think a light with a value of ‘100’ shining directly onto a polygon with your texture. There should be no shadow or AO information in your diffuse map. As stated above, a metal should have a completely black diffuse color.

Geek Stuff: Diffuse can also be reffered to as ‘albedo‘, the albedo is the measure of diffuse reflectivity. This term is primarily used to scare artists.


As previously discussed, non-metals should only have monochrome/gray-scale specular color maps. Specular color is a real-world physical value and your map should be basically flat color, you should use existing values and not induce noise or variation, the spec color map is not a place to be artistic, it stores real-world values. You can find many tables online that have plausible color values for specular color, here is an example:

Material sRGB Color Linear (Blend Layer)
Water 38 38 38 0.02
Skin 51 51 51 0.03
Hair 65 65 65 0.05
Plastic / Glass (Low) 53 53 53 0.03
Plastic High 61 61 61 0.05
Glass (High) / Ruby 79 79 79 0.08
Diamond 115 115 115 0.17
Iron 196 199 199 0.57
Copper 250 209 194 N/A
Gold 255 219 145 N/A
Aluminum 245 245 247 0.91
Silver 250 247 242 N/A

Geek Stuff: SPECULAR IS EVERYWHERE: In 2010, John Hable did a great post showing the specular characteristics of a cotton t-shirt and other materials that you wouldn’t usually consider having specular.


Here you can see the maps that generate this worn, oxidized lion sculpture.


click to enlarge




See above how there are no variations in the specular color map? See how the copper items on the left have a black diffuse texture? Notice there is no variation in the solid colors of the specular color maps.

SETTING UP PHOTOSHOP color_settings In order to create assets properly, we need to set up our content creation software properly, in this case: Photoshop. If you go to Edit>Color Settings… Set the dialog like the above. It’s important that you author textures in sRGB

Geek Stuff: We author in sRGB because it gives us more precision in darker colors, and reduces banding artifacts. The eye has 4.5 million cones that can perceive color, but 90 million rods that perceive luminance changes. Humans are much more perceptive to contrast changes than color changes!

Taking the Leap: Tips for Leads and Directors

New technologies that require paradigm shifts in how people work or how they think about reaching an end artistic result can be difficult to integrate into a pipeline. At Crytek I am the Lead/Director in charge of the team that is making that initial shift to physically-based lighting, I also lead the reference trip, and managed the hardware requests to get key artists on calibrated wide gamut display devices. I am just saying this to put the next items in some kind of context.


It’s very important that your team be able to test their assets in multiple lighting conditions. The easiest route is to make a test level where you can cycle lighting conditions from many different game levels, or sampled lighting from multiple points in the game. The default light in this level should be broad daylight IMO, as it’s the hardest to get right.


I created one of the first example assets for the physically based pipeline. It was a glass inlay table that I had at home, which had wooden, concrete (grout), metal, and multi-colored glass inlay. This asset served as a reference asset for the art team. Try to find an asset that can properly show the guys how to use gloss maps, IMO understanding how roughness effects your asset’s surface characteristics is maybe the biggest challenge when moving to a physically-based pipeline.


As with rolling out any new feature, you should train a few technically-inclined artists to help their peers along. It’s also good to have the artists give feedback to the graphics team as they begin really cutting their teeth on the system. On Ryse, we are doing the above, but also dedicating a single technical artist to helping with environment art-related technology and profiling.


It’s very important to have a ‘cheat sheet’, this is a sheet we created on the Ryse team to allow an artist to use the color picker to sample common ‘plausible’ values.


click to enlarge


We have created a debug view that highlights assets whose specular color was not in a physically-plausible range. We are very in favor of making tools to help people be responsible, and validate/highlight work that is not. We also allowed people to set solid specular values in the shader to limit memory consumption on simple assets.



Above are two things that I actually carry with me everywhere I go. The X-Rite ColorChecker Passport, and the Pantone Huey Pro monitor calibration toolset. Both are very small, and can be carried in a laptop bag. I will go into reference data acquisition in another post. On Ryse we significantly upgraded our reference acquisition pipeline and scanned a lot of objects/surfaces in the field.



Nicolas Shulz has presented many improvements made based on production use at GDC 2014. His slides are here. He details things like the importance of specular filtering on to preserve highlights as objects recede into the distance, and why we decided to couple normals and roughness.

UPDATE: We’ve now shipped Ryse, I have tried to update the post a little.  I was the invited speaker at HPG 2014, where I touched on this topic a bit and can now update this post with some details and images. (Tips for Leads and Directors) Nicolas also spoke at GDC 2014 and I have linked to his slides above. Though this post focuses on environments, in the end, with the amount of armor on characters, the PBR pipeline was really showcased everywhere. Here’s an image of multiple passes of Marius’ final armor:


click to enlarge

posted by Chris at 7:26 PM  

Monday, July 16, 2012

CINEBOX SIGGRAPH Talk and Studio Workshops


I am giving a talk at SIGGRAPH 2012 entitled ‘Film/Game Convergence: What’s Taking So Long?‘ where I discuss the inherent differences between games and film and go over a few case studies of projects that attempted to use a game engine for film previs. I also talk a bit about the development of our CINEBOX application, the decisions we had to make, and how we dealt with many of the issues previous attempts have run into.


I will be giving two more Studio Workshops this year, the first is a followup to last year’s Introduction to Python, entitled ‘Python Scripting in Maya‘. The other workshop is ‘Building a Game Level‘, which is the same basic workshop I gave last year where I show people how to make a playable game level in CryEngine in an hour. Studio Workshops are hands-on sessions where each attendee has a computer and follows along with the instructor. It’s a great chance for people of all ages to learn new things.

posted by admin at 8:06 PM  

Tuesday, October 25, 2011

Quick Note About Range(), Modulus, and Step


143 # bones
root ground
0 0 0
root hips
0 0.9512207 6E-08
spine 1
4E-08 0.9522207 1.4E-07
spine 2
3E-07 1.0324 8.3E-07
spine 3
5.6E-07 1.11357 1.53E-06
spine 4
8.2E-07 1.194749 2.22E-06
head neck lower


for i in range(0,20+1):
	if i%2 == 0: print i
#>> 0
#>> 2
#>> 4
#>> 6
#>> 8
#>> 10


jnts = []
	jnt = lines[i].strip()
	jnts.append([jnt, parent, pos])


posted by admin at 1:42 AM  

Wednesday, October 12, 2011

SIGGRAPH 2011: Intro To Python Course



  • Iterating through batches/lists
  • Reading / writing data to excel files


Installation / Environment Check







string = 'this is a string'
print string
#>>this is a string
num = '3.1415'
print num


print num + 2
#>>Traceback (most recent call last):
#>>  File "", line 5, in
#>>    print num + 2


print float(num) + 2
#>>5.1415 addme



txt = 'jan tony senta michael brendon phillip jonathon mark'
names = txt.split(' ')
print names
for item in names: print item


print names[0], names[5]
#>>jan phillip


print path.split('\\')[-1]



print dict['sean']


#>>sean is False
#>>sascha is tech artist
#>>harry is 142.1


Creating Methods (Functions)



def myFunc(input):
	people = input.split(' ')
	people = sorted(people)
	return people
txt = 'jan tony senta michael brendon phillip jonathon mark'
orderedList = myFunc(txt)
print orderedList

Basic Example: Create A Tag Cloud From an Excel Document



f = open(file, 'r')
lines = f.readlines()
print lines


f = open(file, 'r')
lines = f.readlines()
dict = {}
for line in lines:
print dict


output = ''
for key in dict.keys():

There we go. In one hour you have learned to:

  • Read and write excel files
  • Iterate over data
  • Convert data sets into new formats
  • Write, read and alter ascii files


posted by admin at 5:12 AM  

Monday, October 4, 2010

Writing Custom Perforce Plugins in Python

I recently wrote a custom tool to diff CryEngine layer files in P4, and was surprised how simple it was. What follows is a quick tutorial on adding custom python tools to Perforce.

Start by heading over to Tools>Manage Custom Tools… Then click ‘New’:

You can pass a lot of information to an external tool, here is a detailed rundown. As you see above, we pass the client spec (local) file name (%f) to a python script, let’s create a new script called ‘’:

import sys
from PyQt4 import QtGui    
class custom_tool(QtGui.QMessageBox):
	def __init__(self, parent=None):
if __name__ == "__main__":
	app = QtGui.QApplication(sys.argv)
	theTool = custom_tool()

What this does is simply spits out the sys.argv in a way you can see it. So now you can feed any file you right click in Perforce into a python script:

If you would like to actually do something with a file or revision on the server and are passing the %F flag to get the depot file path, you then need to use p4 print to redirect the file contents (non-binary) to a local file:

p4.run_print('-q', '-o', depotFile, localFile)
posted by admin at 1:09 AM  

Thursday, August 26, 2010

Perforce Triggers in Python (Pt 1)

Perforce is a wily beast. A lot of companies use it, but I feel few outside of the IT department really have to deal with it much. As I work myself deeper and deeper into the damp hole that is asset validation, I have really been writing a lot of python to deal with certain issues; but always scripts that work from the outside.

Perforce has a system that allows you to write scripts that are run, server side, when any number of events are triggered. You can use many scripting languages, but I will only touch on Python.

Test Environment

To follow along here, you should set up a test environment. Perforce is freely downloadable, and free to use with 2 users. Of course you are going to need python, and p4python. So get your server running and add two users, a user and an administrator.

Your First Trigger

Let’s create the simplest python script. It will be a submit trigger that says ‘Hello World’ then passes or fails. If it passes, the item will be checked in to perforce, if it fails, it will not. exiting while returning a ‘1’ is considered a fail, ‘0’ a pass.

print 'Hello World!'
print 'No checkin for you!'

Ok, so save this file as Now go to a command line and enter ‘p4 triggers’ this will open a text document, edit that document to point to your trigger, like so (but point to the location of your script on disk):

	hello_trigger change-submit //depot/... "python X:/projects/2010/p4/"

Close/save the trigger TMP file, you should see ‘Triggers saved.’ echo’d at the prompt. Now, when we try to submit a file to the depot, we will get this:

So: awesome, you just DENIED your first check-in!

Connecting to Perforce from Inside a Trigger

So we are now denying check-ins, but let’s try to do some other things, let’s connect to perforce from inside a trigger.

from P4 import P4, P4Exception
p4 = P4()
	#use whatever your admin l/p was
	#this isn't the safest, but it works at this beginner level
	p4.user = "admin"
	p4.password = "admin"
	p4.port = "1666"
	info ="info")
	print info
#this will return any errors
except P4Exception:
	for e in p4.errors: print e

So now when you try to submit a file to depot you will get this:

Passing Info to the Trigger

Now we are running triggers, accepting or denying checkins, but we really don’t know much about them. Let’s try to get enough info to where we could make a decision about whether or not we want the file to pass validation. Let’s make another python trigger,, and let’s query something from the perforce server in the submit trigger. To do this we need to edit our trigger file like so:

	test change-submit //depot/... "python X:/projects/2010/p4/ %user% %changelist%"

This will pass the user and changelist number into the python script as an arg, the same way dragging/dropping passed args to python in my previous example. So let’s set that up, save the script from before as ‘’ as shown above, and add the following:

import sys
from P4 import P4, P4Exception
p4 = P4()
describe = []
	p4.user = "admin"
	p4.password = "admin"
	p4.port = "1666"
except P4Exception:
	for e in p4.errors: print e
print str(sys.argv)
describe ='describe',sys.argv[2])
print str(describe)

So, as you can see, it has returned the user and changelist number:

However, for this changelist to be useful, we query p4, asking the server to describe the changelist. This returns a lot of information about the changelist.

Where to Go From here

The few simple things shown here really give you the tools to do many more things. Here are some examples of triggers that can be  created with the know-how above:

  • Deny check-ins of a certain filetype (like deny compiled source files/assets)
  • Deny check-ins whose hash digest matches an existing file on the server
  • Deny/allow a certain type of file check-in from a user in a certain group
  • Email a lead any time a file in a certain folder is updated

Did you find this helpful? What creative triggers have you written?

posted by admin at 12:33 AM  

Monday, June 28, 2010

Python: Simple Decorator Example

In Python, a Decorator is a type of macro that allows you to inject or modify code in functions or classes. I was turned onto this by my friend Matt Chapman at ILM, but never fully grasped the importance.

class myDecorator(object):
	def __init__(self, f):
		self.f = f
	def __call__(self):
		print "Entering", self.f.__name__
		print "Exited", self.f.__name__
def aFunction():
	print "aFunction running"

When you run the code above you will see the following:

>>Entering aFunction
>>aFunction running
>>Exited aFunction

So when we call a decorated function, we get a completely different behavior. You can wrap any existing functions, here is an example of wrapping functions for error reporting:

class catchAll:
	def __init__(self, function):
		self.function = function
	def __call__(self, *args):
			return self.function(*args)
		except Exception, e:
			print "Error: %s" % (e)
def unsafe(x):
  return 1 / x
print "unsafe(1): ", unsafe(1)
print "unsafe(0): ", unsafe(0)

So when we run this and divide by zero we get:

unsafe(1):  1
unsafe(0):  Error: integer division or modulo by zero

Using decorators you can make sweeping changes to existing code with minimal effort, like the error reporting function above, you could go back and just sprinkle these in older code.

posted by admin at 9:06 AM  

Saturday, June 26, 2010

Python: Special Class Methods

I have really been trying to learn some Python fundamentals lately, reading some books and taking an online class. So: wow. I can’t believe that I have written so many tools, some used by really competent people at large companies, without really understanding polymorphism and other basic Python concepts.

Here’s an example of my sequence method from before, but making it a class using special class methods:
class imSequence:
	def __init__(self, file):
		dir = os.path.dirname(file)
		file = os.path.basename(file)
		segNum = re.findall(r'\d+', file)[-1]
		self.numPad = len(segNum)
		self.baseName = file.split(segNum)[0]
		self.fileType = file.split('.')[-1]
		globString = self.baseName
		for i in range(0,self.numPad): globString += '?'
		self.images = glob.glob(dir+'\\'+globString+file.split(segNum)[1])
	def __len__(self):
		return len(self.images)
	def __iter__(self):
		return iter(self.images)

Here’s an example of use:

seq = imSequence('seq\\test_00087.tga')
print len(seq)
print 'BaseName: %s  FileType: %s  Padding: %s' % (seq.baseName, seq.fileType, seq.numPad)
>>BaseName: test_  FileType: tga  Padding: 5
for image in seq: print image

[More info and examples: Dive Into Python: Special Class Methods]

posted by admin at 10:16 PM  

Monday, May 3, 2010

Nikon D300 Stereo Rig [$30 DIY]

This is what the final product will look like. Two D300s, mounted as close as possible, sync’d metering, focus, flash, and shutter. Rig cost: Less than 30 dollars! Of course you are going to need two d300s and paired lenses, primes or zooms with a wide rubberband spanning them if you are really hardcore. Keep in mind, the intraoccular is 13.5cm, this is a tad more than double the normal human width, but it’s the best we can do with the d300 [horizontal].

Creating the Camera Bar

This mainly involves you going to the local hardware store with your D300 and looking at the different L-brackets available. It’s really critical that you get the cameras as close as possible, so mounting one upside down is preferable. It may look weird, but heck, it already looks weird; might as well go full retard.

I usually get an extra part for the buttons, because they will need to be somewhere that you can easily reach

Creating the Cabling

Nikon cables with Nikon 10-pin connectors aren’t cheap! The MC-22, MC-23, MC-25, or MC-30 are all over 60 dollars! I bought remote shutter cables at I wanted to make my own switch, and also be able to use my GPS, and change the intraoccular, so the below describes that setup. If you just want to sync two identical cams, the fastest way is to buy a knock-off MC-23, which is the JJ MA-23 or JJ MR-23. I bought two JueYing RS-N1 remote shutters and cut them up. [$6 each]

I only labeled the pins most people would be interested in, for a more in depth pin-out that covers more than AE/AF and shutter (GPS, etc), have a look here. I decided to use molex connectors from RC cars, they make some good ones that are sealed/water-resistant and not too expensive.

So the cables have a pretty short lead. This so that I can connect them as single, double, have an intraoccular as wide or as short as any cable I make.. The next thing is to wire these to a set of AF/AE and shutter buttons.

Black focuses/meters and red is the shutter release. It’s not easy to find buttons that have two press states: half press and full press. If you see above, shutter is the combination of AF/AE, ground, and shutter. This is before the heat shrink is set in place.


So that should be it. Here’s my first photo with sync’d metering, focus, flash, and shutter. They can even do bursts at high speed. Next post I will try to look into the software side, and take a look at lens distortion, vignetting, and other issues.

posted by admin at 12:27 AM  
Next Page »

Powered by WordPress