Skip to content

Instantly share code, notes, and snippets.

@Onefabis
Last active September 27, 2018 20:30
Show Gist options
  • Save Onefabis/c78e2f6b7a634bc2ee69d786a6633de6 to your computer and use it in GitHub Desktop.
Save Onefabis/c78e2f6b7a634bc2ee69d786a6633de6 to your computer and use it in GitHub Desktop.
Create ribbon from selection
'''
1. Launch the script with the command:
import ribb_on
ribb_on.makeRibbSurface()
This will create NURBS surface which goes through all selected objects that need to be snapped to that surface.
After that you will be able to correct the shape of this surface by rotating the profile curve (or maybe scale it). After that you can delete both curves (profile and path)
2. Launch the second script:
import ribb_on
ribb_on.ribbMake( 3, 'parent', 1, 0 )
Where:
3 - number of cluster
'parent' - type of constraint
1 - 'relative' attribute of the cluster
0 - attach the surface to another selected objects.
'''
import maya.OpenMaya as om
import maya.mel as mel
import itertools
import maya.cmds as mc
def makeRibbSurface():
global sel
sel = mc.ls( sl=1 )
if not mc.objExists( 'grpFollicleRIG' ):
mc.group( n='grpFollicleRIG', em=1 )
mc.setAttr( 'grpFollicleRIG.visibility', 0 )
global pts
pts = om.MPointArray()
curveFn = om.MFnNurbsCurve()
for s in sel:
pos = mc.xform( s, q=1, ws=1, a=1, rp=1 )
pts.append(*pos)
curveObj = curveFn.createWithEditPoints( pts, 3, om.MFnNurbsCurve.kOpen, 0, 0, 1 )
dag = om.MDagPath()
path = dag.getAPathTo( curveObj )
mainCurve = path.fullPathName()
p = om.MPoint()
curveFn2 = om.MFnNurbsCurve()
curveFn2.setObject( path )
curveFn2.getPointAtParam( 0, p, om.MSpace.kWorld )
profileCurve = mc.curve( d=1, p=( [-0.5, 0, 0 ], [ 0.5, 0, 0 ]) )
mc.xform( profileCurve, cp=1 )
mc.xform( profileCurve, ws=1, t=( p[0], p[1], p[2] ) )
global surfaceObj
surfaceObj = mc.extrude( profileCurve, mainCurve, ch=1, rn=0, po=0, et=2, ucp=1, fpt=1, upn=1, rotation=0, scale=1, rsp=1 )
selSurf = om.MSelectionList()
selSurf.add( mc.listRelatives( surfaceObj[0], s=1 )[0] )
dagSurface = om.MDagPath()
selSurf.getDagPath( 0, dagSurface )
global apiSurface
apiSurface = om.MFnNurbsSurface( dagSurface )
global folArray
folArray = []
uutil = om.MScriptUtil()
u = uutil.asDoublePtr()
vutil = om.MScriptUtil()
v = vutil.asDoublePtr()
# create surfaceAttach nodes and connect them to the objects
for s in xrange(pts.length() ):
folicle = mc.createNode( "follicle" )
folicleTr = mc.listRelatives( folicle, type='transform', p=1 )
surfaceShape = mc.listRelatives( surfaceObj, s=1 )[0]
mc.connectAttr( folicle + ".outRotate", folicleTr[0] + '.rotate' )
mc.connectAttr( folicle + ".outTranslate", folicleTr[0] + '.translate' )
mc.connectAttr( surfaceShape + '.worldSpace', folicle + '.inputSurface' )
mc.setAttr( folicle + ".simulationMethod", 0 )
apiSurface.getParamAtPoint( pts[s], u, v, 0.1, om.MSpace.kWorld )
sU = mc.getAttr( surfaceShape + '.minMaxRangeU' )[0]
sV = mc.getAttr( surfaceShape + '.minMaxRangeV' )[0]
mc.setAttr( folicle + '.parameterU', om.MScriptUtil().getDouble(u) / sU[1] )
mc.setAttr( folicle + '.parameterV', om.MScriptUtil().getDouble(v) / sV[1] )
newFol = mc.rename( folicleTr, 'cFollicleAttach' )
newObj = mc.parent( newFol, 'grpFollicleRIG' )
folArray.append( newObj )
def ribbMake( numClusters=3, constraint='parent', rel=1, recreate=0 ):
global apiSurface
global surfaceObj
global pts
global sel
global folArray
newSel = mc.ls( sl=1 )
for s in xrange(pts.length() ):
if recreate == 1 and len( newSel ) == len( sel ):
print mc.ls( folArray[s] )
exec( 'mc.' + constraint + 'Constraint( mc.ls( folArray[s], l=1 ), newSel[s], mo=1 )' )
else:
exec( 'mc.' + constraint + 'Constraint( mc.ls( folArray[s], l=1 ), sel[s], mo=1 )' )
# get all CVs from NURBS surface
numUCVs = apiSurface.numCVsInU()
numVCVs = apiSurface.numCVsInV()
allCVs = [ x for x in itertools.product( range(numUCVs), range( numVCVs ) )]
CVName = []
CVCoord = []
CVParam = []
uutil2 = om.MScriptUtil()
u2 = uutil2.asDoublePtr()
vutil2 = om.MScriptUtil()
v2 = vutil2.asDoublePtr()
# get max U and V values
maxU = mc.getAttr( surfaceObj[0] + '.maxValueU' )
minU = mc.getAttr( surfaceObj[0] + '.minValueU' )
maxV = mc.getAttr( surfaceObj[0] + '.maxValueV' )
minV = mc.getAttr( surfaceObj[0] + '.minValueV' )
lenU = [ maxU if minU == 0 else minU ]
lenV = [ maxV if minV == 0 else minV ]
for a in allCVs:
CVName.append( surfaceObj[0] + '.cv[' + str(a[0]) + '][' + str(a[1]) + ']' )
pos2 = mc.xform( surfaceObj[0] + '.cv[' + str(a[0]) + '][' + str(a[1]) + ']', q=1, ws=1, a=1, t=1 )
pts2 = om.MPointArray()
pts2.append( *pos2 )
CVCoord.append( pos2 )
apiSurface.closestPoint( pts2[0], u2, v2, 0, 0.1, om.MSpace.kWorld )
CVParam.append( [om.MScriptUtil().getDouble(u2), om.MScriptUtil().getDouble(v2)] )
clusterUV = []
clusterNames = []
# create clusters
for c in xrange(numClusters):
cpnt = om.MPoint()
inCoord = lenV[0] / ( numClusters - 1 ) * c
clusterUV.append( [lenU[0] / 2, inCoord] )
apiSurface.getPointAtParam( lenU[0] / 2, inCoord, cpnt, om.MSpace.kWorld )
newCluster = mc.cluster( CVName )
clusterNames.append( newCluster )
# set Relative mode for the cluster
mc.setAttr( newCluster[0] + '.relative', rel )
mc.setAttr( newCluster[0] + 'Handle.rotatePivotX', cpnt[0] )
mc.setAttr( newCluster[0] + 'Handle.rotatePivotY', cpnt[1] )
mc.setAttr( newCluster[0] + 'Handle.rotatePivotZ', cpnt[2] )
mc.setAttr( newCluster[0] + 'Handle.translateX', 0 )
mc.setAttr( newCluster[0] + 'Handle.translateY', 0 )
mc.setAttr( newCluster[0] + 'Handle.translateZ', 0 )
mc.setAttr( newCluster[0] + 'Handle.originX', cpnt[0] )
mc.setAttr( newCluster[0] + 'Handle.originY', cpnt[1] )
mc.setAttr( newCluster[0] + 'Handle.originZ', cpnt[2] )
# Reset it 'bind state' pose to the new one
mc.cluster( newCluster[0], e=1, bs=1 )
for c in xrange( len(clusterNames) ):
clMDelta = clusterUV[c][-1] - clusterUV[c - 1][-1]
clPDelta = clusterUV[c - len(clusterNames) + 1][-1] - clusterUV[c][-1]
for x in xrange( len(CVName) ):
mc.setAttr('{0}.weightList[0].weights[{1}]'.format(clusterNames[c][0], x), 0 )
if lenV[0] > 0:
if clMDelta > 0:
for x in xrange( len(CVName) ):
if clusterUV[c - 1][-1] <= CVParam[x][-1] <= clusterUV[c][-1]:
mc.setAttr('{0}.weightList[0].weights[{1}]'.format(clusterNames[c][0], x), ( CVParam[x][-1] - clusterUV[c - 1][-1] ) / (clusterUV[c][-1] - clusterUV[c - 1][-1]) )
if clPDelta > 0:
for x in xrange( len(CVName) ):
if clusterUV[c][-1] <= CVParam[x][-1] <= clusterUV[c - len(clusterNames) + 1][-1]:
mc.setAttr('{0}.weightList[0].weights[{1}]'.format(clusterNames[c][0], x), 1 - ( CVParam[x][-1] - clusterUV[c][-1] ) / (clusterUV[c - len(clusterNames) + 1][-1] - clusterUV[c][-1]) )
else:
if clMDelta < 0:
for x in xrange( len(CVName) ):
if clusterUV[c - 1][-1] >= CVParam[x][-1] >= clusterUV[c][-1]:
mc.setAttr('{0}.weightList[0].weights[{1}]'.format(clusterNames[c][0], x), ( CVParam[x][-1] - clusterUV[c - 1][-1] ) / (clusterUV[c][-1] - clusterUV[c - 1][-1]) )
if clPDelta < 0:
for x in xrange( len(CVName) ):
if clusterUV[c][-1] >= CVParam[x][-1] >= clusterUV[c - len(clusterNames) + 1][-1]:
mc.setAttr('{0}.weightList[0].weights[{1}]'.format(clusterNames[c][0], x), 1 - ( CVParam[x][-1] - clusterUV[c][-1] ) / (clusterUV[c - len(clusterNames) + 1][-1] - clusterUV[c][-1]) )
#makeRibbSurface()
#ribbMake( 5, 'parent', 1, 0 )
@PeterDrakeNichols
Copy link

Hey cool script!
Wanted to share mine for a twisty limb.
really annoying how 'cMuscleSurfAttachSetup();' has like no documentation or python equivalent!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment