Search code examples
pythonmaya

Maya : Grass generator python


Hi guys i am trying to create a grass generator that take a cone, deform it, duplicate it, and then spread it randomly on the geometry that i selected, using maya python.

i am a beginner, i only reached to the duplication part, how can i spread the blades(grass) on a surface ???,thank you !

import maya.cmds as MC
from random import uniform as RN

blade = MC.polyCone( sx=3, sy=5, sz=0, r=0.04, h=1, ax=(0, 1, 0))
MC.setAttr (blade[0] + '.translateY', (.5)) 
MC.delete(ch=True)
MC.polySoftEdge ( a=0 )

#adding deformer

bend = MC.nonLinear( type='bend', lowBound=0, highBound = 2,  
curvature=30)
vtxPos = MC.xform(blade[0]+'.vtx[0]', q=True, t=True, ws=True)
MC.xform(bend, t=(0,vtxPos[1], 0))

MC.select (blade[0])
MC.delete(ch=True)
MC.makeIdentity(apply=True, t=1, r=1, s=1, n=0)
groupblades = MC.group(empty=True, name=blade[0] + '_grp#') 

for i in range(200):
    obj = MC.instance(blade[0])
    MC.move(RN(1,-1), 0, RN(1,-1), blade[0])
    MC.rotate(0, RN(0,360),0, blade[0])
    MC.parent(obj, groupblades)

Solution

  • There's definitely lots of different approaches you can take to scatter objects on a surface. You can use geometry and normal constraints. There's MASH's framework. But I think using a follicle is easy enough and requires less overhead. The only catch is that the surface must have uvs as this is how we'll move the follicle around.

    So here's what I'm suggesting:

    1. Create a follicle, and connect it to the surface you want to scatter onto.
    2. Create your grass object.
    3. Each time you instance a new grass, pick a random uv for the follicle to pop to. This will align it to the face's position and normal.
    4. Use the follicle's transform to move/rotate your grass to.
    5. Rinse and repeat steps 3 & 4.

    Here's a full example:

    import maya.cmds as cmds
    import random
    
    surface = "pPlane1"  # Specify the object you want to scatter on.
    
    fol_shape = cmds.createNode("follicle")  # Create a new follicle object.
    fol = cmds.listRelatives(fol_shape, f=True, parent=True)[0]  # Get the follicle's transform.
    
    # Below we are connecting the follicle to the surface so we can control it with its uvs.
    cmds.connectAttr("{}.outMesh".format(surface), "{}.inputMesh".format(fol))
    cmds.connectAttr("{}.worldMatrix[0]".format(surface), "{}.inputWorldMatrix".format(fol))
    cmds.connectAttr("{}.outTranslate".format(fol_shape), "{}.translate".format(fol))
    cmds.connectAttr("{}.outRotate".format(fol_shape), "{}.rotate".format(fol))
    cmds.setAttr("{}.simulationMethod".format(fol_shape), 0)
    
    # Begin creating blade of grass.
    blade = cmds.polyCone(sx=3, sy=5, sz=0, r=0.04, h=1, ax=(0, 1, 0))
    cmds.setAttr(blade[0] + '.translateY', .5) 
    cmds.delete(ch=True)
    cmds.polySoftEdge(a=0)
    
    # Setting wirecolor to green so it's easier to see.
    cmds.setAttr(blade[0] + ".overrideEnabled", True)
    cmds.setAttr(blade[0] + ".overrideRGBColors", True)
    cmds.setAttr(blade[0] + ".overrideColorRGB", 0.06, 0.2, 0.06)
    
    bend = cmds.nonLinear(type='bend', lowBound=0, highBound=2, curvature=30)
    vtxPos = cmds.xform(blade[0] + '.vtx[0]', q=True, t=True, ws=True)
    cmds.xform(bend, t=(0, vtxPos[1], 0))
    
    cmds.select(blade[0])
    cmds.delete(ch=True)
    cmds.makeIdentity(apply=True, t=1, r=1, s=1, n=0)
    groupblades = cmds.group(empty=True, name=blade[0] + '_grp#') 
    
    for i in range(400):
        obj = cmds.instance(blade[0])
    
        # Randomly set the follicle's uv so it's jumps somewhere else on the surface. Value needs to be between 0.0-1.0
        cmds.setAttr("{}.parameterU".format(fol_shape), random.random())
        cmds.setAttr("{}.parameterV".format(fol_shape), random.random())
        cmds.matchTransform(obj, fol)  # Make grass match follicle's position/rotation/scale.
        cmds.rotate(-90, random.random() * 360, 0, obj, r=True)  # Rotate it upright and give it random twist.
        cmds.move(0, 0.4, 0, obj, r=True)  # The grass's pivot is in the center, so need to offset translation a bit up.
        cmds.parent(obj, groupblades)
    
    cmds.delete(fol)  # Follicle no longer needed, so delete it.
    

    There's one part where I need to move the grass up a bit because the pivot point is at the grass's center. This is a minor hack and the real solution would be to build the grass with the pivot point at its base. Doing that would solve any floating grass and make randomizing scaling easier.

    Here's what it will output:

    Scatter example