Home Technical Talk

Vertex normal script for Maya

polycounter lvl 7
Offline / Send Message
Pomperi polycounter lvl 7
Hi everyone!

I just wanted to share a script I've done for Maya to quickly be able to set the vertex normals based on the face normals. I know there are a few ways to do this within native maya, as well as a few scripts, but I haven't been able to find one which averages the normals for verts shared by multiple selected faces. This can be quite annoying when setting up curved or round surfaces.

oBTdSYt.jpg

The GetVertNormalFromFace script for 3ds Max does a good job for this if I remember correctly. What mine does is essentially the same but in Maya. Just make a face selection of the faces you want to set the vert normals for and run the script.

98uBPfL.jpg

To run the script, just paste into the script editor as python and drag it to a shelf, or set it up as a hotkey


Here's the script:
# ---------------------------------------------------------------------------------------
# - pompVertNormFaceAvg
#
# - Sets the vertex normals of a face selection,
#   averaging normals shared by multiple selected faces.
#
#
# - by Pontus Karlsson 2013 (www.pomperi.com)
# ---------------------------------------------------------------------------------------

import maya.cmds as cmds

faces = cmds.filterExpand(sm=34, ex=True)
verts = cmds.ls((cmds.polyListComponentConversion(faces, ff=True, tv=True)), flatten=True)
normals = [[], [], []]

for v in verts:
    conFaces = cmds.ls(cmds.polyListComponentConversion(v, fv=True, tf=True), flatten=True)
    shaFaces = list(set(conFaces).intersection(set(faces)))
    faceNorm = cmds.polyInfo(shaFaces, fn=True)
    
    for normal in faceNorm:
        label, vertex, x, y, z = normal.split()
        normals[0].append(float(x))
        normals[1].append(float(y))
        normals[2].append(float(z))
        
    x_avg = (sum(normals[0]) / len(shaFaces))
    y_avg = (sum(normals[1]) / len(shaFaces))
    z_avg = (sum(normals[2]) / len(shaFaces))
    
    cmds.select(v)
    cmds.polyNormalPerVertex(xyz = (x_avg, y_avg, z_avg))
    
    normals[:] = [[], [], []]

cmds.select(cl=True)

I hope someone finds it useful emot_smile.gif

Replies

  • passerby
  • Pomperi
    Offline / Send Message
    Pomperi polycounter lvl 7
    Workflow-wise it's the exact same approach; you select your faces and run the script and it sets the vert normals for you. The main difference though is the way it treats verts that share faces of your selection. The script you referred to goes through face by face and sets the verts to the normal of the face, goes to the next face and sets all verts connected to that face to its normal, which means if a vert is shared between two faces it will only have the normal from the last face. If you run that script on the sides of a cylinder you will get this result:

    mmyqUQS.jpg

    A1lYpJM.jpg

    I hope that clears things up a bit :)
  • passerby
    Offline / Send Message
    passerby polycounter lvl 9
    ah, ya just doesn't mess up curved selections. Not sure if possible from the maya.cmds modual but also a way of dealing with spilt normals would be good too, since if used on faces that have verts with spilt normals it breaks them. but that could be a rather difficult task with the averaging, and would likely need pymel or openmaya.
  • Pomperi
    Offline / Send Message
    Pomperi polycounter lvl 7
    Yeah good point! It wouldn't be too hard to have the script set the normals of the vertex face rather than the vertex normal. That way you would get a split on the border of your face selection if the bordering faces have another vertex face direction. I'll definitely look into it!
Sign In or Register to comment.