/* ================================================================ ================================================================ ajhMeshConform v1.5 writted December 12, 2003 by Alex Hogan ================================================================ ====Description==== This script is meant to assist the user in creating a lower-poly surface from a high-poly surface, in the sad event that a smooth operation or two have been carried out and the history deleted. Note -- This script needs the Maya 5 BonusTools installed to function properly using NURBS Curves or Polygon meshes as the goal object ====Usage==== Create a low-resolution poly cage around your high-resolution model. Select the low poly cage (the Change object), then shift-select the high-poly model (the Goal object) and execute the script. Each vertex on the low-res cage will be moved to the closet point to it on the high resolution object, hopefully approximating the shape well enough to be a starting point to remodel from. ====Notes==== V1.5 January 21, 2004 - added plugin checking/loading at beginning of script V1.4 January 19, 2004 - added NURBS curves as a goal object option - added GUI - added polygon vertexes as a change object option - added NURBS surface cv's as a change object option - added NURBS curve cv's as a change object option V1.3 January 12, 2004 - added ability to select multiple change objects - added NURBS curves as a change object option - Still needs GUI V1.2 December 15, 2003 - added NURBS surfaces as an option for both the goal and change objects. - Still needs GUI V1.0 December 12, 2003 - Currently works only on polygon - polygon geometry. A future version will support NURBS surfaces as the change or goal objects. - No UI as of yet - only a progrss bar. A future version will have a GUI window with options. ================================================================ ================================================================ */ proc int ajhMCUI_refresh () { int $yeahRefresh = 0; if ( `window -exists ajhMeshConform` ) { if (`checkBox -q -value ajhMC_ChangeObjects`) $yeahRefresh = 1; } return $yeahRefresh; } global proc ajhMCUI_selectChange () { /* This procedure is intended to be used to populate the text field in the UI meant for the change objects. It populates it with all of the currently selected objects. */ string $selectedChangeObjects[] = `ls -sl`; //get the selection and size of the selection $selSize = size($selectedChangeObjects); string $objectList; for ( $i = 0; $i < $selSize; $i++) //make a simple string out of the selection list { $objectList = $objectList + " " + $selectedChangeObjects[$i]; } textField -e -text $objectList ajhMC_ChangeObjects; //change the field value to be our selection list } global proc ajhMCUI_selectGoal () { /* This procedure is intended to be used to populate the text field in the UI meant for the goal object. It populates it with the first of the currently selected objects. */ string $selectedChangeObjects[] = `ls -sl`; //get the selected objects textField -e -text $selectedChangeObjects[0] ajhMC_GoalObject; //change the field value in the UI to match } global proc ajhMCUI_doConform () { /* This procedure is intended to activate the ajhMeshConform script after selecting the objects in the user interface window. */ string $changeObjects = `textField -q -text ajhMC_ChangeObjects`; //get the change objects from the UI if ( $changeObjects == "" ) // if there aren't any objects listed in the UI to change, error window { warning "There are no objects selected to conform!"; confirmDialog -title "Warning!" -message "There are no objects selected to conform!" -button "OK" -defaultButton "OK"; return; } string $goalObject = `textField -q -text ajhMC_GoalObject`; //get the goal objects from the UI if ( $goalObject == "" ) // if there aren't any objects listed in the UI to conform to, error window { warning "There is no goal object selected!"; confirmDialog -title "Warning!" -message "There is no goal object selected!?" -button "OK" -defaultButton "OK"; return; } select -cl; //clear the selection eval( "select " + $changeObjects ); //make a select command from our UI field's values select -tgl $goalObject; //select the goal object last ajhMeshConform; //activate the script again with the current selection } proc ajhMeshConformUI () { /* This procedure is intended to create a user interface window where the user can select the change and goal obects with more visual interaction. */ if ( `window -exists ajhMeshConform` ) deleteUI -window "ajhMeshConform"; window -s 1 -rtf 1 -w 460 -h 150 -title "Mesh Conform UI" ajhMeshConform; columnLayout; text -l ""; text -l " Select NURBS surfaces, curves, CVs, or Polygon meshes or vertexes as conformable objects."; text -l ""; rowColumnLayout -nc 3 -h 100 -cw 1 300 -cw 2 10 -cw 3 150 ; text -w 200 -l "1. Select objects you wish to conform."; text -l ""; text -l ""; textField -ed 0 -w 400 ajhMC_ChangeObjects; text -l ""; button -l "Load Selected Objects" -c "ajhMCUI_selectChange"; setParent..; text -l ""; separator -w 460; text -l ""; text -l " Select NURBS surfaces, curves, and polygon surfaces to conform to."; text -l ""; rowColumnLayout -nc 3 -h 100 -cw 1 300 -cw 2 10 -cw 3 150 ; text -l "2. Select a single object to conform the above objects to."; text -l ""; text -l ""; textField -ed 0 -w 400 ajhMC_GoalObject; text -l ""; button -l "Load Selected Object" -c "ajhMCUI_selectGoal"; setParent..; separator -w 460 -h 20; separator -w 460 ; text -l ""; checkBox -label "Refresh while conforming" ajhMC_ChangeObjects; text -l ""; rowColumnLayout -nc 3; button -l "Conform" -c "ajhMCUI_doConform; deleteUI -window \"ajhMeshConform\";"; button -l "Apply" -c "ajhMCUI_doConform"; button -l "Cancel" -c "deleteUI -window \"ajhMeshConform\";"; setParent..; setParent..; showWindow; } proc string[] selectNurbsCurveCVS( string $curve ) { /* This procedure is intended to return an array containing all of the CV's of a Nurbs Curve node name passed to it. $surface MUST be a NURBS Curve shape node */ // To determine how many CVs are in a curve: int $numSpans = `getAttr ( $curve + ".spans" )`; int $degree = `getAttr ( $curve + ".degree" )`; int $form = `getAttr ( $curve + ".form" )`; int $numCVs = $numSpans + $degree; // Adjust for periodic curve: if ( $form == 2 ) $numCVs -= $degree; //To select all CVs in this curve: //select ( $curve + ".cv[0:" + ($numCVs-1) + "]" ); $allCVS = $curve + ".cv[0:" + ($numCVs-1) + "]"; $expandedCVS = `filterExpand -ex true -sm 28 $allCVS`; return $expandedCVS; } proc string[] selectNurbsSurfaceCVS( string $surface ) { /* This procedure is intended to return an array containing all of the CV's of a surface node name passed to it. $surface MUST be a NURBS shape node */ string $allCVS; string $expandedCVS[]; int $numSpansU = `getAttr ( $surface + ".spansU" )`; int $degreeU = `getAttr ( $surface + ".degreeU" )`; int $numSpansV = `getAttr ( $surface + ".spansV" )`; int $degreeV = `getAttr ( $surface + ".degreeV" )`; int $formU = `getAttr ( $surface + ".formU" )`; int $formV = `getAttr ( $surface + ".formV" )`; int $numCVsU = $numSpansU + $degreeU; // Adjust for periodic hull: if ( $formU == 2 ) $numCVsU -= $degreeU; int $numCVsV = $numSpansV + $degreeV; // Adjust for periodic hull: if ( $formV == 2 ) $numCVsV -= $degreeV; //To select all CVs in this surface: $allCVS = $surface + ".cv[0:" + ($numCVsU-1) + "][0:" + ($numCVsV-1) + "]"; $expandedCVS = `filterExpand -ex true -sm 28 $allCVS`; return $expandedCVS; } proc string getShape( string $xform ) { /* This procedure is intended to return as a string the first shape node found for a given transform node, or, if a shape node is given to return that shape node's name */ // If given node is not a transform, assume it is a shape // and pass it through. string $shapes[]; string $shape; if ( "transform" == `nodeType $xform` ) { $shapes = `listRelatives -fullPath -shapes $xform`; } $shape = $shapes[0]; return $shape; } proc meshConform ( string $changeObject, string $goalObject, string $CPM ) { /* This is the main work procedure of the meshConform script. It should be passed as strings the names of the change object, the goal object, and the closestPointOn* node. The procedure should get a list of all of the points on the change object, then recurse through them, setting the closestPointOn* node's inPosition X, Y, and Z attributes to the X, Y, and Z of the vertex or CV it is currently on. Then, move that vertex (or CV) to the resulting outPosition given by the node. */ string $changeObjectShape; string $changeType; string $vertexList[]; $checkForCV = `gmatch $changeObject "*.cv*"`; $checkForVTX = `gmatch $changeObject "*.vtx*"`; //get the shape of the changing object, but only if it's not a CV if (!($checkForCV || $checkForVTX)) $changeObjectShape = getShape( $changeObject ); //what nodeType is the change object? If we have a CV selection, define it ourselves! if (!($checkForCV || $checkForVTX)) $changeType = nodeType($changeObjectShape); else if ( $checkForCV ) $changeType = `nodeType $changeObject`; else if ( $checkForVTX ) $changeType = "mesh"; switch ( $changeType ) //base on the nodeType of the change object { case "nurbsSurface" : if ( $checkForCV ) { $vertexList = `filterExpand -ex true -sm 28 $changeObject`; } else { //if the change object is NURBS, get a list of all of it's CV's $vertexList = selectNurbsSurfaceCVS($changeObjectShape); // print $vertexList; } break; case "mesh": if ( $checkForVTX ) { $vertexList = `filterExpand -ex true -sm 31 $changeObject`; } else { //if the change object is a polygon mesh: // convert and faces, edges, or normal vertex to their vertexFace equivalent $vertexList = `polyListComponentConversion -toVertex $changeObject`; // expand the vertex face list into individual names, instead of Maya's default compressed format $vertexList = `filterExpand -sm 31 -ex true $vertexList`; } break; case "nurbsCurve": if ( $checkForCV ) { $vertexList = `filterExpand -ex true -sm 28 $changeObject`; //print $vertexList; } else { $vertexList = selectNurbsCurveCVS($changeObjectShape); //print $vertexList; } break; default: //If it is neither NURBS nor POLYs flag an error (though it does not follow through // with the error yet) $errorLevel = 1; break; } $vertexListSize = size($vertexList); // how many verts do we have? /* print "\n"; print $vertexListSize; print "\n"; */ //initialize the main progress bar global string $gMainProgressBar; progressBar -edit -beginProgress -isInterruptable true -status "Conforming Mesh, please wait..." -maxValue $vertexListSize // the bar knows how many verts it will count to now $gMainProgressBar; string $vtx; int $refresh = ajhMCUI_refresh(); for ($vtx in $vertexList) //recurse through all of the verts of the change object. { // for each vertex or CV, // get the worldspace position of the vertex float $vtxPos[] = `xform -ws -q -t $vtx`; // create commands to set the closestPoint node's inPosition attributes $cmd1 = "setAttr " + $CPM + ".inPositionX " + $vtxPos[0]; $cmd2 = "setAttr " + $CPM + ".inPositionY " + $vtxPos[1]; $cmd3 = "setAttr " + $CPM + ".inPositionZ " + $vtxPos[2]; // evaluate those commands eval($cmd1); eval($cmd2); eval($cmd3); float $newPos[]; // get the resulting closest worldspace location from the closestPoint node $cmd1 = "getAttr " + $CPM + ".positionX"; $cmd2 = "getAttr " + $CPM + ".positionY"; $cmd3 = "getAttr " + $CPM + ".positionZ"; $newPos[0] = eval($cmd1); $newPos[1] = eval($cmd2); $newPos[2] = eval($cmd3); // move the vert or CV to the closest point we grabbed xform -ws -t $newPos[0] $newPos[1] $newPos[2] $vtx; //if view refresh is on, refresh the display (to be added when GUI is made) if ( $refresh ) refresh; // if the user cancels from the progressbar, halt the script if(`progressBar -query -isCancelled $gMainProgressBar`) break; // if all is well and the user hasn't cancelled, update the progress bar progressBar -edit -step 1 $gMainProgressBar; } // after the loop ends, stop the progress bar progressBar -edit -endProgress $gMainProgressBar; } proc int ajhMCLoadPlugins ( string $goalType ) { int $isLoaded; int $returnVal = 0; switch ( $goalType ) //based on the nodetype of the goal object { case "nurbsCurve": $isLoaded = `pluginInfo -q -l closestPointOnCurve`; if ($isLoaded) { $returnVal = 1; break; } loadPlugin -qt closestPointOnCurve; $isLoaded = `pluginInfo -q -l closestPointOnCurve`; if ($isLoaded) { $returnVal = 1; break; } confirmDialog -title "Warning!" -message "The closestPointOnCurve plugin could not be found! Please install the Maya 5.0 Bonus Tools!" -button "OK" -defaultButton "OK"; $returnVal = 0; break; case "nurbsSurface" : $returnVal = 1; break; case "mesh": $isLoaded = `pluginInfo -q -l closestPointOnMesh`; if ($isLoaded) { $returnVal = 1; break; } loadPlugin -qt closestPointOnMesh; $isLoaded = `pluginInfo -q -l closestPointOnMesh`; if ($isLoaded) { $returnVal = 1; break; } confirmDialog -title "Warning!" -message "The closestPointOnMesh plugin could not be found! Please install the Maya 5.0 Bonus Tools!" -button "OK" -defaultButton "OK"; $returnVal = 0; break; default: print "\n"; print "Error in Plugin initialization - unrecognized surface type: "; print $goalType; print "\n"; $returnVal = 0; break; } return $returnVal; } global proc ajhMeshConform ( ) { /* This global procedure is intended to act as the master procedure for the meshConform script. Is should be executed when two objects of the supported types are selected. Supported types: NURBS surfaces and polygon objects. */ $selectionList = `ls -sl`; //get selection list $selSize = size( $selectionList ); if ( $selSize < 2 ) // if there aren't enough objects to run on, open the UI { ajhMeshConformUI; return; } string $goalObject = $selectionList[ ($selSize-1) ]; //get second object name string $goalObjectShape = getShape ( $goalObject ); // get the shapenode of the goal string $goalType = nodeType($goalObjectShape); //get the node type of the goal. if ( `ajhMCLoadPlugins( $goalType )` == 0 ) { error "Plugin Initialization Error!"; return; } // find out whether to use closestPointOnSurface (for NURBS // or closestPointOnMesh (polys) select -r $goalObject; $errorLevel = 0; switch ( $goalType ) //based on the nodetype of the goal object { case "nurbsCurve": // if goal is a NURBS curve, create and connect a closestPointOnCurve node string $CPM = `createNode closestPointOnCurve`; $cmd1 = "connectAttr -f " + $goalObjectShape + ".worldSpace[0] " + $CPM + ".inCurve"; eval($cmd1); break; case "nurbsSurface" : // if goal is a NURBS surface, create and connect a closestPointOnSurface node string $CPM = `createNode closestPointOnSurface`; $cmd1 = "connectAttr -f " + $goalObjectShape + ".worldSpace[0] " + $CPM + ".inputSurface"; eval($cmd1); break; case "mesh": // if goal is a Poly surface, create and connect a closestPointOnMesh node //string $CPM = `createNode closestPointOnMesh`; //$cmd1 = "connectAttr -f " + $goalObjectShape + ".worldMesh[0] " + $CPM + ".inMesh"; //eval($cmd1); string $CPM = `closestPointOnMesh`; break; default: $errorLevel = 1; // only set if the script is used on an unrecognized node type break; } if ($errorLevel) { print "\nUnrecognized goal object type!\n"; return; } for ($i = 0; $i < ($selSize-1); $i ++) { string $changeObject = $selectionList[ $i ]; //get first object name //execute the real work procedure, passing it the necessary arguments meshConform( $changeObject, $goalObject, $CPM ); //clean up our nodes and print a complete! message //delete $CPM; } print "Mesh Conform Complete!"; return; }