
After some new training, here comes the second part of the Python + Maya Linil Tutorial. If you have no idea of what am I talking about in here, please feel free to check the first part of this tutorial Python + Maya – Part 1.
For you to get a grasp of what we’ll be building this time, take a look at the video from the final output.
Looks pretty better than the other time, huh? Let’s get started.
First of all, much of this new part is based on creating materials and changing their attributes, so before we turn into coding, it’s necessary to understand how Maya material system works.
In Maya, one material (let’s say, wood) is not applied directly to an object (let’s say, a table). It’s applied to a Shading Group and the objects are associated to the Shading Group. The purpose of this post is not to discuss this, but it’s very important for you to understand that. So, the chain is:
Material (wood) -> Shading Group -> Object (table)
Note We will get to coding now and I’ll be explaining just the procedure to create and animate only the balls that stand over the x axis, you’ll notice that animating the other ones (over the z axis) will be a matter of parameter setting. As WordPress has a lot of problems with syntax highlighting, you might think it’s better to follow the code through the original file. The link to the code without the z axis animation is: pythonMaya_xaxis.py and the link to the complete animation is: pythonMaya.py
Let’s get to coding now. As we’ll be creating lots of materials, we must define three functions in order to maintain the readability of the code.
def createMaterial( name, color, type ):cmds.sets( renderable=True, noSurfaceShader=True, empty=True, name=name + ‘SG’ )
cmds.shadingNode( type, asShader=True, name=name )
cmds.setAttr( name+”.color”, color[0], color[1], color[2], type=’double3′)
cmds.connectAttr(name+”.outColor”, name+”SG.surfaceShader”)
The first line creates a Shading Group by calling the function sets, which returns nothing more than a set, in this case, an empty set for a while. The next line, creates a Material with the function shadingNode. Its first argument defines the type of material (lambert, blinn, anisotropic, etc), the second one is necessary for the system to understand it’s creating a real shading node, and the last one is the new material’s name.
After that, we set the color attribute. Notice that it doesn’t take a tuple for argument, but three different numbers and a fourth argument defining that the last three are a vector of 3 double integers. The last line connects the output color of the material we created to the surfaceShader of the Shading Group.
def assignMaterial (name, object):cmds.sets(object, edit=True, forceElement=name+’SG’)
This one gets one object (the first argument) and assigns it to a correspondent Shading Group (the third argument and which must have already been created through createMaterial).
def assignNewMaterial( name, color, type, object):createMaterial (name, color, type)
assignMaterial (name, object)
The third, and easiest of them, is just a way to call both others in one line.
After that and as you’ve seen in the movie, you’ll know the purpose of this time is to create some balls and then make them move in wave form. Before moving on to the balls, we need to create the ground plane (15×15).
cmds.polyPlane(name = ‘ground’, sw = 15, sh = 15, w = 15, h = 15)
To animate the balls, the easiest way is to set their vertical positions using a sinus function. We need to initialize the balls’ position and color and create a new material for each one of the plane’s faces that will have a ball on top:
for i in xrange(0,13):cmds.polySphere(name = ‘ball’ + str(i), radius = 0.5)
pos = 2 + 1.5*sin( (1.6/pi)*(6-i) )
val = (1 + sin( (1.6/pi)*(6-i) ))/2
cmds.setAttr( ‘ball’ + str(i) + ‘.translateX’, 6-i)
cmds.setAttr( ‘ball’ + str(i) + ‘.translateY’, pos)
assignNewMaterial( ‘ballShader’ + str(i), (val, val, 1), ‘blinn’, ‘ball’ + str(i) )assignNewMaterial( ‘ground’ + str(i), (1, 1, 1), ‘lambert’, ‘ground.f[‘ + str(118-i) + ‘]’ )
As we have 13 balls, we use a for from 0 to 12. In each step we:
- Create a new Polygon Sphere with radius 0.5 and name ballX where X stands for an index from 0 to 12;
- Calculate their vertical position using a sin function;
- Calculate the color value which will be used ahead. The idea here is for the ball to be completely white (1,1,1) on it’s uppermost position and completely blue (0,0,1) on its downermost part;
- Set the balls X position to coincide with each of the faces of our ground plane;
- Assign the vertical value to the translateY attribute;
- Create a new material for the ball;
- Create a new material for the plane face (the faces of an object are accessed through an indexed list of faces, so the middle line will be like 7*15 + 1 = 106).
After that, it’s time to animate. Keyframe setting comes inside two for loops. The first one, from 0 to 200 (the number of frames of our animation) and the other from 0 to 13 (the number of spheres on the x axis).
for itr in xrange(0,200):for i in xrange(0,13):name = ‘ball’ + str(i)
name2 = ‘ballShader’ + str(i)
pos = 2 + 1.5*sin( (1.6/pi)*(6-i) +itr/5.0 )
val = (1 + sin( (1.6/pi)*(6-i) +itr/5.0 ))/2
cmds.setKeyframe(name, attribute=’translateY’, value=pos, t=itr )
cmds.setAttr( name2 + ‘.color’, val, val, 1, type=’double3′ )
cmds.setKeyframe(name2, attribute = ‘color’, t=itr )
if(pos < 0.55):cmds.setAttr( ‘ground’ + str(i) + ‘.color’, 0, 0, 1, type=’double3′ )
cmds.setKeyframe(‘ground’+str(i), attribute = ‘color’, t=itr )else:
cmds.setAttr( ‘ground’ + str(i) + ‘.color’, 1, 1, 1, type=’double3′ )
cmds.setKeyframe(‘ground’+str(i), attribute = ‘color’, t=itr )
On the beginning of each loop, we set two names, the name of the ball to be animated (name) and the name of the shader to be animated (name2). Then we calculate the new position and color value the same way we did on our initialization. Then we set a keyframe for the ball’s position. To animate the material’s color, it’s needed to first set the attribute of it and then set a keyframe (we can’t use the value tag of the setKeyframe function but I don’t know why this happens yet, so expect some news in here).
After that, we analyze if the ball is sufficiently near the ground for us to set it’s face’s color. It’s done through the if and else statement.
So, you execute it and voilà! A new set of waving and color-changing balls! As I said, doing the z axis balls initialization and animation stands for homework for you all 😀
Again, hope you enjoyed this new part (a lot more complicated than the first one but a lot more beautiful) and stay tuned for new parts.
Ah, don’t forget to comment and tell us your experience, doubts, impressions, etc.
P.S.: I did some tweaking with the render in order to get that nice look, but this will be covered in other post! [=
Thank you! 🙂
I modified the shader creation a bit, so that you don’t have to increment the name of the shader on yourself. Maya automatically increments the number if the shader already exists. You just have to store the new name in a variable (newNode) and reuse it when creating the ShadingGroup and connection. The new name is then returned for the assignNewMaterial procedure.
Just my two cents. 🙂
def createMaterial(name, color, shader):
newNode = cmds.shadingNode(shader, asShader = True, name = name)
cmds.sets(renderable = True, noSurfaceShader = True, empty = True, name = newNode + “SG”)
cmds.setAttr(newNode + “.color”, color[0], color[1], color[2], type = “double3”)
cmds.connectAttr(newNode + “.outColor”, newNode + “SG.surfaceShader”)
return newNode
def assignMaterial(name, target):
cmds.sets(target, edit = True, forceElement = name + “SG”)
def assignNewMaterial(name, color, shader, target):
node = createMaterial(name, color, shader)
assignMaterial(node, target)
You examples are not available anymore at rapidshare 🙂 Can you send me the files? nice tutorials
Ok I’e typed in the code…nice example…tks