Flipping follicles in ribbon IK

try to normalize surface
it’s always better to work with normalized curves, surfaces

Yeah, my surface was 0 to spans instead of 0 to 1. But I just tried that, and it did not make a difference.

Have you ever experienced this flipping?

Hmm, I’m wondering how low this goes. Maybe try using the api to ask about the point, tangent and normals, and see if it flips there?

from maya import OpenMaya as om

def getSurfaceParams(obj, u, v):
    # Get the api object
    sel = om.MSelectionList()
    sel.add(obj)
    dagPath = om.MDagPath()
    sel.getDagPath(0, dagPath)
    fnLoft = om.MFnNurbsSurface(dagPath)

    # Query the data
    uTan, vTan, point = om.MVector(), om.MVector(), om.MPoint()
    norm = fnLoft.normal(u, v)
    fnLoft.getTangents(u, v, uTan, vTan)
    fnLoft.getPointAtParam(u, v, point)

    # Pull the numbers into python
    pyPoint = (point.x, point.y, point.z)
    pyNorm = (norm.x, norm.y, norm.z)
    pyUTan = (uTan.x uTan.y uTan.z)
    pyVTan = (vTan.x vTan.y vTan.z)
    return pyPoint, pyNorm, pyUTan, pyVTan

Interesting. Thanks for the script! This returns vectors that match a non-flipped state! I set some locators at the returned points during my flipping frames. The follicle flips, but the data does not…

image

My next theory was that, in our production, they decided to use units*0.1 as a world scale. Our models and rigs are quite small. So I was thinking I was hitting tiny value precision errors. But a larger test was flipping too.

So maybe there is a bug in the actual follicle node?

I also never thought to just try a pointOnSurfaceInfo node. That is also working fine.

Then from there, I guess maybe I’ll just use that, along with an aimConstraint.

Though I bet someone smart knows how to use those normal and tangent vectors to calculate the rotation, using a node?

Edit: Diving into this thread to construct a matrix to get rotation from the normalized vectors: https://forums.cgsociety.org/t/rotations-by-surface-normal/1228039/4

So here is a PyMEL script to attach a locator on to a surface using pointOnSurfaceInfo and a 4x4 matrix.

This is going to replace my heavy use of follicles!

Hat tip to Vasil Shotarov who already wrote about this: https://bindpose.com/maya-matrix-nodes-part-3-matrix-rivet/

(edit: changed .local to .worldSpace)

import pymel.core as pm

nurbs = pm.PyNode('ribbon_geo')
uPos = 0.2
vPos = 0.6

pointOnSurface = pm.createNode('pointOnSurfaceInfo')
nurbs.getShape().worldSpace.connect(pointOnSurface.inputSurface)
pointOnSurface.parameterU.set(uPos)
pointOnSurface.parameterV.set(vPos)

result = pm.spaceLocator(n='result')

# Compose a 4x4 matrix
mtx = pm.createNode('fourByFourMatrix')
outMatrix = pm.createNode('decomposeMatrix')
mtx.output.connect(outMatrix.inputMatrix)
outMatrix.outputTranslate.connect(result.translate)
outMatrix.outputRotate.connect(result.rotate)

'''
Thanks to kiaran at https://forums.cgsociety.org/t/rotations-by-surface-normal/1228039/4
# Normalize these vectors
[tanu.x, tanu.y, tanu.z, 0]
[norm.x, norm.y, norm.z, 0]
[tanv.x, tanv.y, tanv.z, 0]
# World space position
[pos.x, pos.y, pos.z, 1]
'''

pointOnSurface.normalizedTangentUX.connect(mtx.in00)
pointOnSurface.normalizedTangentUY.connect(mtx.in01)
pointOnSurface.normalizedTangentUZ.connect(mtx.in02)
mtx.in03.set(0)

pointOnSurface.normalizedNormalX.connect(mtx.in10)
pointOnSurface.normalizedNormalY.connect(mtx.in11)
pointOnSurface.normalizedNormalZ.connect(mtx.in12)
mtx.in13.set(0)

pointOnSurface.normalizedTangentVX.connect(mtx.in20)
pointOnSurface.normalizedTangentVY.connect(mtx.in21)
pointOnSurface.normalizedTangentVZ.connect(mtx.in22)
mtx.in23.set(0)

pointOnSurface.positionX.connect(mtx.in30)
pointOnSurface.positionY.connect(mtx.in31)
pointOnSurface.positionZ.connect(mtx.in32)
mtx.in33.set(1)
2 Likes

I know follicles can flip when you put them under 0 and 1 coordinarse bit it doesnt seem yo be your case.

If you wanna put some follicles under a surface you should try “Rivet” script. I Dont remember who scripted that one, sorry

Also, here’s a c++ plugin I know of that handles all that, plus length preservation:

1 Like

It looks like your ribbon is quads. Triangulate the mesh for reliable follicle behavior. FYI - it doesn’t work to triangulate a rigged mesh - that just triangulates it as a post deformation step. You’d need to freeze history, triangulate, transfer skin cluster. That works much better in my experience. What’s happening with a deforming quad mesh is that maya triangulates it differently as it deforms around. This flips the follicle.

hi Chris, Vasil Shotarov method is the way to go if you want clean en stable rivets.
I had this problem too and solved it my uising matrix rivets.

It’s not relative to the scale, the “rivet” script has a bug on tiny scale, the point en surface and 4*4 could be a good solution too, but less stable.

You can try ‘surface attach" node, it’ s a little graph in muscle sys. It does the job sometimes.

Hi skky, Surface Attach nodes are FAR too slow. Losses of 5 to 20 FPS. I do not recommend using them to anyone if they can be avoided. (Tested in Maya 2016 and 2017, maybe they are more parallel-friendly now, but I just avoid them completely.)

Depending of the number, but yeah generaly speaking i use matrix script now. Fast and robust.

Chipping in to say surface rivets can also be applied to meshes as well, circumventing the dreaded geometry constraint: Find the closest face on mesh, make a nurbs plane, and drive its control points with the face’s vertices. It won’t be absolutely rock stable, but unless the driver face flips, your constraint will be stable and very fast.

Hum, need to give a try :D! Seems to be a good idea!

That may work, but PLEASE don’t do that.
Just get Maya 2016 or later and use the PointOnPoly constraint.

I just did a brief test, and this seems to flip around like crazy. (Anyway, I won’t spend any more time on that. I’m not trying to pin things, personally. I’m animating parametrically along surfaces.)

Got a case I can test with? Or at least describe your test, and where it was failing? Every time I’ve worked with it, it’s been solid so long as I had good UVs. Though I admit I probably didn’t test too rigorously because I just started using the tool that was built for the job.

Basically, I’m trying to squash this “Bad PR” for the PointOnPoly constraint because it’s the Right Way™ to do it, and if there’s something wrong with it, it needs to get fixed rather than worked around again. (Sorry if I’m being intense about this. When we were forced to make the XSI->Maya transition, I ran into this stuff a lot, and I got a little salty)

  1. Make a poly plane
  2. Auto UVs (resulting in a clean flat plane of UVs, and yes I deleted history.)
  3. Select one vertex
  4. Select a locator
  5. PointOnPoly constraint
  6. Deform the plane gently and moderately.
  7. Bad PR

flippyflips

Maybe I’m missing something but isn’t pointOnPolyConstraints only job to constraint the position at the vertex and not the normal? Jitter is probably a result or re-calculating against the mesh as its a worldSpace constraint. Is this a bug? - in theory the locator should not even rotate seeing as its point.

From the docs:

pointOnPolyConstraint is undoable, queryable, and editable.

Constrain an object’s position to the position of the target object or to the average position of a number of targets. A pointOnPolyConstraint takes as input one or more “target” DAG transform nodes at which to position the single “constraint object” DAG transform node. The pointOnPolyConstraint positions the constrained object at the weighted average of the world space position target objects.

It’s your job to build the normals/bi-tangent from other pop’s I’d assume.

huh… The first time I tried specifying the transform as the target, it just did nothing. The locator didn’t follow.

Now as I test it, it is stable, and animatable along the poly’s “surface”, if you are transforming the mesh.

(When deforming the mesh, still flips like mad.)