Python Tools - Session 2B
Objects on Curves and Mesh Vertices
January 2020
Session Navigation
Overview
Moving beyond the mosaic grid, this session explores placing objects along NURBS curves and on mesh vertices. You'll learn how to query curve parameters, evenly distribute duplicated objects along a path, and add randomized scaling for organic variation. These techniques are fundamental for environment art — think fences, railings, pillars, and foliage scatter.
Key Concepts
- FOR loops with 3D components — iterating over vertices (
mesh.verts) and curve CVs (curve.getCVs()) - Vertex positions — querying world-space positions with
vert.getPosition(space="world") - Curve parameterization — using
getPointAtParam()to find positions along a NURBS curve - findParamFromLength() — converting curve arc length to parameter space for even spacing
- Object duplication — using
pm.duplicate()to create copies of a source object - Random variation — applying
random.uniform()to scale for natural-looking distributions - Cleanup patterns — deleting previously generated objects by name before re-running the script
Distributing Objects Along a Curve
The core algorithm for evenly spacing objects along a curve:
- Get the total parameter length of the curve with
findParamFromLength(length()) - Divide by the desired number of objects to get the step size
- Loop through, incrementing the parameter and querying the position at each step
- Duplicate and move the source object to each position
Code
This script distributes duplicated objects evenly along a NURBS curve, with random scale variation. It first cleans up any previous run, then creates the new distribution:
import pymel.core as pm
import random
OBJ_NAME = "Pillar"
# Delete the previous locators
for obj in pm.ls(type='mesh'):
if OBJ_NAME in obj.name():
pm.delete(obj.getTransform())
path = pm.PyNode('curve1')
object = pm.PyNode('pCylinder1')
print path.numCVs()
for point in path.getCVs():
print point
print path.getPointAtParam(4.5)
print path.length()
# Now let's go along the curve
curve_length = path.findParamFromLength(path.length())
num_objects = 50
position = 0.0
for i in xrange(0, num_objects):
print position
position += (curve_length/num_objects)
position = min(position, curve_length)
# Duplicate the object and rename it so we can delete it
dupe = pm.duplicate(object)[0]
dupe.rename(OBJ_NAME)
pm.move(dupe, path.getPointAtParam(position, space='world'))
# Randomly uniformly scale each pillar
random_scale = random.uniform(0.7, .8)
dupe.sx.set(random_scale)
dupe.sy.set(random_scale)
dupe.sz.set(random_scale)
Scene Setup
Before running this script, you need two things in your Maya scene:
- A NURBS curve named
curve1— this is the path objects will follow - A polygon object named
pCylinder1— this is the source object to duplicate