//_________________________________________________________________________________________________ // // TITLE: BvhExport.mel // AUTHOR: Robert Bateman // E-MAIL: robthebloke@hotmail.com // DATE: 10/Jan/2001 // // NOTES: // // Given a root joint in Maya, the script will export the skeleton // and animation associated with it as a BVH mocap file. To use call... // // bvhExport( string $filename , // string $joint , // int $startFrame , // int $endFrame , // float $frameInterval , // int $fps ); // // and specify the following params, the filename you want to save the file as, // the name of the root joint of the skeleton, the start & end frames of the // animation, the number of frames between the keys held in the file - do you // want it to record every frame, every 3, every 2.5 ... The final param is // whether the animation is at 24fps, 25fps or whatever (BVH only deals with // seconds, has no concept of frames). // // Or, just run the script ........ // // // PROBLEMS: // // 1. This script has been known to omit branches of the skeletal tree. // //_________________________________________________________________________________________________ // // // Copyright © 2001 Robert Bateman // All Rights Reserved // // For educational purposes only. // Please do not republish in electronic or print form without permission // Thanks - robthebloke@hotmail.com // //________________________________________________________________________________________________ //___________________________________________________________________________________ GetRotationValue() ///////////////////////////////////////////////////////////////////////////////////// // // Recursive function, Sets Time to current sampling position in animation , // evaluates the current rotation value & spits it out. The function then works // out what joints are next in the hierarchy and calls itself once more for those // joints // global proc GetRotationValue( float $frameNum , string $name , int $fH , int $EndSite ) { // // Set current time along animation // currentTime $frameNum; // // Get downward connections // string $upCs[] = `listConnections -d true -s false $name`; // // Process all returned nodes // int $len = size( $upCs ); int $bHasChildren = 0; for( $i=0; $i<$len; $i++ ) { string $nT = `nodeType $upCs[ $i ]`; int $res = `strcmp "joint" $nT `; if( $res == 0 ) $bHasChildren++; } // // Overide - if no children and skelly contains end sites, // then this must be an endsite // if( $bHasChildren != 0 && $EndSite == 1 ) { // // Get rotation values & print to file // float $v[] = `getAttr ($name + ".r")`; $w = $v[0] + "\t" + $v[1] + "\t" + $v[2] + "\t"; fprint $fH $w; for( $i = 0; $i<$len; $i++ ) { string $nT = `nodeType $upCs[ $i ]`; int $res = `strcmp "joint" $nT`; // // Only process if node is a joint // if( $res == 0 ) { GetRotationValue( $frameNum , $upCs[ $i ] , $fH , $EndSite ); } } } } //___________________________________________________________________________________ GetAnimationData() ///////////////////////////////////////////////////////////////////////////////////// // // Runs through each key frame and outputs data to file // global proc GetAnimationData( float $fps , int $startFrame, int $endFrame , float $FrameInc , int $fH , string $RootJoint , int $EndSite ) { int $TotFrames = $endFrame - $startFrame; $TotFrames = $TotFrames/$FrameInc; float $currFrame = $startFrame; // // Output info detailing the animation data contained // float $out = ( $FrameInc / $fps ); fprint $fH "MOTION\nFrames: "; fprint $fH $TotFrames; fprint $fH "\nFrame Time: "; fprint $fH $out; fprint $fH "\n"; // // For each keyframe, output animation data // while( $currFrame <= $endFrame ) { // // Output root translation data // float $trans[] = `getAttr ($RootJoint + ".t")`; $w = $trans[0] + "\t" + $trans[1] + "\t" + $trans[2] + "\t"; fprint $fH $w; // // Output all rotational information // GetRotationValue( $currFrame , $RootJoint , $fH , $EndSite ); fprint $fH "\n"; print $currFrame; print "\n"; $currFrame += $FrameInc; } } //___________________________________________________________________________________ GetJointInfo() ///////////////////////////////////////////////////////////////////////////////////// // // Recursivly traverses the skeleton to output all tree data into // the BVH HIERARCHY section // global proc GetJointInfo( string $joint , string $spaces , int $fH , int $EndSite ) { // // Check to ensure that node is actually a joint // string $nodeType = `nodeType $joint`; int $result = `strcmp "joint" $nodeType`; if( $result == 0 ) { string $PreS = "\t"; $PreS += $spaces; // // get upward & downward connections // string $upCs[] = `listConnections -d true -s false $joint`; string $dwCs[] = `listConnections -d false -s true $joint`; // // calculate number of children if any present. // int $len = size( $upCs ); // // $bHasChildren will hold total number of children // int $bHasChildren = 0; for( $i=0; $i<$len; $i++ ) { string $nT = `nodeType $upCs[ $i ]`; int $res = `strcmp "joint" $nT `; if( $res == 0 ) $bHasChildren++; } setAttr ($joint + ".rx") 0; setAttr ($joint + ".ry") 0; setAttr ($joint + ".rz") 0; float $offset[] = `getAttr ($joint + ".t")`; if( $bHasChildren == 0 && $EndSite == 1 ) { string $out = $spaces + "End Site\n" + $spaces + "{\n" + $spaces + "\t" + "OFFSET " + $offset[0] + "\t" + $offset[1] + "\t" + $offset[2] + "\n"; fprint $fH $out; } else { string $out = $spaces + "JOINT " + $joint + "\n" + $spaces + "{\n"; fprint $fH $out; // // calculate and output, offset values // $out = $PreS + "OFFSET " + $offset[0] + "\t" + $offset[1] + "\t" + $offset[2] + "\n"; fprint $fH $out; // // Output channel order // string $channels = $PreS + "CHANNELS 3 Xrotation Yrotation Zrotation\n"; fprint $fH $channels; } if( $bHasChildren == 0 && $EndSite == 0) { string $out = $spaces + "\tEnd Site\n" + $spaces + "\t{\n" + $spaces + "\t\t" + "OFFSET\t0.0000\t0.0000\t0.0000\n" +$spaces + "\t}\n" ; fprint $fH $out; } // // If joint has children, output them also // if( $bHasChildren > 0 ) { for( $i=0; $i<$len; $i++ ) { string $nT = `nodeType $upCs[ $i ]`; int $res = `strcmp "joint" $nT `; if( $res == 0 ) { GetJointInfo( $upCs[ $i ] , $PreS , $fH , $EndSite ); } } } // // End joint // $out = $spaces + "}\n"; fprint $fH $out; } }; //___________________________________________________________________________________ bvhExport() ///////////////////////////////////////////////////////////////////////////////////// // // Recursivly traverses the skeleton to output all tree data into // the BVH HIERARCHY section // global proc bvhExport( string $filename , string $joint , int $startFrame , int $endFrame , float $frameInterval , int $fps , int $EndSite ) { // // Open file // int $fH = `fopen $filename "w"`; string $nodeType = `nodeType $joint`; int $result = `strcmp "joint" $nodeType`; if( $result == 0 ) { string $out = "HIERARCHY\n" + "ROOT " + $joint + "\n{\n"; fprint $fH $out; // // get upward connections // string $upCs[] = `listConnections -d true -s false $joint`; int $len = size( $upCs ); if( $len > 0 ) { string $PreS = "\t"; string $channels = $PreS + "CHANNELS 6 Xposition Yposition Zposition Xrotation Yrotation Zrotation\n"; // // calculate & output offset values // setAttr ($joint + ".rx") 0; setAttr ($joint + ".ry") 0; setAttr ($joint + ".rz") 0; setAttr ($joint + ".tx") 0; setAttr ($joint + ".ty") 0; setAttr ($joint + ".tz") 0; $out = $PreS + "OFFSET\t0.0000\t0.0000\t0.0000\n"; fprint $fH $out; fprint $fH $channels; // // Process all children // for( $i=0; $i<$len; $i++ ) { string $nT = `nodeType $upCs[ $i ]`; int $res = `strcmp "joint" $nT `; if( $res == 0 ) { GetJointInfo( $upCs[ $i ] , $PreS , $fH , $EndSite ); } } } $out = "}\n"; fprint $fH $out; // // Output animation data // GetAnimationData($fps,$startFrame,$endFrame,$frameInterval,$fH,$joint,$EndSite); } fclose $fH; }; //___________________________________________________________________________________ The Sketchy Land Of UI's ///////////////////////////////////////////////////////////////////////////////////// // // The following Section creates UI and calls all that is required. Dont Actually // need any of it, but it makes it all a bit more user friendly // // // Some Global Params for File Browser // string $bG = "Best Guess", $sA = "Save As", $cm = "onBrowserOK"; // // File Browser Dialog Callback // proc int onBrowserOK( string $s , string $n ) { print $s; textFieldButtonGrp -edit -text $s tField; return 1; } // // Kill Window when Cancel Is Pressed // proc onCancel() { deleteUI -window BvhExport; } // // Retreive All data from UI elements, call Export Function, Kill Window // proc onOK() { string $fileName = `textFieldButtonGrp -q -text tField`, $rootJoint = `textField -q -text jName`, $startFrame = `textFieldGrp -q -text start`, $endFrame = `textFieldGrp -q -text end`, $incFrame = `textFieldGrp -q -text sample`; int $HasEndSites = `checkBox -q -v EndSite`, $FramesPerSec = `radioButtonGrp -q -sl fps`; print "End Sites : "; print $HasEndSites; print "\n"; int $fps; if( $FramesPerSec == 1 ) $fps = 24; if( $FramesPerSec == 2 ) $fps = 25; int $sF = $startFrame, $eF = $endFrame; float $iF = $incFrame; bvhExport( $fileName , $rootJoint , $sF , $eF , $iF , $fps , $HasEndSites ); deleteUI -window BvhExport; } // // Creates Window & UI elements // proc sortUI() { string $command = "fileBrowser $cm $sA $bG 1"; window -rtf true -s false BvhExport; columnLayout; // // Joint name input // frameLayout -label "Skeleton Root Joint" -labelAlign "top" -borderStyle "in" -w 300 -cll true; textField jName; setParent ..; setParent ..; // // Export Options // columnLayout; textFieldGrp -cw 1 90 -cw 2 50 -cl2 "left" "left" -text "1" -label "Start Frame" start; textFieldGrp -cw 1 90 -cw 2 50 -cl2 "left" "left" -text "2" -label "End Frame" end; rowLayout -nc 2 -ct2 "left" "left" -cw 1 145; textFieldGrp -cw 1 90 -cw 2 50 -cl2 "left" "left" -text "2" -label "Sample Every" sample; text -label "Frames"; setParent ..; radioButtonGrp -cl2 "left" "left" -cw 1 30 -cw 2 50 -numberOfRadioButtons 2 -label "fps " -labelArray2 "24" "25" fps; checkBox -label "Skeleton Includes Endsites" -align "left" EndSite; setParent ..; // // File Request // frameLayout -label "Output File" -labelAlign "top" -borderStyle "in" -w 300 -cll true; textFieldButtonGrp -cw 1 250 -cw 2 50 -text "" -bl "Browse" -bc $command tField; setParent ..; setParent ..; // // Ok/Cancel // rowLayout -nc 2 -ct2 "right" "left" -cw 1 150 -cw 2 150; button -label "ok" -align "center" -w 80 -c "onOK"; button -label "cancel" -align "center" -w 80 -c "onCancel"; setParent ..; showWindow BvhExport; } // // Sets every thing in motion..... // sortUI();