// =========================================================================== // Copyright 2018 Autodesk, Inc. All rights reserved. // // Use of this software is subject to the terms of the Autodesk license // agreement provided at the time of installation or download, or which // otherwise accompanies this software in either electronic or hard copy form. // =========================================================================== // // // // // // // makeCurvesDynamic(int $version, string $args[] ) // // // None. // // // This command makes the selected nurbs curves into dynamic Maya hair objects. It is equivalent to // the menu call "makeSelectedCurvesDynamic". The selection may consist of curves as well a mesh to attach // the curves to using closest point of the first cv of each hair to the mesh. If a hairSystem node is also selected // the dyamic curves will be added to it, otherwise a new hair system will be created. // // // int $version The version of Maya for this command. This allows old scripts using this call to still work after changes to this function. // $args[0] = surfaceAttach If true then connect the hairs to a surface(also selected) basing the uv on the nearest point to the first curve cv // $args[1] = snapToSurface If true and attaching to a surface then also snap the curve to the surface. // $args[2] = matchPosition If true then make the input curve a degree one so resulting output curve exactly matches the position. // $args[3] = createOutCurves If true then output curves are created // $args[4] = createPfxHair If true then hair is created. // // // makeCurvesDynamic 2 { "0", "0", "0", "0", "1" }; // proc float convertToCmFactor() { string $unit = `currentUnit -q -linear`; if( $unit == "mm" ){ return( 0.1 ); } else if( $unit == "cm" ){ return( 1.0 ); } else if( $unit == "m" ){ return( 100.0 ); } else if( $unit == "in" ){ return( 2.54 ); } else if( $unit == "ft" ){ return( 30.48 ); } else if( $unit == "yd" ){ return( 91.44 ); } else { return( 1.0 ); } } proc float distBetweenPoints( float $pos1[], float $pos2[]) { float $dx = $pos1[0] - $pos2[0]; float $dy = $pos1[1] - $pos2[1]; float $dz = $pos1[2] - $pos2[2]; return( sqrt( $dx * $dx + $dy * $dy + $dz * $dz ) ); } global proc makeCurvesDynamic(int $version, string $args[]) { if( $version > 2 || $version < 0 ){ error( (uiRes("m_makeCurvesDynamic.kBadArgsError"))); return; } int $surfaceAttach = false; int $snapToSurface = false; int $matchPosition = false; int $doOutputCurves = true; int $doPfxOutput = false; int $numArgs = size($args); if( $numArgs > 2 ){ $surfaceAttach = $args[0]; $snapToSurface = $args[1]; $matchPosition = $args[2]; if( $version > 1 && $numArgs > 4){ $doOutputCurves = $args[3]; $doPfxOutput = $args[4]; } } string $hsystems[] = `getSelectedHairSystems`; string $hsys = "" ; if( size( $hsystems ) > 0 ){ $hsys = $hsystems[0]; } int $i, $j, $k, $l; string $curves[] = `ls -sl -dag -type nurbsCurve`; string $surfaces[] = `ls -sl -dag -type nurbsSurface`; string $meshes[] = `ls -sl -dag -type mesh`; int $numSurfaces = size( $surfaces ); int $numMeshes = size( $meshes ); int $attachToSurface = $numSurfaces > 0 || $numMeshes > 0; if( !$surfaceAttach ){ $attachToSurface = false; } if( size( $curves ) < 1 ){ warning((uiRes("m_makeCurvesDynamic.kNoCurvesSelecetd"))); return; } int $doCollideMesh = false; string $collideMesh = ""; if( `optionVar -exists makeCurvesDynamicCollideWithMesh` ){ $doCollideMesh = `optionVar -query makeCurvesDynamicCollideWithMesh`; } // set up temp calculation nodes for closest point on surface calculations string $meshClPos[]; string $surfaceClPos[]; string $surface, $mesh; float $minU[], $minV[]; float $sizeU[], $sizeV[]; if( $attachToSurface ){ for( $i=0; $i < $numSurfaces; $i++ ){ $surfaceClPos[$i] = `createNode closestPointOnSurface`; string $objShape = $surfaces[$i]; connectAttr ($objShape + ".worldSpace[0]") ($surfaceClPos[$i] + ".inputSurface"); $minU[$i] = `getAttr ($objShape+".mnu")`; float $maxU = `getAttr ($objShape+".mxu")`; $sizeU[$i] = $maxU - $minU[$i]; $minV[$i] = `getAttr ($objShape+".mnv")`; float $maxV = `getAttr ($objShape+".mxv")`; $sizeV[$i] = $maxV - $minV[$i]; } for( $i=0; $i < $numMeshes; $i++ ){ string $objShape = $meshes[$i]; int $pomLoaded = `pluginInfo -query -l nearestPointOnMesh`; if( !$pomLoaded ){ loadPlugin nearestPointOnMesh; $pomLoaded = `pluginInfo -query -l nearestPointOnMesh`; if( !$pomLoaded ){ warning((uiRes("m_makeCurvesDynamic.kCantLoadPlugin"))); return; } } // The following is to overcome a units bug in the nearestPointOnMesh plugin // If at some point it correctly handles units, then we need to take out the // following conversion factor. $meshClPos[$i] = `createNode nearestPointOnMesh`; connectAttr ($objShape + ".worldMesh") ($meshClPos[$i] + ".inMesh"); } } string $parent = ""; int $lastIndex[] = {0}; int $madeHairCurve = false; int $newHairSys = false; string $hsysGroup = ""; string $hsysOutputHairGroup = ""; for( $i = 0; $i < size( $curves ); $i++ ){ string $curve = $curves[$i]; string $con[] = `listConnections -sh 1 ($curve + ".worldSpace[0]")`; int $j; int $attachedToHairCurve = false; for( $j = 0; $j < size( $con ); $j++ ){ string $type = nodeType( $con[ $j ] ); if( $type == "follicle" ){ $attachedToHairCurve = true; continue; } } if( $attachedToHairCurve ){ continue; } int $intermediateObject = `getAttr ($curve + ".io")`; if( 0 != $intermediateObject ){ continue; } $madeHairCurve = true; if( $hsys == "" ){ // create the first time we hit a valid curve $hsys = `createNode hairSystem`; // we want uniform stiffness because the curves // are initially point locked to both ends removeMultiInstance -break true ($hsys + ".stiffnessScale[1]"); setAttr ($hsys + ".clumpWidth") 0.00001; setAttr ($hsys + ".hairsPerClump") 1; connectAttr time1.outTime ($hsys + ".currentTime"); string $nucleus = getActiveNucleusNode( false, true ); addActiveToNSystem( $hsys, $nucleus); connectAttr ($nucleus + ".startFrame") ($hsys + ".startFrame"); if( !$doOutputCurves ){ string $pfxHair = `createNode pfxHair`; connectAttr ($hsys + ".outputRenderHairs") ($pfxHair + ".renderHairs"); setAttr ($pfxHair +".displayPercent") 100; setAttr ($pfxHair +".drawAsMesh") false; } } string $hsysParent[] = `listTransforms $hsys`; if( size($hsysParent) > 0 ){ if( $hsysGroup == "" ){ $hsysGroup = ($hsysParent[0] + "Follicles"); if( !objExists($hsysGroup ) ){ $hsysGroup = `group -em -name $hsysGroup`; } } if( $doOutputCurves && $hsysOutputHairGroup == ""){ $hsysOutputHairGroup = ($hsysParent[0] + "OutputCurves"); if( !objExists($hsysOutputHairGroup ) ){ $hsysOutputHairGroup = `group -em -name $hsysOutputHairGroup`; } } } // Determine if the curve is a COS or just a regular curve. // This is cloned from "convertCurvesToStrokes.mel" int $COS = 0; string $relatives[] = `listRelatives -ap -pa -p $curve`; for ($j = 0; $j < size( $relatives ); $j++) { string $testMe = $relatives[$j]; if (`nodeType $testMe` == "transform") { string $testList[] = `listRelatives -ap -pa -p $testMe`; for ($k = 0; $k < size( $testList ); $k++) { string $subTest = $testList[$k]; if (`nodeType $subTest` == "curveVarGroup") { string $testList2[] = `listRelatives -ap -pa -p $subTest`; for ($l = 0; $l < size( $testList2 ); $l++) { if (`nodeType $testList2[$l]` == "nurbsSurface") { $COS = 1; $parent = $testList2[$l]; $j = size( $relatives ); $k = size( $testList ); $l = size( $testList2 ); } } } else { if (`nodeType $testList[$k]` == "nurbsSurface") { $COS = 1; $parent = $testList[$k]; $j = size( $relatives); $k = size( $testList ); } } } } else if (`nodeType $testMe` == "nurbsSurface") { $parent = $testMe; $COS = 1; $j = size( $relatives); } } if ( $COS == 1 ) { // the name in $curves is something like nurbsPlane->curve1: this // has illegal characters for a name so we use the default naming // string $cFS = `createNode "curveFromSurfaceCoS" -name ($curves[$i] + "cFS")`; string $cFS = `createNode "curveFromSurfaceCoS"`; // Do we need to check the index of the WS connections? connectAttr ($curve + ".ws[0]") ($cFS + ".curveOnSurface"); connectAttr ($parent + ".ws[0]") ($cFS + ".inputSurface"); $curve = $cFS; } string $surf = ""; // no surface to attach to float $u = 0; float $v = 0; int $doRest = false; int $doStart = 1; if( $attachToSurface ){ if( $COS == 1){ // attach needs a full curve node to parent to the follicle string $crv = `createNode nurbsCurve`; connectAttr ($curve + ".outputCurve") ($crv + ".create"); $curve = $crv; } float $nearDist = 10000000.0; float $curveBase[3] = `xform -q -ws -t ($curve + ".cv[0]")`; float $nearPos[3] = {0,0,0}; // find the worldspace position of the curve origin point // find the surface uv that is closest to the curve origin // among all selected geometry for( $j = 0; $j < $numSurfaces; $j++ ){ setAttr ($surfaceClPos[$j] + ".inPosition") -type double3 $curveBase[0] $curveBase[1] $curveBase[2]; float $surfPos[] = `getAttr ($surfaceClPos[$j] + ".position")`; float $dist = distBetweenPoints($surfPos, $curveBase); if( $dist < $nearDist ){ $nearDist = $dist; $surf = $surfaces[$j]; $nearPos = $surfPos; $u = getAttr( $surfaceClPos[$j] + ".parameterU"); $v = getAttr( $surfaceClPos[$j] + ".parameterV"); $u = ($u + $minU[$j])/$sizeU[$j]; $v = ($v + $minV[$j])/$sizeV[$j]; } } if( $numMeshes > 0 ){ float $convertFac = convertToCmFactor(); for( $j = 0; $j < $numMeshes; $j++ ){ setAttr ($meshClPos[$j] + ".inPosition") -type double3 ($curveBase[0]*$convertFac) ($curveBase[1]*$convertFac) ($curveBase[2]*$convertFac); float $surfPos[] = `getAttr ($meshClPos[$j] + ".position")`; $surfPos[0] /= $convertFac; $surfPos[1] /= $convertFac; $surfPos[2] /= $convertFac; float $dist = distBetweenPoints($surfPos, $curveBase); if( $dist < $nearDist ){ $nearDist = $dist; $nearPos = $surfPos; $surf = $meshes[$j]; $collideMesh = $surf; $u = getAttr( $meshClPos[$j] + ".parameterU"); $v = getAttr( $meshClPos[$j] + ".parameterV"); } } } if( $snapToSurface ){ move -r ($nearPos[0] - $curveBase[0]) ($nearPos[1] - $curveBase[1]) ($nearPos[2] - $curveBase[2]) $curve; } $doStart = 2; // if 2 then the curve worldspace position is preserved } int $deg = -1; string $rebuildTform; if( $matchPosition ){ $deg = getAttr( $curve + ".degree" ); if( $deg > 1 ){ // rebuild curve to degree 1 with history keeping cvs string $rebuild[] = `rebuildCurve -ch 1 -rpo 0 -rt 0 -end 1 -kr 0 -kcp 1 -kep 1 -kt 0 -s 0 -d 1 -tol 0.1 $curve`; $rebuildTform = $rebuild[0]; $rebuild = `ls -dag -type nurbsCurve $rebuildTform`; string $tforms[] = `listTransforms $curve`; string $curveTform = $tforms[0]; parent -r -s $rebuild[0] $curveTform; parent -r -s $curve $rebuildTform; // temporarily move $curve = $rebuild[0]; } } string $hname = createHairCurveNode($hsys, $surf, $u, $v, 0, $doOutputCurves, $doStart, $doRest, false, $curve, 1.0, $lastIndex, $hsysGroup, $hsysOutputHairGroup, 1); if( size( $hname ) > 0 ){ if( !$attachToSurface ){ setAttr ( $hname + ".pointLock" ) 3; setAttr ( $hname + ".restPose" ) 1; } if( $matchPosition ){ // match output degree with input setAttr ( $hname + ".degree" ) $deg; if( $deg > 1 ){ // curve was rebuilt... make the rebuild curve intermediate // and parent to the original curve tform string $curveTform[] = `listTransforms $curve`; string $sourceCurve[] = `ls -dag -type nurbsCurve $rebuildTform`; parent -r -s $sourceCurve[0] $curveTform[0]; delete $rebuildTform; setAttr( $curve + ".intermediateObject" ) true; } } } } // delete the follicle groups if we did not add any hairs to them if( objExists($hsysGroup ) ){ string $shapes[] = `ls -dag -type follicle $hsysGroup`; if( 0 == size($shapes) ){ delete $hsysGroup; } else { string $fol; for( $fol in $shapes ){ setAttr ($fol + ".startDirection") 1; } } } if( objExists($hsysOutputHairGroup ) ){ string $shapes[] = `ls -s -dag $hsysOutputHairGroup`; if( 0 == size($shapes) ){ delete $hsysOutputHairGroup; } } string $clPos; for( $clPos in $surfaceClPos ){ delete $clPos; } for( $clPos in $meshClPos ){ delete $clPos; } if( $madeHairCurve ){ if( $attachToSurface ){ attachNObjectToHair( $hsys, $collideMesh, $doCollideMesh ); } select $hsys; displayHairCurves "current" 1; } else { warning((uiRes("m_makeCurvesDynamic.kAlreadyDynamicHairCurves")) ); } }