// to export, select objects and call the routine. either select the shape // nodes directry or collapse the tree (delete history) and select the // transform nodes. if transform nodes are selected and the tree is not // collapsed, some shape nodes get exported twice. //////////////////////////////////////////////////////////// // utility routines. // take an array of strings and returns an array of the same strings but they // are unique and sorted. // there was system function stringArrayRemoveDuplicates // to do the same thing. /* proc string[] uniqStrArray(string $orig[]) { string $ret[]; int $idx = 0; string $s[] = sort($orig); string prev = ""; string $str; for($str in $s) { if(strcmp($dnode, $prev) != 0) { $ret[$idx++] = $str; } } return $ret; }*/ // look for blind data of node name $node. this is the name of the mesh node // the blind data is associated to. // for this routine to work, the history needs to be deleted on the mesh so // the blind data is directly connected to the mesh. proc string [] searchBlindData(string $node) { string $dt[] = `listHistory $node`; // for some reason, maya returns duplicate node names. we'll filter them // here. $dt = stringArrayRemoveDuplicates($dt); int $i = 0; string $ret[]; for($dnode in $dt) { // check the type of the node. if(`objectType -isType "polyBlindData" $dnode`) { $ret[$i++] = $dnode; } } return $ret; } proc string [] searchSelectedShapes() { string $select[] = `ls -sl -type "mesh" -type "transform"`; string $sel[]; int $idx = 0; // extend the scope to shapes connected to the selected transforms. for($node in $select) { if(`objectType -isType "mesh" $node`) { $sel[$idx] = $node; $idx++; } if(`objectType -isType "transform" $node`) { string $shapes[] = `listRelatives -shapes $node`; for($snode in $shapes) { $sel[$idx] = $snode; $idx++; } } } // uniquify it. $sel = stringArrayRemoveDuplicates($sel); // discard nodes which are ancestors of others. $idx = 0; string $ret[]; string $children[]; string $intersect = `stringArrayIntersector`; for($node in $sel) { $children = `listHistory -f true $node`; stringArrayIntersector -edit -reset $intersect; stringArrayIntersector -edit -intersect $children $intersect; stringArrayIntersector -edit -intersect $sel $intersect; if(size(`stringArrayIntersector -query $intersect`) <= 1) { $ret[$idx++] = $node; } } return $ret; } //////////////////////////////////////////////////////////// // routines for ply format output. // output the header of ply format to file $o. $node is the name of // the mesh node to be processed and $bd is the blind data to be output with. proc plyOutputHeader(int $o, string $node, string $bd[]) { fprint $o "ply\n"; fprint $o "format ascii 1.0\n"; fprint $o "comment exported from maya\n"; fprint $o ("comment node name in maya - " + $node + "\n"); // vertex format information. string $vattr = ($node + ".vrts"); fprint $o ("element vertex " + `getAttr -size $vattr` + "\n"); fprint $o "property float x\n"; fprint $o "property float y\n"; fprint $o "property float z\n"; // we might have extra data in the form of blind data. for($bnode in $bd) { fprint $o ("property int " + $bnode + "\n"); } // face format information. string $fattr = ($node + ".face"); fprint $o ("element face " + `getAttr -size $fattr` + "\n"); fprint $o "property list uchar int vertex_indices\n"; fprint $o "end_header\n"; } // output the vertex section of ply format to file $o. $node is the name of // the mesh node to be processed and $bd is the blind data to be output with. // $extra is the specific blind data field to be output. at this point, only // one blind data field of int type can be output. // $extra is the name of attribute (not the type). blind data editor labels // it as 'short name' or 'long name' (either should be fine). // TODO: change it so that it can output more than one blind data fields. // TODO: process color etc. properties associated to vertices as well. proc plyOutputVertices(int $o, string $node, string $bd[], string $extra) { string $vattr = ($node + ".vrts"); int $size = `getAttr -size $vattr`; for($i = 0; $i < $size; $i++) { string $velem = ($node + ".vrts[" + $i + "]"); float $v[] = `getAttr $velem`; //This prints out each component of the coord for($j in $v) { if( $j > 0.0001 || $j < -0.0001 ) { fprint $o ($j + " "); } else { fprint $o "0.0 "; } } //Now for this vertex, lets grab all of the weights string $welem = ($node + ".vtx[" + $i + "]"); string $cluster = findRelatedSkinCluster($node); float $w[] = `skinPercent -v -q $cluster $welem`; fprint $o (size($w) + " "); int $counter; $counter = 0; for($j in $w) { $counter = $counter + 1; if( $j > 0.0001 ) { //fprint $o $counter; //fprint $o " "; fprint $o $j; fprint $o " "; // fprint $o "\n"; } else { fprint $o "0.0 "; } } //End weights section if(`strcmp "" $extra` != 0) { for($bnode in $bd) { string $bn = ($bnode + ".vbd[" + $i + "]." + $extra); fprint $o ((string)`getAttr $bn` + " "); } } fprint $o "\n"; } } proc float ok(float $x) { if( $x < 0.00001 && $x > -0.00001 ) return 0; else return $x; } global proc sklOutput() { global string $plyOutputDir; string $fn = ($plyOutputDir + "/cop.skl"); int $o = `fopen $fn "w"`; //string $joints[] = `ls -type joint`; string $joints[] = `skinCluster -q -inf skinCluster1`; int $i = 0; string $joint; for($joint in $joints) { //Get the parent name and find it in the joint array string $parents[] = `listRelatives -p $joint`; string $parent = $parents[0]; int $parentid = -1; if( size($parent) > 1 ) { for($j = 0; $j< size($joints); $j++) { string $testname = $joints[$j]; if( `strcmp $testname $parent` == 0 ) { $parentid = $j; } } } float $pos[] = `joint -q -p -r $joint`; float $orient[] = `joint -q -o $joint`; float $rotate[] = `joint -q -ax -ay -az $joint`; float $lx[] = `joint -q -lx $joint`; float $ly[] = `joint -q -ly $joint`; float $lz[] = `joint -q -lz $joint`; print $joint; print "\n"; float $stxv[] = `joint -q -stx $joint`; float $styv[] = `joint -q -sty $joint`; float $stzv[] = `joint -q -stz $joint`; float $stx = $stxv[0]; float $sty = $styv[0]; float $stz = $stzv[0]; print ($stx + "\n"); print ($sty + "\n"); print ($stz + "\n"); fprint $o ($i + " " + $parentid + " " + $joint + " " + ok($pos[0]) + " " + ok($pos[1]) + " " + ok($pos[2]) + " " + ok($orient[0]) + " " + ok($orient[1]) + " " + ok($orient[2]) + " " + ok($rotate[0]) + " " + ok($rotate[1]) + " " + ok($rotate[2]) + " " + ok($stx) + " " + ok($lx[0]) + " " + ok($lx[1]) + " " + ok($sty) + " " + ok($ly[0]) + " " + ok($ly[1]) + " " + ok($stz) + " " + ok($lz[0]) + " " + ok($lz[1]) ); fprint $o "\n"; $i++; } fclose $o; } // output the face section of ply format to file $o. $node is the name of the // mesh node to be processed. proc plyOutputFaces(int $o, string $node) { string $fattr = ($node + ".face"); int $size = `getAttr -size $fattr`; for($i = 0; $i < $size; $i++) { string $felem = ($node + ".f[" + $i + "]"); string $v[] = `polyListComponentConversion -ff -tvf $felem`; int $vsize = size($v); int $items = 0; string $line = ""; for($j = 0; $j < $vsize; $j++) { string $t[]; tokenize $v[$j] "[]" $t; // now the vertex id should be in $t[1]. if(`gmatch $t[1] "*:*"`) { string $n[]; tokenize $t[1] ":" $n; int $s = (int)$n[0]; int $e = (int)$n[1]; for($k = $s; $k <= $e; $k++) { $line = $line + " " + $k; $items++; } } else { $line = $line + " " + $t[1]; $items++; } } fprint $o ($items + $line + "\n"); } } // output mesh data of node $node to a file. the file name is derived from // the node name and global variable $plyOutputDir. // the file name will get extension of .ply. proc plyOutputData(string $node, string $extra) { global string $plyOutputDir; string $fn = ($plyOutputDir + "/" + $node + ".ply"); print ("node: " + $node + " -> " + $fn + "\n"); int $output = `fopen $fn "w"`; string $bd[] = searchBlindData($node); plyOutputHeader $output $node $bd; plyOutputVertices $output $node $bd $extra; plyOutputFaces $output $node; fclose $output; } // callback for a dialog box. global proc int plySetOutputDir(string $filename, string $type) { global string $plyOutputDir; $plyOutputDir = $filename; return 1; } // export a mesh node into ply format. one node will be one file. this // routine processes all the nodes selected in the scene. if none is // selected, this routine does nothing. limited form of blind data can be // exported at the same time. global proc plyExport() { string $select[] = searchSelectedShapes(); if(size($select) == 0) { confirmDialog -title "Nothing to be done" -message "Select objects to be exported and try again."; return; } global string $plyOutputDir; $plyOutputDir = ""; fileBrowserDialog -mode 4 -actionName "Directory to put ply files in:" -fileCommand "plySetOutputDir"; if($plyOutputDir == "") { // canceled. return; } promptDialog -title "extra vertex information" -message "If necessary, type blid data name associated to vertices:" -button "ok"; string $extra = `promptDialog -q`; string $node; for($node in $select) { plyOutputData $node $extra ; } } global proc sklExport() { global string $plyOutputDir; $plyOutputDir = ""; fileBrowserDialog -mode 4 -actionName "Directory to put ply files in:" -fileCommand "plySetOutputDir"; if($plyOutputDir == "") { // canceled. return; } sklOutput; } //////////////////////////////////////////////////////////// // import method. // pick up a word from the input $in and return it. // line starts with 'comment' and 'property' are ignored since they are not // usful in ply file for our parsing. proc string plyPickword(int $in) { string $word = `fgetword $in`; while(`strcmp "comment" $word` == 0 || `strcmp "property" $word` == 0) { // comment started. eat up until the end of line. fgetline $in; $word = `fgetword $in`; } return $word; } // check if the next work in the input $in is $model. returns true or false. // the next word in the input is discarded regardless of wether it was $model. proc int plyCheckword(int $in, string $model) { string $word = `plyPickword $in`; if(`strcmp $model $word` != 0) { // wrong string. print ("-- " + $word + " found where " + $model + " was expected" + "\n"); return false; } return true; } // import header of ply file from input $in into node $node. // it sets a global variable $plyfile_import_vnum to be the numver of vertices // we expect in this file and $plyfile_import_fnum to be the number of faces // in the file. proc int plyImportHeader(int $in, string $node) { if(! `plyCheckword $in "ply"` || ! `plyCheckword $in "format"` || ! `plyCheckword $in "ascii"` || ! `plyCheckword $in "1.0"`) { return false; } // get the number of vertices. if(! `plyCheckword $in "element"` || ! `plyCheckword $in "vertex"`) { return false; } global int $plyfile_import_vnum; $plyfile_import_vnum = (int)`plyPickword $in`; string $vattr = ($node + ".vrts"); setAttr -s $plyfile_import_vnum $vattr; // get the number of faces. if(! `plyCheckword $in "element"` || ! `plyCheckword $in "face"`) { return false; } global int $plyfile_import_fnum; $plyfile_import_fnum = (int)`plyPickword $in`; string $fattr = ($node + ".face"); setAttr -s $plyfile_import_fnum $fattr; string $eattr = ($node + ".edge"); setAttr -s ($plyfile_import_fnum * 3) $eattr; if(! `plyCheckword $in "end_header"`) { return false; } return true; } // read vertex section of ply file from $in into node $node. proc plyImportVertices(int $in, string $node) { global int $plyfile_import_vnum; string $vattr = ($node + ".vrts"); for($i = 0; $i < $plyfile_import_vnum; $i++) { float $x = (float)`plyPickword $in`; float $y = (float)`plyPickword $in`; float $z = (float)`plyPickword $in`; fgetline $in; setAttr ($vattr + "[" + $i + "]") $x $y $z; } } // read face section of ply file format $in into node $node. // this only keeps face with three vertices. // TODO: make it to process faces with more vertices. proc plyImportFaces(int $in, string $node) { global int $plyfile_import_fnum; string $fattr = ($node + ".face"); string $eattr = ($node + ".edge"); for($i = 0; $i < $plyfile_import_fnum; $i++) { int $n = (int)`plyPickword $in`; if($n != 3) { print "-- skipping non-triangle face.\n"; continue; } int $v0 = (int)`plyPickword $in`; int $v1 = (int)`plyPickword $in`; int $v2 = (int)`plyPickword $in`; setAttr ($eattr + "[" + ($i * 3) + ":" + ($i * 3 + 2) + "]") $v0 $v1 0 $v1 $v2 0 $v2 $v0 0; } for($i = 0; $i < $plyfile_import_fnum; $i++) { setAttr -type polyFaces ($fattr + "[" + $i + "]") f 3 ($i * 3) ($i * 3 + 1) ($i * 3 + 2); } } // make sure the node $node is usable. if it doesn't exist, we create one // with that name. if it exists and it's not a mesh, we give up. if it // exists and it is a mesh, we make sure with user that we can overwrite it. proc int plyIdentifyNode(string $node) { if(`objExists $node`) { if(`objectType -isType "mesh" $node`) { // check if we have already asked. global int $plyfile_import_overwriteall; if($plyfile_import_overwriteall == 1) { return true; } else if($plyfile_import_overwriteall == 2) { return false; } // we need to ask. string $ans = `confirmDialog -title "Node exists" -message ("Node " + $node + " already exists.") -button "Overwrite" -button "Overwrite All" -button "Skip" -button "Skip All" -defaultButton "Skip"`; if(`strcmp $ans "Overwrite"` == 0) { // we assume that the object will be cleaned up by the // import process. return true; } else if(`strcmp $ans "Overwrite All"` == 0) { // keep the info and return. $plyfile_import_overwriteall = 1; return true; } else if(`strcmp $ans "Skip All"` == 0) { // keep the info and return. $plyfile_import_overwriteall = 2; return false; } else { return false; } } else { // can't do much. confirmDialog -title "Wrong type node" -message ("Node " + $node + " is not a mesh node... Skipping it"); return false; } } else { // just create one. createNode "mesh" -n $node; return true; } } // import data info from a file of filename $filename in directory $dir. proc int plyImportData(string $dir, string $filename) { string $t[]; tokenize $filename "." $t; string $node = $t[size($t) - 2]; if(! `plyIdentifyNode $node`) { return false; } print ("node: " + $filename + " -> " + $node + "\n"); string $path = ($dir + $filename); int $input = `fopen $path "r"`; if(`plyImportHeader $input $node`) { plyImportVertices $input $node; plyImportFaces $input $node; fclose $input; return true; } else { fclose $input; return false; } } // callback for a dialog. global proc int plySetLoadDir(string $filename, string $type) { global string $plyLoadDir; $plyLoadDir = $filename; return 1; } // import from a ply file. it asks user for a directory name where the // ply files are located and process all the files with .ply extension // in that directory. global proc plyImport() { global string $plyLoadDir; $plyLoadDir = ""; fileBrowserDialog -mode 4 -actionName "Directory to load the ply files from:" -fileCommand "plySetLoadDir"; if($plyLoadDir == "") { // canceled. return; } // look through the files in the selected directory. string $dir = ($plyLoadDir + "/"); string $files[] = `getFileList -fld $dir -fs "*.ply"`; if(size($files) == 0) { confirmDialog -title "No ply files in the directory" -message ($plyLoadDir + " directory does not contain ply files."); return; } global int $plyfile_import_overwriteall; $plyfile_import_overwriteall = 0; // 0 - not specified, 1 - overwrite all, 2 - skip all. string $file; for($file in $files) { if(! `plyImportData $dir $file`) { print ("failed to import: " + $file + "\n"); } } } //////////////////////////////////////////////////////////// // export routines for Pss // output vertices of mesh node $node to $o with blind data named $extra. proc pssOutputVertices(int $o, string $node, string $extra) { string $bd[] = searchBlindData($node); print ("-- " + $node + "\n"); string $vattr = ($node + ".vrts"); int $size = `getAttr -size $vattr`; for($i = 0; $i < $size; $i++) { string $velem = ($node + ".vrts[" + $i + "]"); float $v[] = `getAttr $velem`; for($j in $v) { fprint $o (" " + $j); } if(`strcmp "" $extra` != 0) { for($bnode in $bd) { string $bn = ($bnode + ".vbd[" + $i + "]." + $extra); fprint $o (" [" + $extra + " " + ((string)`getAttr $bn`) + "]"); } } fprint $o "\n"; } } // output faces of mesh node $node to $o. proc pssOutputFaces(int $o, string $node) { global int $pssVertexIndex; string $fattr = ($node + ".face"); int $size = `getAttr -size $fattr`; for($i = 0; $i < $size; $i++) { string $felem = ($node + ".f[" + $i + "]"); string $v[] = `polyListComponentConversion -ff -tvf $felem`; int $vsize = size($v); string $line = ""; for($j = 0; $j < $vsize; $j++) { string $t[]; tokenize $v[$j] "[]" $t; // now the vertex id should be in $t[1]. if(`gmatch $t[1] "*:*"`) { string $n[]; tokenize $t[1] ":" $n; int $s = (int)$n[0]; int $e = (int)$n[1]; int $k; for($k = (int)$n[0]; $k <= $e; $k++) { int $tt = ($k + $pssVertexIndex + 1); fprint $o (" " + $tt); } } else { int $tt = $t[1]; fprint $o (" " + ($tt + $pssVertexIndex + 1)); } } if($vsize >= 1) fprint $o ";\n"; } // add the index. string $vattr = ($node + ".vrts"); $pssVertexIndex = $pssVertexIndex + (int)`getAttr -size $vattr`; } // callback for a dialog. global proc int pssSetOutputFile(string $filename, string $type) { global string $pssOutputFile; $pssOutputFile = $filename; return 1; } // actually go though the selection list and export all the shape data. proc pssDoExport(string $filename, string $select[], string $extra) { int $output = `fopen $filename "w"`; print ("-> " + $filename + "\n"); // output the vertices. string $node; for($node in $select) { pssOutputVertices $output $node $extra; } fprint $output ";;\n"; // output the faces. global int $pssVertexIndex; $pssVertexIndex = 0; for($node in $select) { pssOutputFaces $output $node; } fprint $output ";;\n"; fclose $output; } // export the selected mesh in pss format. it will create a single file // combining all the selected mesh nodes. // blind data assigned to vertices will be output in non-standard format, so // jack wouldn't be able to read it if there is such data. global proc pssExport() { string $select[] = searchSelectedShapes(); if(size($select) == 0) { confirmDialog -title "Nothing to be done" -message "Select objects to be exported and try again."; return; } // get the file name. global string $pssOutputFile; $pssOutputFile = ""; fileBrowserDialog -mode 1 -actionName "Name of the output file" -fileCommand "pssSetOutputFile"; if($pssOutputFile == "") { // canceled. return; } promptDialog -title "extra vertex information" -message "If necessary, type blid data name associated to vertices:" -button "ok"; string $extra = `promptDialog -q`; pssDoExport($pssOutputFile, $select, $extra); } // ask user to set the global variables so that pssExportParFrame can be // correctly used. // for each frame, shapes are exported without user intervension. // the filename of each file would be base.#.pss. global proc pssExportSeq() { string $select[] = searchSelectedShapes(); if(size($select) == 0) { confirmDialog -title "Nothing to be done" -message "Select objects to be exported and try again."; return; } // get the file name. global string $pssOutputFile; $pssOutputFile = ""; fileBrowserDialog -mode 1 -actionName "Base name of the output file (without .pss)" -fileCommand "pssSetOutputFile"; if($pssOutputFile == "") { // canceled. return; } promptDialog -title "extra vertex information" -message "If necessary, type blid data name associated to vertices:" -button "ok"; string $extra = `promptDialog -q`; promptDialog -title "starting frame number" -message "give me the start frame number." -button "ok"; int $timeStart = `promptDialog -q`; promptDialog -title "ending frame number" -message "give me the end frame number." -button "ok"; int $timeEnd = `promptDialog -q`; for($i = $timeStart; $i <= $timeEnd; $i++) { // set the system animation time. currentTime $i; // create the file name. string $ext = ("." + ((string)$i) + ".pss"); string $file = ($pssOutputFile + $ext); // then, do export the data. pssDoExport($file, $select, $extra); } }