#!/usr/bin/perl # # bvh2hanim - convert bvh mocap file to vrml format, print to stdout # (not really to hanim yet) # # Assumes Filmbox joint naming conventions. Add your own joint # names to the "hanimName()" function at the bottom. # # Should be able to find this at: # http://www.accad.ohio-state.edu/~mlewis/VRML/Mocap # # Matthew Lewis, 10/9/2001 # mlewis@cgrg.ohio-state.edu # http://www.accad.ohio-state.edu/~mlewis # ######################################################## # usage if ($#ARGV != 0) { print "usage: bvh2hanim in.bvh > out.wrl\n"; exit; } $bvhfn = $ARGV[0]; # bvh file name open(BVH,"<$bvhfn"); # open bvh file # make list of joint and segment names as read them @jnts; @segs; # parse special first line "HIERARCHY" $line = ; # read ROOT line, get name, and pass to parse joint function $line = ; $line =~ /\s*ROOT\s+(\S+)/; $jname = $1; $jname = hanimName($jname); # print "1st jname = $jname\n"; printHeaders(); # HANIM protos print "Viewpoint { position 0 100 500 }\n"; print "NavigationInfo { type \"EXAMINE\" }\n"; # create Humanoid node print "DEF Humanoid Humanoid {\n"; print " name \"Eric\"\n"; print " humanoidBody [\n"; parseJoint("ROOT", $jname, 0,0,0, " "); print " ]\n"; # list joints # BUG!!! Commented out the list because both NS and IE are hanging # when I include the joint names here... what's up with that?!? print " joints [\n"; print "# "; foreach $jnt (@jnts) { print "USE hanim_$jnt "; } print "\n ]\n"; #list segments print " segments [\n"; print " "; foreach $seg (@segs) { print "USE hanim_$seg "; } print "\n ]\n"; print "}\n"; parseMotion(); printHUD(); # to start/stop/scroll motion, comment out to kill it printRoutes(); print "# all done!\n"; close(BVH); ######################################## # read and output one joint sub parseJoint { local $line; local $jtype = $_[0]; # joint type (ROOT or JOINT) local $jname = $_[1]; # joint name local $pjx = $_[2]; # parent joint global x position local $pjy = $_[3]; # parent joint global y position local $pjz = $_[4]; # parent joint global z position local $psp = $_[5]; # parent indent space local $jx = $pjx; # joint global x position local $jy = $pjy; # joint global y position local $jz = $pjz; # joint global z position local $i; # gen purpose index local $nc; # num coords (child joints for seg making) local $sname; # segment name (given a parent name) local $kidCoords = ""; # global coords of each child joint local $retCoords = ""; # return global coords to parent joint local $kidIndices = ""; # indices of line seg to child joints push(@jnts, $jname); $line = ; # parse open curly bracket # print "open curly? : $line"; # parse joint OFFSET $line = ; $line =~ /\s*OFFSET\s+(\S+)\s+(\S+)\s+(\S+)/; $ojx = $1; $ojy = $2; $ojz = $3; # print "Joint offset: ($ojx, $ojy, $ojz)\n"; $jx += $ojx; $jy += $ojy; $jz += $ojz; # print "Joint gpos: ($jx, $jy, $jz)\n"; $retCoords = "$jx $jy $jz "; # find and store a segment name - child of this joint $sname = segName($jname); push(@segs, $sname); print $psp."DEF hanim_".$jname." Joint {\n"; # start a new joint print $psp." name \"hanim_".$jname."\"\n"; print $psp." center $jx $jy $jz\n"; print $psp." children [\n"; # READ CHANNELS LINE # assume for now that for ROOT, it always looks like this: # CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation # and for JOINT it always looks like this: # CHANNELS 3 Zrotation Xrotation Yrotation # note the wacky joint ordering! $line = ; # NEXT is either a child joint, or an end site $line = ; # "JOINT" or "End Site" # print "should be jnt or end site: $line"; if($line =~ /\s*JOINT\s+(\S+)/) { $jname = $1; $jname = hanimName($jname); # print "Joint name: $jname\n"; $kidCoords .= parseJoint("JOINT", $jname, $jx, $jy, $jz, $psp." "); } elsif($line =~ /\s*End Site/) { $line = ; # parse open curly bracket # parse End Site OFFSET $line = ; $line =~ /\s*OFFSET\s+(\S+)\s+(\S+)\s+(\S+)/; $ojx = $1; $ojy = $2; $ojz = $3; # print "End offset: ($ojx, $ojy, $ojz)\n"; # $ejx = $jx + $ojx; $ejy = $jy + $ojy; $ejz = $jz + $ojz; # print "End gpos: ($ejx, $ejy, $ejz)\n"; $line = ; # parse close curly bracket } # end of joints? or another child joint? while(!(($line = ) =~ /\s*\}/)) { # while not close curly... $line =~ /\s*JOINT\s+(\S+)/; $jname = $1; $jname = hanimName($jname); # print "Joint name: $jname\n"; $kidCoords .= parseJoint("JOINT", $jname, $pjx, $pjy, $pjz, $psp." "); } # how many line segs should we draw? $nc = int(split(/ /,$kidCoords))/3; for($i=1; $i<=$nc; $i++) { $kidIndices .= "0 $i "; } # create segment for this joint # currently using one line seg per child joint print $psp." DEF hanim_$sname Segment {\n"; print $psp." name \"hanim_$sname\"\n"; print $psp." children [\n"; print $psp." Transform {\n"; print $psp." translation $jx $jy $jz\n"; print $psp." scale 3 3 3\n"; print $psp." children Shape {\n"; print $psp." appearance Appearance {\n"; print $psp." material Material { diffuseColor 1 0 0 }\n"; print $psp." }\n"; print $psp." geometry Box {}\n"; print $psp." }\n"; print $psp." }\n"; if($kidCoords ne "") { print $psp." Shape {\n"; print $psp." appearance Appearance {\n"; print $psp." material Material { emissiveColor 0 0 1 }\n"; print $psp." }\n"; print $psp." geometry IndexedLineSet {\n"; print $psp." coord Coordinate {\n"; print $psp." point [ $jx $jy $jz $kidCoords ]\n"; #print $psp." point [ $pjx $pjy $pjz $jx $jy $jz ]\n"; print $psp." }\n"; print $psp." coordIndex [ $kidIndices ]\n"; print $psp." }\n"; print $psp." }\n"; } print $psp." ]\n"; # close Segment's children print $psp." }\n"; # close Segment print $psp." ]\n"; # close Joint's children print $psp."}\n"; # close Joint return $retCoords; } ######################################## # read all the motion frames sub parseMotion { local @pos; # positions $line = ; # read line sez "MOTION" #print "should say MOTION: $line"; # debug # read frame info $line = ; # line sez "Frames: 7200" $line =~ /Frames:\s+(\d+)/; $nframes = $1; #print "nframes = $nframes\n"; $line = ; # line sez "Frame Time: 0.008333" $line =~ /Frame Time:\s+(\S+)/; # duration of one frame $spf = $1; # seconds per frame $fps = round(1/$spf); # frames per second #print "frameDur = $frameDur\n"; $dur = $nframes * $spf; # motion duration #print "dur = $dur\n"; $dfps = 15; # desired frames per second $dnframes = $dur * $dfps; # desired number of frames $keeper = $fps/$dfps; # e.g. keep every 4th frame, etc... print "# dfps=$dfps dur=$dur dnframes=$dnframes\n"; $cnt = 0; # count frame numbers read $fn = 0; # frame number of frames actually used # read all of the frames while ($line = ) { if(($cnt % $keeper)==0) { # 15 fps for a 120fps capture chop $line; # hack off newline at the end $line =~ s/\s+/ /g; # replace all multiple spaces with single @p = split / /, $line; # put value into an array # shift root position XYZ off push(@pos, shift(@p)); # X push(@pos, shift(@p)); # Y push(@pos, shift(@p)); # Z # get joint rotations (NOTE ZXY order!!!) for $jnt (@jnts) { $rz = shift(@p); $rx = shift(@p); $ry = shift(@p); $orientation = zxy2aa($rz,$rx,$ry); # convert ZXY to AA $jframes{$jnt} .= "$orientation "; # put in associative array } ++$fn; # count frames actually used } ++$cnt; # number of frames read } print "# cnt=$cnt fn=$fn\n"; # time for timer! print "DEF TIMER TimeSensor { loop TRUE cycleInterval $dur }\n"; # position interpolator print "DEF WHERE PositionInterpolator {\n"; print " key [\n"; for($i=0; $i<$dnframes; $i+=1) { $key = $i / ($dnframes-1); $key = int($key*10000)/10000; print "$key "; } print "\n ]\n"; print " keyValue [\n"; foreach $pc (@pos) { print "$pc "; } print "\n ]\n"; print "}\n"; # joint orientation interpolators foreach $jnt (@jnts) { print "DEF ".$jnt."_OI OrientationInterpolator {\n"; print " key [\n"; for($i=0; $i<$dnframes; $i+=1) { $key = $i / ($dnframes-1); $key = int($key*10000)/10000; print "$key "; } print "\n ]\n"; $okeys = $jframes{$jnt}; print " keyValue [\n"; print " $okeys\n"; print " ]\n"; print "}\n"; } } ######################################## # timer to sub printRoutes() { print "ROUTE TIMER.fraction_changed TO WHERE.set_fraction\n"; print "ROUTE WHERE.value_changed TO hanim_HumanoidRoot.translation\n"; foreach $jnt (@jnts) { print "ROUTE TIMER.fraction_changed TO ".$jnt."_OI.set_fraction\n"; print "ROUTE ".$jnt."_OI.value_changed TO hanim_".$jnt.".rotation\n"; } } ######################################## sub printHeaders() { print < 0.00001) { $ilen = 1/$len; $qyxz[0] *= $ilen; $qyxz[1] *= $ilen; $qyxz[2] *= $ilen; $qyxz[3] = 2 * acos($qyxz[3]); $aa = "$qyxz[0] $qyxz[1] $qyxz[2] $qyxz[3]"; } return $aa; } ######################################## sub radians { local $d = $_[0]; return $d * 0.017453293; } ######################################## sub degrees { local $r = $_[0]; return $r * 57.295779513; } ######################################## sub acos { atan2( sqrt(1 - $_[0] * $_[0]), $_[0] ) } ######################################## sub segName { local $pname = $_[0]; # parent joint name local $sname = $pname; # segment name if($pname eq "HumanoidRoot") { $sname = "sacrum"; } elsif($pname eq "vl5") { $sname = "l5"; } elsif($pname eq "vl1") { $sname = "l1"; } elsif($pname eq "vt6") { $sname = "t6"; } elsif($pname eq "vt1") { $sname = "t1"; } elsif($pname eq "skullbase") { $sname = "skull"; } elsif($pname eq "l_sternoclavicular") { $sname = "l_clavicle"; } elsif($pname eq "l_shoulder") { $sname = "l_upperarm"; } elsif($pname eq "l_elbow") { $sname = "l_forearm"; } elsif($pname eq "l_wrist") { $sname = "l_hand"; } elsif($pname eq "r_sternoclavicular") { $sname = "r_clavicle"; } elsif($pname eq "r_shoulder") { $sname = "r_upperarm"; } elsif($pname eq "r_elbow") { $sname = "r_forearm"; } elsif($pname eq "r_wrist") { $sname = "r_hand"; } elsif($pname eq "l_hip") { $sname = "l_thigh"; } elsif($pname eq "l_knee") { $sname = "l_calf"; } elsif($pname eq "l_ankle") { $sname = "l_hindfoot"; } elsif($pname eq "l_midtarsal") { $sname = "l_middistal"; } elsif($pname eq "r_hip") { $sname = "r_thigh"; } elsif($pname eq "r_knee") { $sname = "r_calf"; } elsif($pname eq "r_ankle") { $sname = "r_hindfoot"; } elsif($pname eq "r_midtarsal") { $sname = "r_middistal"; } return $sname; } ######################################## sub hanimName { local $bname = $_[0]; # bvh name (local to accad?) local $sname = $bname; # equiv hanim name if($bname eq "root") { $hname = "HumanoidRoot"; } elsif($bname eq "lowerback") { $hname = "vl5"; } elsif($bname eq "upperback") { $hname = "vl1"; } elsif($bname eq "thorax") { $hname = "vt6"; } elsif($bname eq "neck") { $hname = "vt1"; } elsif($bname eq "head") { $hname = "skullbase"; } elsif($bname eq "lshoulderjoint") { $hname = "l_sternoclavicular"; } elsif($bname eq "lhumerus") { $hname = "l_shoulder"; } elsif($bname eq "lradius") { $hname = "l_elbow"; } elsif($bname eq "lhand") { $hname = "l_wrist"; } elsif($bname eq "rshoulderjoint") { $hname = "r_sternoclavicular"; } elsif($bname eq "rhumerus") { $hname = "r_shoulder"; } elsif($bname eq "rradius") { $hname = "r_elbow"; } elsif($bname eq "rhand") { $hname = "r_wrist"; } elsif($bname eq "lfemur") { $hname = "l_hip"; } elsif($bname eq "ltibia") { $hname = "l_knee"; } elsif($bname eq "lfoot") { $hname = "l_ankle"; } elsif($bname eq "ltoes") { $hname = "l_midtarsal"; } elsif($bname eq "rfemur") { $hname = "r_hip"; } elsif($bname eq "rtibia") { $hname = "r_knee"; } elsif($bname eq "rfoot") { $hname = "r_ankle"; } elsif($bname eq "rtoes") { $hname = "r_midtarsal"; } return $hname; } ######################################## ########################################