Warning: Declaration of HSM_PageTitle::load($meta) should be compatible with HSM_Module::load($meta = '') in /homepages/22/d93554086/htdocs/ys/wp-content/plugins/headspace2/modules/page/page_title.php on line 201
computer science » Yekaterina Satanina
May 052015
 

This week the trees are finally sprouting leaves and flowers in Rochester. I started learning about L-systems this week. Coincidence? Pre-planned? Coincidence. But appropriate.

These things, right here!

L-systems are recursion. Visual code, just like our very first Python project in Computer Science 1 class. They’re a rewriting system, consisting of an alphabet/vocabulary of symbols that make rule strings, and grow with each generation according to those rules. They can be very rigid and architectural, or organic and random. Very versatile; both design-y and artsy. But based on math, and created by a biologist/botanist. Often used to model organisms.. molecules.. fractals.. plants. Many other things. I got excited.

I’d say this here is a pretty darn good example of generations of a growing plant. Fairly random-lookin, fairly organic. If I can learn that, ‘twould be cool!

Why do I need L-systems all of a sudden? For the focal point of my piece – the bonfire. Branches, twigs. To start, I quickly mocked up some logs and arranged them in a bonfire:

lsys7

Now I need to make me some branches.

I’ll make this post into a handy reference for myself, for future endeavors involving L-systems. As I said, L-systems are created based on rules. There’s a base, then there are rules defining the iterations.

Example base:    X

Example rule:     X = ! /(3) T F[+X] ~F[-X] +X : 0.4

Unlike regular algebra, each symbol is read and interpreted left to right. This one would read as: the thickness decreases, as the limbs roll 3 degrees counterclockwise, and curve down due to gravity, do a step, twist randomly, do a step and another turn and step – and the probability of all this happening is 40%. The (not full) syntax broken down is:
F   —> full step
H   —> half step
  —> full step, no line drawn
h   —> half step, no line drawn
+,   —>  turn right, left; recursion in 2 dimensions (flat)
&, ^   —> pitch up, down; adds rotation in the third dimension
\, /   —> roll clockwise, counterclockwise
[ ]   —> the start and end of a side branch; “push and pop” (because that makes more sense to me, in CS stack terms), allow for backtracking (node rewriting)
T   —> tropism (curvature) – adding a curve to the branches. The definition of tropism is: “the turning of all or part of an organism in a particular direction in response to an external stimulus.” And that makes sense because, you know, L-systems can be used to describe molecules and other biological things.
”   —> multiplier of curvature
!   —> multiplier of thickness
;   —> multiplier of angle
~   —> turn a random degree

Here’s a much more thorough list of commands: http://www.selcukergen.net/ncca_lsystems_research/houdini.html. And and even longer but thorougher explanation, straight from the SideFX website: http://www.sidefx.com/docs/houdini10.0/nodes/sop/lsystem.

So let’s do something. There are two methods of creating L-systems: 1) edge rewriting – replacing edges with the recursion, and 2) node rewriting – appending recursion to the last node.

lsys1

This Koch star, which started as a triangle, is an example of edge rewriting, as the edges get more complex with each generation. Fun fact: adding a Revolve node to the resulting curve is fun. Or, you know, not stopping with the L-system and actually creating something interesting using it as a stepping stone. (Which is how it should be done.)

lsys2

And this is a simple example of node rewriting, where the structure actually “grows” with each generation. Very simple rule up there – each time you see a full step, replace it with: a full step, a branch turned right, another full step, a branch turned left, and a third full step.

lsys3

This example introduces variables – symbols outside of L-system vocabulary that are defined by the artist in the rules. These variables are just another “full step,” but you get to make whatever you want of it. Easy, right? Easy..

But then you add the ! for thickness, to get the branch to thin out on top, and the T for some sagging of the side branches, and for curvature, and ~ for twisting, and add values in parentheses like (15) to specify the angle/value of turning/manipulation, and get it more and more and more complex and cool!

lsys4

So I was already happy with my branch at this point: it had quite a lot of variation and twist to it, looked fairly organic overall! And it doesn’t even include any probability rules!

Probability rules?

If you add ‘:number‘ to the end of a rule (like this: X = ! // FF [-X] F [+X] F : 0.8), that number becomes the % of the time that rule gets executed. Randomness!

And there are, of course, conditionals – if statements. F: t>number = rule means execute the rule if the generations are greater than the number specified. Very very cool for structures that age.

  —> or
  —> and
Example: F: (t<3) | (t>6) = rule

But that’s not all. The L-system node in Houdini has four inputs up top, labeled by letters of the alphabet. They’re there for copying geometry onto the ends of branches! Hello, fruits, leaves, atoms, or whatever else your heart desires on the ends of those branches. I quickly threw some randomized berries on my tree, just by taking a sphere, mountain-SOPing it, scattering points on the deformed thing, and copying a little-er sphere onto each scattered point. And plugging that into the J input of the L-system:

lsys5

Neat. I like it!

The possibilities here, oh man. It would probably help me, being a visual person, to draw the shape of the tree/system/object I want, then figure out how to make that pattern, what building blocks go into it – and then translate that “pseudo code” into L-system rules.

But this was enough for me to make the branches for the bonfire. Here’s what I ended up with. It’s not final, but I’m happy with the direction it’s going:

lsys8

Next up: the fire and the smoke.

Dec 262013
 

So after several weeks (months) of not updating this blog with progress of the Computer Science independent study, guess what? I’m done! Nowhere near what I wanted to achieve in the beginning, but I think I learned a thing or two.

As always, as with any of my other brilliant ideas, I regretted this one ardently and often.

Here’s what came of it!

final1

Okay, fine, that’s a Photoshop composite of the final product. But hey, here’s the final product, and this time all code and absolutely no hand-sy manipulation apart from opening up Maya and the script editor:

final_rough01

final_rough02

I owe this largely to Autodesk Maya’s online Python command reference. All semester long, that was by far the best resource I found for a beginner 3D programmer. I’m sure that taking a 3D graphics programming class, focusing on MEL or Python, would help a lot, too. We’ll see about that senior year.

For now, I learned how to generate a simple scene with a stylized ground plane, grass, rocks, and trees. Everything is controlled by the inbuilt random generator, which will produce the same exact results each time I execute my code, but will change drastically at the slightest alteration of a number.

The size, shape, rotation, component deformation, and placement of each element in this scene is controlled by the random generator. The ground plane can be any size, with any amount of huge hills or tiny ditches, or just plain smooth. The trees, rocks, and grass can cover its entirety, or be limited to a specific portion of the ground. It’s a Cartesian plane. Just change the numbers.

final_rough11

Here, a lot of trees and not much grass.

final_rough10

Here – a lot of grass, more rocks, and only a few trees.

And if I was creative (let’s face it, I meant “not lazy”), I’d make scenes with big hills, grass and rocks of varying size, trees along the path of a winding river, a dense forest of skinny trees, a dark scary forest of fat trees and millions of small pebbles instead of grass covering the ground.. the more I look at it, the more I realize that I actually made something! Hey, look at that!

Unfortunately, as much as I wanted to figure out coding shaders and lights, I ended up adding those manually. Cheater.

final_rough07

final_rough08

By the way, here, before I forget, the actual code for the thing:

from maya.cmds import *
from random import uniform as rand

### CREATE GROUND PLANE
polyPlane(w=40, h=40, n=’pln’)
for i in range(150):
    s = int(rand(0,120))
    d = rand(-0.5, 1)
    polyMoveVertex( ‘pln.vtx[‘+ str(s) +’]’, ty=d )
select(‘pln’)
polySmooth(dv=2)

### CREATE AND SHAPE GRASS
gBlade = polyPlane(w=0.5, h=2, sx=4, sy=6, n=’blade1′)
select( ‘blade1.vtx[30:34]’)
softSelect(sse=1, ssd=1.5)
scale( 0.2, 1, 1 )
select( ‘blade1.vtx[0:4]’)
softSelect(sse=1, ssd=1)
scale( 0.6, 1, 1 )
softSelect(sse=0)
select(‘blade1′)
rotate(90,0,0)
move(0,1,0)
for i in range(0,34):
   polyMoveVertex( ‘blade1.vtx[‘+ str(i) +’]’, ltz=rand(-0.1, 0.1) )
select( ‘blade1.vtx[30:34]’)
softSelect(sse=1, ssd=2)
rotate(rand(-10,10), rand(-10,10), rand(-60,60))

### GENERATE A FIELD OF GRASS
select(‘blade1′)
for g in range(1000):
    duplicate()
    move(rand(-19,19), 1.2, rand(-19,19))
    rotate( rand(0, 360), y=True)
    scale(rand(0.8,1.2), rand(0.4,1.5), rand(0.8,1.2))

### CREATE AND MODIFY TREE
polyCylinder(r=0.4, h=30, sy=30, n=’tree0′)
move(0,14,0, ‘tree0′)
vertnum = polyEvaluate(v=True)
for i in range(vertnum/2):
   s = int(rand(0,vertnum))
   d = rand(-0.1, 0.1)
   polyMoveVertex( ‘tree0.vtx[‘+ str(s) +’]’, ltz=d )

### GENERATE A FOREST OF TREES
for m in range(1,15):
    select(‘tree’+str(m-1))
    duplicate(‘tree’+str(m-1), rc=True)
    rename(‘tree’+str(m-1), ‘tree’+str(m))
    move(rand(-19,19), 16, rand(-19,19))
    scale(rand(0.8,1.2), rand(0.8,1.2), rand(0.8,1.2))

### CREATE ROCK
polyCube(n=’rock0′, w=3, h=3, d=3, sx=1, sy=1, sz=1)
polySmooth(dv=1)
scale(rand(0.8,1.2), rand(0.8,1.2), rand(0.8,1.2))
move(4,1,0, ‘rock0′)
select(‘rock0′)
vertnum2 = polyEvaluate(v=True)
for m in range(vertnum2/2):
   q = int(rand(0,vertnum2))
   polyMoveVertex( ‘rock0.vtx[‘+ str(q) +’]’, lt=(rand(-1, 1), rand(-1, 1), rand(-1, 1)) )
polySmooth(dv=1)
move(4, 0, 0, “rock0.scalePivot”,”rock0.rotatePivot”, absolute=True)

### GENERATE A BUNCH OF ROCKS
for p in range(1,12):
    select(‘rock’+str(p-1))
    duplicate(‘rock’+str(p-1), rc=True)
    rename(‘rock’+str(p-1), ‘rock’+str(p))
    vertnum3 = polyEvaluate(v=True)
    for m in range(vertnum3/3):
        q = int(rand(0,vertnum3))
        polyMoveVertex( ‘rock’ +str(p) +’.vtx[‘+ str(q) +’]’, lt=(rand(-0.1, 0.1), rand(-0.1, 0.1), rand(-0.1, 0.1)) )
    select(‘rock’+str(p))
    move(rand(-19,19), 1, rand(-19,19))
    scale(rand(0.2,1.4), rand(0.2,1.4), rand(0.2,1.4))

final_rough05

And as much as I like to say that I’ll pick up Python again and learn some more code in the future… We all know that I won’t. No, everyone thinks I will, but I actually won’t.

Knowing me, though, I will.

We’ll see. It’s a long life. Thank goodness, this semester is, at last, over.

Oct 182013
 

Life, like anything in life, needs direction.

That is more vague and pretentious than I intended it to be.

The point is, I sat down this week to do some Python coding in Maya, and realized that I really lack direction. That is, I know where I want to end up, but I still, still don’t have a good idea of how to get there. So, once again, I went looking for.. something. Something that wasn’t a random tutorial or another student’s question on Stack Overflow. And I found, once again, that there really aren’t many resources for scripting in Python for Maya, which is strange since Python is so widely used in 3D applications. A friend of mine said I should try looking in Mexico, because, apparently, there are plenty of pythons and Mayans there.

After some more digging, however, I found this – a collection of beginner video tutorials by Chris Zurbrigg. Zurbrigg is an independent software developer from Canada, who creates “animator-friendly” Python coding tutorials on the side. Most of the are really recent, from this summer, and they start from the very basics. This would have been perfect during week 1, since the majority of this course is beginner level, but I decided to go through it anyway, just in case I happen to learn something new.

Here’s one thing. To prove how widespread Python is in 3D applications, here’s a list of common ones, some industry standard, that employ Python scripting:

  • Maya, of course
  • Blender
  • Cinema 4D
  • Houdini
  • Nuke
  • Realflow

Well, what do you know, I use three of those on a daily basis! What this list ultimately shows is that Python is a connector between different stages of the 3D pipeline. It helps bring modeled objects into an animation program, then into a simulation program, then into a compositing program, etc. It all makes sense now.

— To be continued —

 

Oct 072013
 

Here I was, trying my best to just select and move some vertices with your regular old select(), move(), setAttr() functions… When all that I need to do is use the polyMoveVertex() function. If I knew that it existed from the start… Oh boy.

So the title of this post may be a bit over-dramatic, but necessary. We’re moving into scripted component editing.

I followed my plan for the ground plane, and finally got it to work. Here, I randomly select a number of vertices and move them a certain random amount within set bounds. Then I subdivide the the plane to smooth it out. And, ground!

lesson6_movedVerts

And, code:

from maya.cmds import *
from random import uniform as rand

def displaceVerts(obj):
     for i in range(60):
         s = int(rand(0,120))
         d = rand(-0.5, 1)
         polyMoveVertex( ‘obj.vtx[‘+ str(s) +’]’, ty=d )  #<– ty = translate along the y-axis

polyPlane(w=20, h=20, n=’pln’)
displaceVerts(pln)
select(‘pln’)
polySmooth(dv=2)

Similarly, I experimented a bit and made a tree. A rough tree, using the same technique as above: taking random vertices and displacing them on a local axis before subdividing the mesh to smooth it.

lesson6_treees

polyCylinder(r=0.5, h=20, sy=20, n=’tree0′)
move(0,10,0, ‘tree0′)  # <– moves the cylinder up to make up for the scaling

vertnum = polyEvaluate(v=True)  # <– grabs the number of vertices there are on the model, a useful variable to have

for i in range(vertnum/2):
   s = int(rand(0,vertnum))
   d = rand(-0.1, 0.1)
   polyMoveVertex( ‘tree0.vtx[‘+ str(s) +’]’, ltz=d )  # <– ltz= local translate along z-axis
   
select(‘tree0′)
polySmooth(dv=1)

(I think I like this style and can work with it. It’s not realistic, but it’s organic and has character. I can imagine where I’d go with it. Next week, adding colors.)

Taking it a step further, I made several trees to populate the scene. Doesn’t seem to look too bad…

lesson6_trees

However, upon closer inspection, I found that there are actually 60 trees in this scene. Oops. I tried altering my numbers and figuring out what is happening, but nothing helped. I came to the conclusion that I need a new random generation method, since the current one generates the same numbers every few iterations. Another task for next week.

For now, here’s the full code for the scene:

from maya.cmds import *
from random import uniform as rand

polyPlane(w=20, h=20, n=’pln’)

for i in range(60):
    s = int(rand(0,120))
    d = rand(-0.5, 1)
    polyMoveVertex( ‘obj.vtx[‘+ str(s) +’]’, ty=d )

select(‘pln’)
polySmooth(dv=2)

polyCylinder(r=0.5, h=20, sy=20, n=’tree0′)
move(0,10,0, ‘tree0′)
vertnum = polyEvaluate(v=True)

for i in range(vertnum/2):
   s = int(rand(0,vertnum))
   d = rand(-0.1, 0.1)
   polyMoveVertex( ‘tree0.vtx[‘+ str(s) +’]’, ltz=d )
   
select(‘tree0′)
polySmooth(dv=1)

for m in range(1,60):
    duplicate(‘tree’+str(m-1), rc=True)
    rename(‘tree’+str(m-1), ‘tree’+str(m))
    #randnum = rand(0,9)
    move(rand(-9,9), 6, rand(-9,9))
    scale(rand(0.4,0.8), rand(0.4,0.8), rand(0.4,0.8))

That’s all. Ground and six(ty) trees.

And honestly, in retrospect, these are still very simple and basic functions. I’m still figuring out what in Maya is easier done in Python, and what is more efficient to do manually. Once I get a good grasp of that, I expect my workflow and my production times to improve significantly.

Sep 302013
 

Having brushed up on functions, iteration, conditionals, and the such, I have come up with a very basic pseudo code for how my final project should work. Each file referenced into the scene would hold one single object – a type of tree, a flower, something that will be used to populate the scene. These files will be named and processed accordingly.

for all the files in a folder:
    create references
    put in separate groups (group for flowers, group for grass blades, etc.)

create ground plane
modify ground plane

for all objects in scene:
    if object is of a certain type (ex. flower):
        generate a certain number of these objects
        place them within certain bounds on the plane
        scale/rotate/otherwise modify to randomize
    if object is of a different type:
        # repeat procedures similar to the one above
        # for all referenced objects

All the textures will be created and applied within the referenced files. Lights will be added after all the objects have been created and placed.

 

And in the meantime, more practice.

I found this code online, from another 3D scripting blog. It uses succinct functions to create and arrange a row of spheres and give them different materials:

lesson5_colorfulBalls

Ahh. Nice. Here’s the code:

from maya.cmds import *
from math import *

def createMaterial( nm, color, type ):
    sets( renderable=True, noSurfaceShader=True, empty=True, n=nm + ‘SG’)
    shadingNode( type, asShader=True, name=nm )
    setAttr( nm+ ‘.color’, color[0], color[1], color[2], type=’double3′)
    connectAttr(nm+ ‘.outColor’, nm+ ‘SG.surfaceShader’)
    
def assignMaterial (nm, object):
    sets(object, edit=True, forceElement=nm+’SG’)

def assignNewMaterial( nm, color, type, object):
    createMaterial (nm, color, type)
    assignMaterial (nm, object)

polyPlane(name = “ground”, sx = 15, sy = 15, w = 15, h = 15)

for i in xrange(0,13):
    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
    setAttr( ‘ball’ + str(i) + ‘.translateX’, 6-i)
    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) + ‘]’ )

So I took this code for a ride and made something of my own. Fun fact: after about an hour of tweaking it and testing it, Maya crashes and doesn’t save the code. But I fixed it.

columns

These are my procedural columns. Would have been really useful last winter when I was modeling a Renaissance cathedral… Colors, placement, duplication, etc. – it’s all here:

def createMaterial( nm, color, type ):
    sets( renderable=True, noSurfaceShader=True, empty=True, n=nm + ‘SG’)
    shadingNode( type, asShader=True, name=nm )
    setAttr( nm+ ‘.color’, color[0], color[1], color[2], type=’double3′)
    connectAttr(nm+ ‘.outColor’, nm+ ‘SG.surfaceShader’)
    
def assignMaterial (nm, object):
    sets(object, edit=True, forceElement=nm+’SG’)

def assignNewMaterial( nm, color, type, object):
    createMaterial (nm, color, type)
    assignMaterial (nm, object)

def makeRows( lst, iter ):
    for n in lst:
        select(n)
        duplicate()
        move(5*(iter+1), 0, 0, relative=True)

polyPlane(name = “ground”, sx = 20, sy = 75, w = 20, h = 75)
move(0,0, -35)
assignNewMaterial( ‘ground’ + str(i), (1, 1, 1), ‘lambert’, ‘ground’ + str(i))
columnRow = []

for i in xrange(0,35):
    polyCylinder(name = ‘col’ + str(i), r = 0.3, h = 20)
    val = (1 + sin( (1.6/pi)*(6-i) ))/2
    setAttr( ‘col’ + str(i) + ‘.translateX’, -10)
    setAttr( ‘col’ + str(i) + ‘.translateY’, 10)
    setAttr( ‘col’ + str(i) + ‘.translateZ’, i*(-2))
    assignNewMaterial( ‘colShader’ + str(i), (val, val, val), ‘blinn’, ‘col’ + str(i) )
    columnRow.append(‘col’ + str(i))

for k in range(4):
    makeRows(columnRow, k)

And, of course, I can’t just leave it there. I add a couple of colorful lights and do a basic render, because I’m a big fan of lighting.

columnrender03

columnrender04

Blinn, blinn everywhere. Jim Blinn is my favorite. Also, I should learn how to code lights, since I like them so much.

Quick note: using this method to create and assign shaders is not the best. The material colors follow a sine pattern, therefore they repeat after so many iterations, but a new (duplicate) shader is created for each object regardless of that. So this scene had a lot of shaders, which would be difficult to sift through in the long run.

And lastly, this project may not exactly look like the organic environment I am hoping to achieve, but it was a valuable exercise in functions and procedural modeling overall. Hey, maybe my environment will have ancient ruins in it!

Sep 192013
 

All evening long, I’ve been trying to figure out how to randomly generate objects and materials and simultaneously assign the latter to the former. Here’s what I have:

spheres = []
for i in range(6):      

    thisS = polySphere(radius = rand(0.2,2))  
    move(rand(-8,8), rand(-8,8), rand(-8,8))
    spheres.append(thisS)
    
for k in range(len(spheres)):
    shader = shadingNode(“blinn”, asShader=True)

    shading_group = sets(renderable=True, noSurfaceShader=True, empty=True)  # <– the trouble line
    connectAttr(‘%s.outColor’ %shader , ‘%s.surfaceShader’ %shading_group)
    
    select(spheres[k])

    hyperShade( assign = shader )

    setAttr( “blinn1.color”, rand(0,1), rand(0,1), rand(0,1), type=”double3″ )

However, this doesn’t quite work. The code breaks on the line that creates a shading group set. What needs to happen is a new set created for every sphere in the scene. However, each time the program tries assigning the material it just created to an object, it attempts to push that object into the previous set instead of outright creating a new one. Sets have many flags that one can use, but no matter what I tried, I did not figure out how to create a new set during each iteration of the loop.

Yet.

There’s no such thing as giving up.

Sep 162013
 

How do you explain to a 6-months old kitten that you are not his mother?

I digress. Today we shall discuss not kittens, but Python variables, Maya nodes, and communication between the two.

If, instead of simply calling a command, assign that command to a variable, Maya will make the variable a list and hold the names of the nodes that were created by the command there. For example, this line of code:

nodes = polySphere();

will create a list like:

[u’pSphere1′, u’polySphere1′]

without actually holding any objects or data. The first is a transform node, the second – a shape node. The name of the shape node can now be stored in another variable for future access. It is not the best practice to go off the current state of nodes, so storing and calling on variables is the way to go. Maya can and will rename your nodes without telling you so. Use variables.

To grab a variable’s value (or the value of any attribute), use getAttr, and setAttr to change it:

sy = getAttr( variableName + ‘.scaleY’) ;

Good thing some objects in Python are mutable:

sy * = 2;
setAttr( variableName + ‘.scaleY’, sy );

So wait, there was something about transform versus shape nodes?

As I have written before, Maya stores data in the from of nodes that describe the objects’ attributes. The shape node, as the name implies, holds the geometry attributes of an object, and is the child of the transform node, which holds the object’s translation, rotation, and scale values. Unlike the getAttr and setAttr commands, the xform command is designed to work with transform nodes and can be used to quickly change them.

To connect nodes, use connectAttr and disconnectAttr. Easy, right? Go and build those complex procedural shaders using these two commands. Theoretically, that’s how you’d do it. Good luck doing it! On a different note, these commands can be used to set driven keys. This is a rigging concept – connecting the transform nodes of two objects so that, say, the rotation of a cube along the Y axis would affect the translation of a sphere along the Y axis:

lesson3_setDriven1

I cannot stress enough the importance and the widespread use of set driven keys. And this code makes it conveniently easy.

Back to connecting attributes… Here’s how you’d create a shader/material (and assign a color):

shader = shadingNode(“blinn”, asShader=True)  #creates the material
shading_group = sets( renderable=True, noSurfaceShader=True, empty=True )  #creates a shading group
connectAttr( ‘%s.outColor’ %shader ,’%s.surfaceShader’ %shading_group )  #connect material to the shading group

sph = polySphere()[0]
hyperShade( assign = shader )  #assign material to selected object

setAttr( “blinn1.color”, 0.9, 0.8, 0.7, type=”double3″ )  #set color

lesson3_assignShader

So I finally figured out how to do colors!! Nobody can stop me now, muahaha.

Now I can achieve last week’s dream and create a random generator of colors. In theory, I’d need to create a bunch of materials with different colors (made random by varying RGB values), connect to their own shading groups, and indiscriminately assign them to the objects in the scene. In a similar manner, I can create and assign random textures/bump maps/specular values/etc. In theory, I could create only a few color, texture, specular (and so on) nodes, and make a much greater number of materials based on their combinations. The only problem with all this is that… there will be a lot of fraggin materials in the scene.

That… is the task of next time.

 

P. S. This code snippet contains the weird “%” sign and “%s”. The modulo is the string formatting or interpolation operator, and allows for more concise formatting when multiple arguments are involved. %s indicates a string, %f indicates a float, etc. So basically – a shorter way to write things. Only practice will make me understand it better. More about this in the Python Standard Library, 5.6.2.

Sep 092013
 

I always do this: I start a tutorial, and not even halfway through I veer off into a different direction as my heart desires. Here I was, trying out simple beginner Python-Maya lessons, one of them being a short script for random bubbles:

lesson2_randomSpheres

As soon as I saw the result, I thought: I should randomize as many attributes and parameters as I can! Let’s try cubes. Let’s resize them and rotate and add colors.

lesson2_randomCubes

And here I encountered the first problem: Hypershade. Maya’s Hypershade works based on nodes. Shading networks are collections of connected nodes that make up an object’s “look” – colors, textures, etc. They consist of individual nodes for each of those attributes that define how the materials look. You’d think that you can just write a piece of code like this:

mc.select(all=True)
for i in range(255):
    mc.hyperShade(r=rand(0, i), g=rand(0, i), b=rand(0, i), assign=True)  # r, g, and b is not how you define colors. I used these variables just to demonstrate my idea

But no. That is not how it works. In order to do something seemingly as simple as assigning different colors to objects, you have to:

  1. Create shading groups
  2. Specify material types for the SGs
  3. Assign colors to the different materials
  4. Select objects
  5. Select shading group and connect to object

And, well, after almost two hours of research and trial and error, I decided that colors will have to wait. I will get to them, eventually, but not tonight.

However, I did make good progress in something. My final scene, being an organic environment, will have a whole field of grass: grass by the blade that nobody wants to duplicate and reposition manually. So I modeled a little grass blade, gave it a procedural texture (can’t wait to figure out how to do these in code!), shaped it a bit…

grassBlade

… and proceeded to experiment with manipulating it. The theory is simple: select, duplicate, move, turn, repeat. It took me several tries, but I finally generated a little scene that fit what I was imagining:

grassblades

This is the final clean code for this scene (minus the ground plane, materials, and creating the original object):

import maya.cmds as mc
from random import uniform as rand    

mc.select(‘grassBlade’)
for i in range(500):
    mc.duplicate()
    mc.move(rand(-6,6), rand(0.5,1), rand(-6,6))
    mc.rotate( rand(0, 360), y=True)
    mc.scale(rand(0.8,1.2), rand(0.4,1.5), rand(0.8,1.2))

Of course, then I decided to get a bit fancier and render it out:

lesson2_grass1a

This, right there, is 500 blades of grass. Yes, they have a bit more geometry than they need, but they occupy a fairly tiny bit of space. Generating this little scene took a little over 2 seconds. It seems like a short time and an insignificant detail, but it is anything but – when the time comes to create a full scene with thousands of objects, dozens of materials, several lights and additional particle effects, these seconds will add up, a lot. I can already tell that I’ll have to spend quite some time on code optimization later.

Python is fun, guys. Try it sometime.

Sep 052013
 

Nora was right. This is a bit addicting.

I am currently in the process of figuring out how to edit mesh in component mode. It’s so straightforward when using the Maya interface.. and a bit confusing and cumbersome when trying to code it. It would still be useful to use script for certain editing jobs, I just need to figure out how. I tried selecting a face on a cube to move it to a point in space, and ended up aligning all the vertices of that face along one axis. Give me a minute, I’ll get this.

But operating with objects seems like no problem at all!

lesson1_cubes

Python commands in Maya have flags (width/w, subdivisionsY/sy, all, name/n) to specify the objects’ attributes. Commands may be available in different modes: create, edit, query, or multiuse. Flags are often available only in certain modes – you cannot create every editable attribute. Consecutive commands will be executed on the last selected object, unless you specify otherwise (select something else).

Straightforward. Right?

Sep 052013
 

So Nora, Emily, and I are being brave pioneers.

Let’s back up to late freshman year. Thinking of what I want to do with the rest of my college years, I realize that I want a minor. A useful minor. A minor that could potentially boost my resume, widen my skill set, and give me a better chance at being hired in my professional field. All that good stuff. After doing some research into RIT’s minors, classes, and animation skills that companies are looking for, I decided on Computer Science. Managed to convince four of my friends to do it with me.

Unarguably, he following year has been a serious struggle.

We trudged through the introductory computer science sequence of classes, getting our feet wet with Python and Java. It was never easy and invariably painful, especially when stirred together with our other classes, films, and copious amounts of poor quality caffeine into one cringe-worthy brew of sophomore year. I have nothing but good memories, can you tell?

Still, in all seriousness, we do not regret it. We haven’t quit, after all. Two more classes to complete this deal, and we’re determined to get there.

Which brings me back to today’s reality. We have successfully argued our way to get independent study classes for this semester, with credits for our minor. Now the interesting thing here is that we’re the first to do this. Professors in the computer science department know little about Maya, and our animation professors are not coding specialists. We are quite on our own. It’s exciting and terrifying.

My independent study will revolve around, you guessed it, modeling. Modeling using code, and in our case – Python, because it’s the most widely used language for 3D software. I will attempt to model assets for an organic environment scene, generate different random variations of the fully populated scene, texture, light, and render it. With Python and minimal manipulation of Maya’s manual tools.

Here is my plan for the next 15 weeks:

  • Python commands in Maya (intro to Python in Maya)
  • Creating objects and manipulating them in 3D space
  • Object-oriented vs. procedural programming in Maya
  • Creating Maya tools with Python
  • Select scene to recreate; model one asset for it
  • Add variety to modeled asset to create a range of similar objects
  • Model all the assets needed for the scene
  • Populate the scene with objects
  • Create several different scenes with one set of objects by arranging them differently (randomly) with code
  • Add color and texture to objects
  • Optimize the program (break the code into components that would quickly build the scene)
  • Add lights to scene
  • Render the scene

It’s terrifying. And exciting.

And terrifying.