So You Want To Start A Digital Project Hua?

Article / 14 December 2021

Who is this for?

I wrote this up to be able to refer people to it as a standing recommendation for contractees, project leads and chief stakeholders who contact me for work on their projects.  This is meant to help you, the project owner, formulate a plan that can help contractors serve you better.
I have been in the game and app industry for over 15 years developing 3D assets for delivery via film, web, game engines and product development.  I have worked for studios as a regular employee and I have worked as a contractor on many projects, both small and large.  Suffice it to say that I have learned a few things about how projects like this work.  If you are contracting people for your project, please read this.  I promise it will save you time, money, and frustration.

About Your Project

What is your project?
If you are contracting people for a project, odds are, you are the chief stakeholder.  You probably have your own money and time already invested in this project.  Perhaps the idea, the intellectual property is yours and you are looking for professionals to help you realize your idea.  The first step in this process should be a series of questions you ask yourself about you, your project, and your resources.

Can you describe your project in a paragraph or two?
Note, this description needs to have the creative idea.  It needs to have the media(s) it will be delivered as, e.g. film, game, book.  It also needs to have the device(s) that it will be delivered on e.g. phone, PC, game console.  Also, what sort of artwork is it, live action, 3D, 2D, AR, VR, .GIF?  This description needs to be concise and complete.  

What is your timeline for this project?
Games and apps typically take several years to develop.  Individual assets like characters can take weeks.  Are your expectations realistic? 

Who are the other professionals you will need to complete this project?
There is no end to the job postings on freelance websites seeking a “full stack” developer/artist who can single handedly create an entire game or app. I am afraid that is not realistic. For an app or game you will need at minimum 2 professionals. A technical artist and a programmer. However, for those two to develop an entire app, they both need to have robust skillsets and plenty of time to develop. The smallest team I have ever worked with was 5 people. I was the lead technical artist, there were two programmers, a UI specialist, and the project lead.
Is this just a "simple" cinematic animation? Have you thought about the audio? Your animator may be able to do the sound and the visuals too, but maybe not. Do you know what a storyboard is? An animatic? They are very useful tools that help with project scope and vision. Typically those are provided by professional illustrators and your animator may not be the person for that work.

About Your Financial Resources

What is your budget for this project?  
Please know this, production work is not cheap.  To give you an idea, large production companies like Disney, Pixar, Sony and Dreamworks have reported spending as much as $35,000.00 USD per SECOND of animation.  AAA game titles typically cost hundreds of millions of dollars to produce.  Certainly your budget is far less, but what does that mean for the overall quality and scope of your project?  

Who are the other stakeholders?
If there are others, what are their expectations? If you have investors, they will want to see progress. What is your delivery system for that? Do you know what a MVP, a Minimal Viable Product is?  Are you familiar with the fact that production work is a highly iterative process?  Are you comfortable mitigating your stakeholders expectations?  Do you understand the process enough to present it in stages?  If you don't understand the production process enough to speak with authority about it, can you develop those skills?

About You

Who do you want to be to this project?
Do you want to own every creative decision that is made?  Do you want to get your hands dirty and be in the mix?  Or do you want to hang back and worry about financing and overall project steerage while the production people do their thing and report back?  Exactly what do you want to do and what don’t you want to do?  Chiefly, does this sort of work excite you?  Is this something that you have passion for?  Excitement, passion and drive are the most important things you can bring to your project.  With those motivators you can learn and do whatever you need to bring your project into the world.

What are you qualified to do on this project?
If you want to be heavily involved in the project, do you know enough about production work to do that?  If you want to just worry about funding and big picture stuff, do you have the resources for that?  How much time and energy are you prepared to invest in this project?  If you want to be really involved but you have a lot to learn, do you have the bandwidth for that? 

Now go to work!
Make a cup of joe, sit down and start answering these questions.  It is important that you write the answers out, get them expressed outside of your head.  If you can’t answer all of these questions, no worries, just google it, your questions are probably answered somewhere out there.  If you can't find the answers, get on reddit and find a thread you can follow, ask questions.  Finally, if you are still stumped, you can certainly pay a professional for a consultation.

Good luck!

Don't hesitate to reach out. 

Maya's Plus Minus Average Node Explained From a Rigging Perspective. Python

General / 14 December 2021

The Autodesk documentation on the plus minus average node is atrocious, so I though I would make some notes about it in case somebody else is also confused.

Also, these nodes are frequently used in rigging, probably more than they are used in shading networks, but they are written for shading networks and the documentation assumes that is how you are using them so they can be confusing to look at for their rigging potential.  Just for the record I think Autodesk should make another class of math nodes that are not shading-centric but they don't listen to me.

Sorry in advance for this really long post, but there is a lot to know about how to use this node and in typical Autodesk fashion most of what you need to know is about how flawed the tool is.

First let's talk about how it works.  And then I will walk through the setup.

The input values that you plug into this node can be added, subtracted or averaged and the output can be used for whatever purpose you like.  That's fairly straight forward but knowing how those values get plugged in and how they work once they are there is where the confusion sets in.   There are 3 sets of inputs available on this node that offer the ability to process 1 list of numbers, 2 lists of numbers or 3 lists of numbers.  All of the numbers in the list will have the operation performed on them. So you might have values in 1D of [1, 2, 3]  and the operation is Sum the output would be 6.  You may have a 2D list [[1, 2, 3,], [4, 5, 6]]  if the operation was Sum then you would have an output of 6 and 15.

So again, it can take either single dimension inputs which appears as (input 1D), double input dimensions (input 2D),  or triple input dimensions (input 3D).  It could conceivably take all three if you like since each of these inputs whether a single, double or triple are their own distinct lists with their own distinct outputs.

The designations for the different inputs are:

.input1D[num], .input2D[num], .input3D[num] where num is the position of numbers in the list to be added.

So if you wanted to add the number 1 plus the number 2 you would write that like this:

cmds.setAttr('plusMinusAverage1' + '.input1D[0]', 1)

# the way this would read is: set an attribute on the plusMinusAverage1 node, the attribute is the first position in the input list designated with the index position [0] and the value of that entry is 1.

cmds.setAttr('plusMinusAverage1' + '.input1D[1]', 2)

# the way this would read is: set an attribute on the plusMinusAverage1 node, the attribute is the first position in the input list designated with the index position [1] and the value of that entry is 2.


Now lets look at setting up a plusMinusAverageNode manually.

Open the node editor under "Windows"  although you can work with this object in the hypershade the node editor works better for utility nodes.

Create a plus minus average node.   You can do this by clicking in the Node Editor window and hitting the tab button, then you can type in 'plus', it will narrow your search and then you can click the node you want and hit enter to create.

Select that node and view it in your attribute editor.  You will see that there are drop down menus.  All of the business for this node is under Plus-Minus-Average Attributes.  Open that.  The first thing you will see is the operation.  Its default is "Sum" you can set it to "Subtract" or "Average" and  "No operation"  That's fairly self explanatory, you can change the operation of the node by changing that value.

Next set of drop downs are the Input 1D, input 2D and input 3D.  Open these up and you will see that there are no inputs available yet.  There is also an incongruity between the field for the Input 1D and the other inputs.  The Input 1D area is a large inert field with no buttons to interact with it.  You can't drag anything into the field, you can't enter anything, it's useless as it is.  But the other inputs allow you to click buttons that add new items.  Go ahead and add a new item to either the 2D or the 3D. "The Add New Item" creates lists of inputs that can be directly manipulated through the attribute editor, you can also make new connections via the familiar checker button next to the fields and you can make manual connections via the Node Editor, the Hypershade or the Connection editor by middle mouse dragging connections.

Now understand this, there is no functional reason for the 1D field.  In fact there is no functional reason for the 2D either.  Each channel of the 3D input has a corresponding 3D output.  Each of the 3 channels operate independently and don't effect each other so if you wanted to just calculate a single list of values you could just use one channel of the Input 3D.  I don't know exactly why they have the 1D and 2D inputs.  I imagine there is a slight computational savings to use the 1D if you are only calculating a single value.  However that doesn't explain the wildly different treatment of the 1D input.  Overall this is a really poorly written tool.  What would make this a really smart tool is if you could dynamically assign any number of input dimensions.  Why limit it to 3 and why treat any number of dimensions different from any other?  Why?  because Autodesk... that's why.

Whatever, I'll still tell you how to use the 1D, but the fact that a single channel of the 3D does the same thing as the 1D at least helps illuminate the nature of this node.

So, make another plus minus average node in the Node Editor window.

Now click on the little 3 oblong button icon in the upper right hand corner of each of the nodes.  Click 3 times on the button to expand the node completely. Now you will see the channels for the nodes.

MMD the output 1D of one node into the input 1D of the second node.  Now look in the attribute editor for the node that is getting the input and you will see that it now has an input that is being generated by a connection (yellow block) and the value is 0.  Now look back at the node editor at the node receiving the input.  There is now a second value field that magically appeared when you added the first.  It can also take an input connection, so you can go on making connections.  But what if you wanted a set of values to be static or not derived from a connection?  Go ahead and try to figure it out...  I'm waiting... still waiting...  Ha!  You can't do it without code.  Isn't that bullshit? We'll get to how you do that in a bit, but lets now use the 2D input/output to manually set up a connection.

Select the firstPlusMinusAverage node and view it in your attribute editor.

Hit the "Add New Item" button under the Input 2D drop down. The fields for the node are made available to you.

Click in the the first channel in the Input2D[0] to 5.  BTW this first column is the "x" channel in code but has no decoration here.

Go back to the node editor and middle mouse drag Output 2Dx from the first plus minus average node into the Input 2Dx of the second plus minus average node.

Now select the second plus minus average node and view it in the attribute editor.  You will see that the Input 2D has the yellow filed signifying it is connected and the value is 5.

Click the "Add New Item" button and you will get another set of fields.

Select the first plus minus average node and in the attribute editor.

Change the value in the second column of Input to 10 (this second column is the "y" channel in code but has no decoration here)

Go back to the node editor and from the first plus minus average node drag Output 2Dy into the Input 2D[1], Input 2Dx of the second plus minus average node

Select the second plus minus average node receiving the inputs, view it in the attribute editor and you will now see that there are two connected inputs, one with a value of 5, the other 10.

You will see that there is nowhere to view the output for this node which I think is dumb but whatever.

Create a sphere and it will automatically be loaded into your Node Editor.  There are 3 nodes to this shape, the shape node itself, the history node and the transform.

Middle mouse drag the Output 2D, Output 2Dx into the Translate Y of the transform node for the sphere you just created and you will now see that shape jump up in your scene.

You can now control this shape's y position with the first plus minus average node inputs.

Pretty cool hua?

Now lets move on to the code you would use to create these nodes and setup inputs and outputs for them.

You create a plusMinusAverage Node  like this:



SomePlusMinusAverageNode = cmds.shadingNode('plusMinusAverage', asUtility = True, name ='somethingPlusMinusAverageNode')


The code to change the operation for the node is:


#this changes the operation to subtract cmds.setAttr(SomePlusMinusAverageNode + '.operation', 2)


Note that the value passed is an enum, with 1 for add, 2 for subtract, 3 for average and 0 for none


We will be manipulating the 1D input here.  Before you can do anything with the 1D field you have to first create a field.

Here is the code to create an entry field without a connection:

cmds.setAttr(SomePlusMinusAverageNode + '.input1D[0]', 0)


Now you can change that value manually to whatever you want.  That's how you can create a static field, or one that is changed with code.

Add another value as this operation is currently just outputting the single value you have entered.


cmds.setAttr(SomePlusMinusAverageNode + '.input1D[1]', 5)



The way that this now works is that the operation will be performed on every value in the list of the attribute editor.  The values appear as a vertical list in the form:

Input 1D[0]

Input 1D[1]

and so on...

This same logic follows for the 2D and 3D Inputs.  2D inputs are simply two 1D inputs and 3D inputs are three 1D inputs.  You can think of them as separate lists in the multi D inputs.

There is an additional input value for the 2D and 3D inputs that designates which "dimension" the value should be entered into.


This is setting the 3DX input to 5:

cmds.setAttr(SomePlusMinusAverageNode + '.input3D[1]' + '.input3Dx', 5)


To connect the output of a plus minus average node you simply have to connect the output of the specific channel you have manipulated to whatever input you want it to drive.


cmds.sphere(name = 'testSphere')

cmds.connectAttr(SomePlusMinusAverageNode + '.output1D', 'testSphere' + '.translateY' )


To connect the 3D output it would look like this:
cmds.connectAttr(SomePlusMinusAverageNode + '.output3Dx ', 'testSphere' + '.translateY' )

That's it!  Now you should know the different ways to create and manipulate a plus minus average node and how to use it.!

Method for Multiple UV Tiles in Maya

Tutorial / 01 December 2021

Setup a layered texture node feeding whatever channel (diffuse) for example.

Layer your image files into the layered texture.
Set the blend modes to add

Select all of the image nodes and go to the attribute spread sheet.  Turn all default color RBG values to 0

For each 2D placement node, turn off Wrap U and Wrap V

In the Attribute Editor for the Image Node:

Set UV Tiling Mode to Explicit Tiles

Change the U and V values to reflect each image's placement along the UV tiles.

The coordinate system is 0 based so if you had your head texture on the second UV tile (one right of the default)  the values would be U=1 V=0

Generate preview

Accessing data in json lists python

Tutorial / 23 May 2021
for eachEntry in rigData:
     controllerName = eachEntry
     #print('eachEntry is: ' + str(eachEntry))#The name for each list 
     for eachList in rigData[eachEntry]:
         #print('eachList: ' + str(eachList))#The values of each list in the root of data 
          for eachListEntry in [eachList]: 
               print('eachListEntry: ' + str(eachListEntry))#This is the value for each key in each list 
               for eachKey, eachValue in eachListEntry.iteritems(): 
                    print('eachKey: ' + str(eachKey))#This is the value for each key in each list 
                    print('eachValue: ' + str(eachValue))#This is the value for each key in each list

Note, Artstation's janky code format doesn't allow for tabbing, so copy and paste may not work, you may need to re-establish the tabs.

Python Format for Outputting to JSON

Tutorial / 23 May 2021
skeletonRoot = = True) skeletonList = cmds.listRelatives(skeletonRoot, allDescendents = True, type = 'joint') skeletonList.append(skeletonRoot[0]) skeletonList.reverse() 
space = " " * 4 
for eachJoint in skeletonList: 
     print(space + '"' + str(eachJoint) + '":')
     print(space + "[")
     print(space * 2 + "{")
     print(space * 3 + '"' + "ControlsList" + '"' + ":" ' "' + "worldSpaceController" + '"')
     print(space * 2 + "}")
     print(space + "],")

Note, Artstation's janky code format doesn't allow for tabbing, so copy and paste may not work, you may need to re-establish the tabs.

Export an FBX from Maya using Python

Tutorial / 23 May 2021

There are a fair amount of people out there who have weighed in on how to do this in various forums but I didn't find a single one that actually worked despite them being voted as the solution. Perhaps they worked in previous versions of Maya. I don't know, but I got this solution by sure educated guess, trial and error. This works as of Maya 2018: 

def exportMeshes(*args): 
     nameField = cmds.textField(shipNameTextField, editable = True, query = True, text=True)
     projectDirectory = cmds.workspace(query = True, directory = True) 
     print('The projectDirectory is: ' + str(projectDirectory)) savePath = str(projectDirectory) + nameField cmds.sysFile(savePath, makeDir = True) cmds.file(savePath, exportSelected = True, type = 'FBX')

The code above creates a new folder in your project named by a text field in a form. I didn't include the form. If you need to know how to build a form and query a textfield just search on my site for textfield and you will find info on how to do that. Cheers!

Note, Artstation's janky code format doesn't allow for tabbing, so copy and paste may not work, you may need to re-establish the tabs.

Create a Directory from Maya Python

Tutorial / 23 May 2021

def directoryCheckMake(): directoryExists = os.path.isdir(savePath) if not directoryExists: cmds.sysFile(savePath, makeDir = True)

Generate a list of joints from a skeleton in Maya, cut and paste into a python list

Tutorial / 23 May 2021

skeletonRoot = = True)
skeletonList = cmds.listRelatives(skeletonRoot, allDescendents = True, type = 'joint') skeletonList.append(skeletonRoot[0]) skeletonList.reverse() space = " " * 4 for eachJoint in skeletonList: print("'" + str(eachJoint) + "'")