Fix motion path lattice twist in Maya


Despite the overall result may look good enoph:

the details are unexaptable…

The lattice is irregular:

And also I can’t rise the lattice resolution as much as I may need, after 400 division lattice become to broke my geometry. Here are 400 and 401 divisions:

If irregularity comes from bad algorithm of taking normals from surface vertices (and potentially can be fixed), I have no idea how to solve latice resolution limet. So I am looking for the new ideas for the same initial task: place stitches on surface.


You could blend the normals between the different faces. Many if your points will have the same normal direction because they lie on the same facem causing the irregularity. If you lerp between the normals of the point between two faces you get a smoother result.

As for the lattice division issue. You could just split it up into multiple sections never exceeding the 400 limit and afterwards combine the geometry together.


Not very clear how can I blend the normals exactly.

Currently, I have a list of point position (generated from the curve) so for each point, I search for the closest vertex on mesh and query it normal.


If you have 5 points with a matching normal, and after that another 5 with a matching ( because they are on the same face )

you can lerp the normals based on the middle of the points that are on the same face.
so if we take the 10 point example ( will start and end at the middle point on a face as before and after the middle point you would use the normal of the previous and next face ):

In this example we have 2 normals ( face_1_normal and face_2_normal ), point 3-5 are associated with face_1 and point 6-8 are associated with face_2.
The normals of the in between points are a blend between the two normals generating a smooth transition.

point_3: (face_1_normal * 1) + (face_2_normal * 0)
point_4: (face_1_normal * 0.8) + (face_2_normal * 0.2)
point_5: (face_1_normal * 0.6) + (face_2_normal * 0.4)
point_6: (face_1_normal * 0.4) + (face_2_normal * 0.6)
point_7: (face_1_normal * 0.2) + (face_2_normal * 0.8)
point_8: (face_1_normal * 0) + (face_2_normal * 1)


If you have 5 points with a matching normal, and after that another 5 with a matching ( because they are on the same face )

This part is already confusing… I am not dealing with geometry directly. I am using a NURBS curve (converted edge loop) to generate a list of positions in space with tangents. Then for each position in space, I get vertex normal from geometry, so I have 3 vectors: position, tangent, normal. With those vectors, I modify the transformation matrix of a cluster.

E.g I have a 3 lists of 3 vectors (normals, tangents and positions):
N = [(x1, y1, z1), (x2, y2, z2) … (x#, y#, z#)]
T = [(x1, y1, z1), (x2, y2, z2) … (x#, y#, z#)]
P = [(x1, y1, z1), (x2, y2, z2) … (x#, y#, z#)]

Normals are irregular. In such a case is it possible to blend them?


Indeed, you can group the P, T, N into bits where the normals are the same.
If you print your normal when you query them you will see that they are clustered because a lot of the points will exist on the same face. You can blend the normals between the groups generating a smooth transition between the two groups rather than a quick jump when you get a normal of a neighbouring face. Once you adjusted the normal you can generate a transformation matrix and position the clusters.


Hi Kiryha,

Does this code,

for i in range(num):
    parameter = c.findParamFromLength(length * increment * i)
    c.getPointAtParam(parameter, p)

produce uniform divisions along your curve? Im working on some pretty complex curve stuff now and had to rebuild the curve before getting parametric values along its length. Next to build a true curvature along this curve you’ll need to create a frenet frame - essentially its a moving coordinate frame along the curve to get the tangent/bi-tangent and normal to get the orientation - fortunately Maya has awesome curve support.

import maya.cmds as cmds
import maya.api.OpenMaya as apiOM

p0 = apiOM.MVector(0,0,0)
p1 = apiOM.MVector(0,1,0)
p2 = apiOM.MVector(1,1,0)
p3 = apiOM.MVector(1,0,0)

normal = (-1, 0, 0)

my_curve = cmds.curve(points=[p0, p1, p2, p3], editPoint=True)

count = 10
division = 1.0/(count-1)

for i in range(count):

   position = apiOM.MVector(cmds.pointOnCurve(my_curve, po=True, pr=i*division))
   tangent = apiOM.Vector(cmds.pointOnCurve(my_curve, nt=True, pr=i*division))

   bi_tangent = (tangent ^ normal).normal()
   normal = (tangent ^ bi_tangent).normal()

Note this isn’t complete - I’m just getting the data to build the transforms, but basically we’re recalculating the normal from the bi_tangent at each point, which itself is computed from the previously created normal hence the moving coordinate frame.


1 Like

Yes, I guess it does. Thanks for the info, Charles, that might be very useful (parallel transport for Maya I was looking for initially)!


What about killing the lattice entirely, creating a curve from the groove in the source (yellow) mesh and using your uniform code to place the stitches along it using a curve warp deformer?

More info here but looks like you can keep the length of the source mesh too.


1 Like

Curve wrap instead of a lattice… nice idea, will give it a try!


Ok, Curve Wrap may work… if I will be able to fix curve normals (or build a new curve with proper normals and tangents).

Is it possible to build a curve from points with proper normals (define normals explicitly for each point)?
And how can I visualize the curve normals?


If the stitches are essentially tubes/capsule is there a need to calculate the normal for each point? In any case the deformer info states:

  • Auto Normals: Automatically determines how to orient the mesh based on its normals.

As part of its aim options - so might be able to do the trick. If not may need to do some manual fix-ups.



In my case, the stitches are more complex than just a revolved shape so conforming them to the mesh ideally is an essential task.

Do not understand this part, orient the mesh based on its normals, how you can define object orientation on a curve using just object attributes, while you need to use curve attributes for that.

The Curve Rotation > Rotation and Twist Rotation does not work well on complex curves…


This may be what you looking for - from the info:

  • Aim Curve: Orients the mesh towards a specified curve.

Curve Warp Deformer

You can supply an additional curve the deforming mesh aims at - basically this is your normal. So you could build you base curve then build a projected curve to define the aiming. For this I’d just get the normal at the vertex as the input positions for it.

Looks like you can crank up the sampling accuracy too - which should keep the deformation more accurate.


1 Like

Am I understand correctly that there is no way to set curve normal in maya, and with normal() you can only query the value?


Yes, you can query curve information using curveInfo, nearestPointOnCurve, pointOnCurve type nodes for things such as the normal at a parameter value. But a normal itself is the product of tangent and u value which which i don’t think you can set.

This is why I compute my normal as a continuous coordinate frame from the previous input - but i still have to provide an origin normal.


1 Like

What the difference between curve command in cmds and PyMel?
cmds.curve(point=listPoints_NEW, name='CRV_NEW') working
pm.curve(point=listPoints_NEW, name='CRV_NEW') gives error:

# Error: TypeError: file C:\Program Files\Autodesk\Maya2018\Python\lib\site-packages\pymel\internal\ line 93: cannot convert dictionary update sequence element #0 to a sequence #

Where listPoints_NEW is a list of openMaya vectors.


Does pymel support the type conversion? - from the docs looks like a list of tuples.

1 Like

Right, convert to tuple fix it! Thanks!


Despite the aim curve is very bumpy (I use same uneven normals to offset the original curve), seems that it works fine. Need to test this workflow on more complex shapes of geometry and stitches.

But it super slow due to getting normals from mesh via finding nearest point on mesh.