Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2005-12-16 02:18:48 +0300
committerCampbell Barton <ideasman42@gmail.com>2005-12-16 02:18:48 +0300
commitf5121b2496ad8ace0f9262ae9c47b52caeb700e8 (patch)
tree9a9211ac76ed85902120080086481f6b99a643a0 /release
parent826591d778dc30fa727473757b11eb8b31cec5ab (diff)
BVH Export updated for 2.4x, also small updates in many areas.
Diffstat (limited to 'release')
-rw-r--r--release/scripts/bvh_export.py575
1 files changed, 277 insertions, 298 deletions
diff --git a/release/scripts/bvh_export.py b/release/scripts/bvh_export.py
index 84425626cee..7c6ac4c7e36 100644
--- a/release/scripts/bvh_export.py
+++ b/release/scripts/bvh_export.py
@@ -9,7 +9,7 @@ Tip: 'Export a (.bvh) motion capture file'
__author__ = "Campbell Barton"
__url__ = ("blender", "elysiun")
-__version__ = "1.0 03/30/04"
+__version__ = "1.1 12/16/05"
__bpydoc__ = """\
This script exports animation data to BVH motion capture file format.
@@ -30,12 +30,11 @@ Notes:<br>
# BVH Export script 1.0 by Campbell Barton #
# Copyright MetaVR 30/03/2004, #
# if you have any questions about this script #
-# email me ideasman@linuxmail.org #
-# #
+# email me cbarton@metavr.com #
#===============================================#
# --------------------------------------------------------------------------
-# BVH Export v0.9 by Campbell Barton (AKA Ideasman)
+# BVH Export v1.1 by Campbell Barton (AKA Ideasman)
# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
@@ -46,12 +45,12 @@ Notes:<br>
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
-# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------
@@ -59,348 +58,328 @@ Notes:<br>
import Blender
from Blender import Scene, Object
import math
+time = Blender.sys.time
from math import *
# Get the current scene.
scn = Scene.GetCurrent()
context = scn.getRenderingContext()
-frameRate = 0.3333 # 0.04 = 25fps
-scale = 1
+frameRate = 1.0/context.framesPerSec() # 0.04 = 25fps
+scale = 1.0
-indent = ' ' # 2 space indent per object
+indent = '\t' # 2 space indent per object
prefixDelimiter = '_'
# Vars used in eular rotation funtcion
RAD_TO_DEG = 180.0/3.14159265359
-
+DEG_TO_RAD = math.pi/180.0
#====================================================#
# Search for children of this object and return them #
#====================================================#
def getChildren(parent):
- children = [] # We'll assume none.
- for child in Object.Get():
- if child.getParent() == Object.Get(parent):
- children.append( child.getName() )
- return children
+ children = [] # We'll assume none.
+ for child in Object.Get():
+ if child.parent == parent:
+ children.append( child )
+ return children
#====================================================#
-# MESSY BUT WORKS: Make a string that shows the #
-# hierarchy as a list and then eval it #
+# MESSY BUT WORKS: Make a string that shows the #
+# hierarchy as a list and then eval it #
#====================================================#
def getHierarchy(root, hierarchy):
- hierarchy = hierarchy + '["' + root + '",'
- for child in getChildren(root):
- hierarchy = getHierarchy(child, hierarchy)
- hierarchy += '],'
- return hierarchy
+ hierarchy = '%s["%s",' % (hierarchy, root.name)
+ for child in getChildren(root):
+ hierarchy = getHierarchy(child, hierarchy)
+ hierarchy = '%s],' % hierarchy
+ return hierarchy
#====================================================#
-# Strips the prefix off the name before writing #
+# Strips the prefix off the name before writing #
#====================================================#
def stripName(name): # name is a string
-
- # WARNING!!! Special case for a custom RIG for output
- # for MetaVR's HPX compatable RIG.
- print 'stripname', name[0:10]
- if name[0:10] == 'Transform(':
- name = name[10:]
- while name[-1] != ')':
- name = name[0:-1]
- print name
- name = name[:-1]
-
-
- return name[1+name.find(prefixDelimiter): ]
-
-
-#====================================================#
-# Return a 6 deciaml point floating point value #
-# as a string that dosent have any python chars #
-#====================================================#
-def saneFloat(float):
- #return '%(float)b' % vars() # 6 fp as house.hqx
- return str('%f' % float) + ' '
-
-
+
+ # WARNING!!! Special case for a custom RIG for output
+ # for MetaVR's HPX compatable RIG.
+ # print 'stripname', name[0:10]
+ if name.lower().startswith('transform('):
+ name = name[10:].split(prefixDelimiter)[0]
+ return name.split('_')[0]
+
#====================================================#
# Recieves an object name, gets all the data for that#
# node from blender and returns it for formatting #
# and writing to a file. #
#====================================================#
-def getNodeData(nodeName):
- Object.Get(nodeName)
- # Get real location
- offset = Object.Get(nodeName).getLocation()
- offset = (offset[0]*scale, offset[1]*scale, offset[2]*scale,)
-
- #=========================#
- # Test for X/Y/Z IPO's #
- #=========================#
- obipo = Object.Get(nodeName).getIpo()
-
- # IF we dont have an IPO then dont check the curves.
- # This was added to catch end nodes that never have an IPO, only an offset.
- if obipo == None:
- xloc=yloc=zloc=xrot=yrot=zrot = 0
-
- else: # Do have an IPO, checkout which curves are in use.
- # Assume the rot's/loc's exist until proven they dont
- xloc=yloc=zloc=xrot=yrot=zrot = 1
- if obipo.getCurve('LocX') == None:
- xloc = 0
- if obipo.getCurve('LocY') == None:
- yloc = 0
- if obipo.getCurve('LocZ') == None:
- zloc = 0
-
- # Now for the rotations, Because of the conversion of rotation coords
- # if there is one rotation er need to store all 3
- if obipo.getCurve('RotX') == None and \
- obipo.getCurve('RotY') == None and \
- obipo.getCurve('RotZ') == None:
- xrot=yrot=zrot = 0
-
- # DUMMY channels xloc, yloc, zloc, xrot, yrot, zrot
- # [<bool>, <bool>, <bool>, <bool>, <bool>, <bool>]
- channels = [xloc, yloc, zloc, xrot, yrot, zrot]
-
- return offset, channels
+def getNodeData(nodeOb):
+ ob = nodeOb
+ obipo = ob.getIpo()
+ # Get real location
+ offset = [o*scale for o in ob.getLocation()]
+
+ #=========================#
+ # Test for X/Y/Z IPO's #
+ #=========================#
+
+ # IF we dont have an IPO then dont check the curves.
+ # This was added to catch end nodes that never have an IPO, only an offset.
+
+ # DUMMY channels xloc, yloc, zloc, xrot, yrot, zrot
+ # [<bool>, <bool>, <bool>, <bool>, <bool>, <bool>]
+ channels = [0,0,0,0,0,0] # xloc,yloc,zloc,xrot,yrot,zrot
+ if obipo != None: # Do have an IPO, checkout which curves are in use.
+ # Assume the rot's/loc's dont exist until they proven they do.
+ if obipo.getCurve('LocX') != None:
+ channels[0] = 1
+ if obipo.getCurve('LocY') != None:
+ channels[1] = 1
+ if obipo.getCurve('LocZ') != None:
+ channels[2] = 1
+
+ # Now for the rotations, Because of the conversion of rotation coords
+ # if there is one rotation er need to store all 3
+ if obipo.getCurve('RotX') != None or \
+ obipo.getCurve('RotY') != None or \
+ obipo.getCurve('RotZ') != None:
+ channels[3] = channels[4] = channels[5] = 1
+ #print ob, channels
+ return offset, channels
#====================================================#
-# Return the BVH hierarchy to a file from a list #
+# Writes the BVH hierarchy to a file #
# hierarchy: is a list of the empty hierarcht #
-# bvhHierarchy: a string, in the bvh format to write #
# level: how many levels we are down the tree, #
# ...used for indenting #
# Also gathers channelList , so we know the order to #
-# write the motiondata in #
+# write the motiondata in #
#====================================================#
-def hierarchy2bvh(hierarchy, bvhHierarchy, level, channelList, nodeObjectList):
- nodeName = hierarchy[0]
-
- # Add object to nodeObjectList
- nodeObjectList.append(Object.Get(nodeName))
-
- #============#
- # JOINT NAME #
- #============#
- bvhHierarchy += level * indent
- if level == 0:
- # Add object to nodeObjectList
- nodeObjectList.append(Object.Get(nodeName))
- bvhHierarchy+= 'ROOT '
- bvhHierarchy += stripName(nodeName) + '\n'
- # If this is the last object in the list then we
- # dont bother withwriting its real name, use "End Site" instead
- elif len(hierarchy) == 1:
- bvhHierarchy+= 'End Site\n'
- # Ok This is a normal joint
- else:
- # Add object to nodeObjectList
- nodeObjectList.append(Object.Get(nodeName))
- bvhHierarchy+= 'JOINT '
- bvhHierarchy += stripName(nodeName) + '\n'
- #================#
- # END JOINT NAME #
- #================#
-
- # Indent again, this line is just for the brackets
- bvhHierarchy += level * indent + '{' + '\n'
-
- # Indent
- level += 1
-
- #================================================#
- # Data for writing to a file offset and channels #
- #================================================#
- offset, channels = getNodeData(nodeName)
-
- #============#
- # Offset #
- #============#
- bvhHierarchy += level * indent + 'OFFSET ' + saneFloat(scale * offset[0]) + ' ' + saneFloat(scale * offset[1]) + ' ' + saneFloat(scale * offset[2]) + '\n'
-
- #============#
- # Channels #
- #============#
- if len(hierarchy) != 1:
- # Channels, remember who is where so when we write motiondata
- bvhHierarchy += level * indent + 'CHANNELS '
- # Count the channels
- chCount = 0
- for chn in channels:
- chCount += chn
- bvhHierarchy += str(chCount) + ' '
- if channels[0]:
- bvhHierarchy += 'Xposition '
- channelList.append([len(nodeObjectList)-1, 0])
- if channels[1]:
- bvhHierarchy += 'Yposition '
- channelList.append([len(nodeObjectList)-1, 1])
- if channels[2]:
- bvhHierarchy += 'Zposition '
- channelList.append([len(nodeObjectList)-1, 2])
- if channels[5]:
- bvhHierarchy += 'Zrotation '
- channelList.append([len(nodeObjectList)-1, 5])
- if channels[3]:
- bvhHierarchy += 'Xrotation '
- channelList.append([len(nodeObjectList)-1, 3])
- if channels[4]:
- bvhHierarchy += 'Yrotation '
- channelList.append([len(nodeObjectList)-1, 4])
-
- bvhHierarchy += '\n'
-
- # Loop through children if any and run this function (recursively)
- for hierarchyIdx in range(len(hierarchy)-1):
- bvhHierarchy, level, channelList, nodeObjectList = hierarchy2bvh(hierarchy[hierarchyIdx+1], bvhHierarchy, level, channelList, nodeObjectList)
- # Unindent
- level -= 1
- bvhHierarchy += level * indent + '}' + '\n'
-
- return bvhHierarchy, level, channelList, nodeObjectList
+def hierarchy2bvh(file, hierarchy, level, channelList, nodeObjectList):
+ nodeName = hierarchy[0]
+ ob = Object.Get(nodeName)
+ '''
+ obipo = ob.getIpo()
+ if obipo != None:
+ obcurves = obipo.getCurves()
+ else:
+ obcurves = None
+ '''
+ #============#
+ # JOINT NAME #
+ #============#
+ file.write(level * indent)
+ if level == 0:
+ # Add object to nodeObjectList
+ #nodeObjectList.append( (ob, obipo, obcurves) )
+ nodeObjectList.append( ob )
+ file.write( 'ROOT %s\n' % stripName(nodeName) )
+ # If this is the last object in the list then we
+ # dont bother withwriting its real name, use "End Site" instead
+ elif len(hierarchy) == 1:
+ file.write( 'End Site\n' )
+ # Ok This is a normal joint
+ else:
+ # Add object to nodeObjectList
+ #nodeObjectList.append((ob, obipo, obcurves))
+ nodeObjectList.append( ob )
+ file.write( 'JOINT %s\n' % stripName(nodeName) )
+ #================#
+ # END JOINT NAME #
+ #================#
+
+ # Indent again, this line is just for the brackets
+ file.write( '%s{\n' % (level * indent) )
+
+ # Indent
+ level += 1
+
+ #================================================#
+ # Data for writing to a file offset and channels #
+ #================================================#
+ offset, channels = getNodeData(ob)
+
+ #============#
+ # Offset #
+ #============#
+ file.write( '%sOFFSET %.6f %.6f %.6f\n' %\
+ (level*indent, scale*offset[0], scale*offset[1], scale*offset[2]) )
+
+ #============#
+ # Channels #
+ #============#
+ if len(hierarchy) != 1:
+ # Channels, remember who is where so when we write motiondata
+ file.write('%sCHANNELS %i ' % (level*indent, len([c for c in channels if c ==1]) ))
+ if channels[0]:
+ file.write('Xposition ')
+ channelList.append([len(nodeObjectList)-1, 0])
+ if channels[1]:
+ file.write('Yposition ')
+ channelList.append([len(nodeObjectList)-1, 1])
+ if channels[2]:
+ file.write('Zposition ')
+ channelList.append([len(nodeObjectList)-1, 2])
+ if channels[5]:
+ file.write('Zrotation ')
+ channelList.append([len(nodeObjectList)-1, 5])
+ if channels[3]:
+ file.write('Xrotation ')
+ channelList.append([len(nodeObjectList)-1, 3])
+ if channels[4]:
+ file.write('Yrotation ')
+ channelList.append([len(nodeObjectList)-1, 4])
+ file.write('\n')
+
+ # Loop through children if any and run this function (recursively)
+ for hierarchyIdx in range(len(hierarchy)-1):
+ level = hierarchy2bvh(file, hierarchy[hierarchyIdx+1], level, channelList, nodeObjectList)
+ # Unindent
+ level -= 1
+ file.write('%s}\n' % (level * indent))
+
+ return level
# added by Ben Batt 30/3/2004 to make the exported rotations correct
def ZYXToZXY(x, y, z):
- '''
- Converts a set of Euler rotations (x, y, z) (which are intended to be
- applied in z, y, x order) into a set which are intended to be applied in
- z, x, y order (the order expected by .bvh files)
- '''
- A,B = cos(x),sin(x)
- C,D = cos(y),sin(y)
- E,F = cos(z),sin(z)
-
- x = asin(-B*C)
- y = atan2(D, A*C)
- z = atan2(-B*D*E + A*F, B*D*F + A*E)
-
- # this seems to be necessary - not sure why (right/left-handed coordinates?)
- x = -x
- return x*RAD_TO_DEG, y*RAD_TO_DEG, z*RAD_TO_DEG
-
-
-
-def getIpoLocation(object, frame):
- x = y = z = 0
- obipo = object.getIpo()
- for i in range(object.getIpo().getNcurves()):
- if obipo.getCurves()[i].getName() =='LocX':
- x = object.getIpo().EvaluateCurveOn(i,frame)
- elif obipo.getCurves()[i].getName() =='LocY':
- y = object.getIpo().EvaluateCurveOn(i,frame)
- elif obipo.getCurves()[i].getName() =='LocZ':
- z = object.getIpo().EvaluateCurveOn(i,frame)
- return x, y, z
-
+ '''
+ Converts a set of Euler rotations (x, y, z) (which are intended to be
+ applied in z, y, x order, into a set which are intended to be applied in
+ z, x, y order (the order expected by .bvh files)
+ '''
+ A,B = cos(x),sin(x)
+ C,D = cos(y),sin(y)
+ E,F = cos(z),sin(z)
+
+ x = asin(-B*C)
+ y = atan2(D, A*C)
+ z = atan2(-B*D*E + A*F, B*D*F + A*E)
+
+ # this seems to be necessary - not sure why (right/left-handed coordinates?)
+ # x = -x # x is negative, see below.
+ return -x*RAD_TO_DEG, y*RAD_TO_DEG, z*RAD_TO_DEG
+
+
+''' # UNUSED, JUST GET OBJECT LOC/ROT
+def getIpoLocation(object, obipo, curves, frame):
+ x = y = z = rx = ry = rz =0
+ if obipo:
+ for i in range(obipo.getNcurves()):
+ if curves[i].getName() =='LocX':
+ x = obipo.EvaluateCurveOn(i,frame)
+ elif curves[i].getName() =='LocY':
+ y = obipo.EvaluateCurveOn(i,frame)
+ elif curves[i].getName() =='LocZ':
+ z = obipo.EvaluateCurveOn(i,frame)
+ elif curves[i].getName() =='RotX':
+ rx = obipo.EvaluateCurveOn(i,frame)
+ elif curves[i].getName() =='RotY':
+ ry = obipo.EvaluateCurveOn(i,frame)
+ elif curves[i].getName() =='RotZ':
+ rz = obipo.EvaluateCurveOn(i,frame)
+ return x, y, z, rx*10*DEG_TO_RAD, ry*10*DEG_TO_RAD, rz*10*DEG_TO_RAD
+'''
#====================================================#
# Return the BVH motion for the spesified frame #
# hierarchy: is a list of the empty hierarcht #
-# bvhHierarchy: a string, in the bvh format to write #
# level: how many levels we are down the tree, #
# ...used for indenting #
#====================================================#
-def motion2bvh(frame, chennelList, nodeObjectList):
-
- motionData = '' # We'll append the frames to the string.
-
- for chIdx in chennelList:
- ob = nodeObjectList[chIdx[0]]
- chType = chIdx[1]
-
- # Get object rotation
- x, y, z = ob.getEuler()
-
- # Convert the rotation from ZYX order to ZXY order
- x, y, z = ZYXToZXY(x, y, z)
-
-
- # Using regular Locations stuffs upIPO locations stuffs up
- # Get IPO locations instead
- xloc, yloc, zloc = getIpoLocation(ob, frame)
-
- # WARNING non standard Location
- xloc, zloc, yloc = -xloc, yloc, zloc
-
-
- if chType == 0:
- motionData += saneFloat(scale * (xloc))
- if chType == 1:
- motionData += saneFloat(scale * (yloc))
- if chType == 2:
- motionData += saneFloat(scale * (zloc))
- if chType == 3:
- motionData += saneFloat(x)
- if chType == 4:
- motionData += saneFloat(y)
- if chType == 5:
- motionData += saneFloat(z)
-
- motionData += ' '
-
- motionData += '\n'
- return motionData
+def motion2bvh(file, frame, chennelList, nodeObjectList):
+ for chIdx in chennelList:
+ #ob, obipo, obcurves = nodeObjectList[chIdx[0]]
+ ob = nodeObjectList[chIdx[0]]
+ chType = chIdx[1]
+
+ # Get object rotation
+ x, y, z = ob.getEuler()
+
+ # Convert the rotation from ZYX order to ZXY order
+ x, y, z = ZYXToZXY(x, y, z)
+
+ # Location
+ xloc, yloc, zloc = ob.matrixLocal[3][:3]
+
+ # Using regular Locations stuffs upIPO locations stuffs up
+ # Get IPO locations instead
+ #xloc, yloc, zloc, x, y, z = getIpoLocation(ob, obipo, obcurves, frame)
+ # Convert the rotation from ZYX order to ZXY order
+ #x, y, z = ZYXToZXY(x, y, z)
+
+
+ # WARNING non standard Location
+ # xloc, zloc, yloc = -xloc, yloc, zloc
+
+ if chType == 0:
+ file.write('%.6f ' % (scale * xloc))
+ if chType == 1:
+ file.write('%.6f ' % (scale * yloc))
+ if chType == 2:
+ file.write('%.6f ' % (scale * zloc))
+ if chType == 3:
+ file.write('%.6f ' % x)
+ if chType == 4:
+ file.write('%.6f ' % y)
+ if chType == 5:
+ file.write('%.6f ' % z)
+ file.write('\n')
+
def saveBVH(filename):
-
- if filename.find('.bvh', -4) <= 0: filename += '.bvh' # for safety
-
- # Here we store a serialized list of blender objects as they appier
- # in the hierarchy, this is refred to when writing motiondata
- nodeObjectList = []
-
- # In this list we store a 2 values for each node
- # 1) An index pointing to a blender object
- # in objectList
- # 2) The type if channel x/y/z rot:x/y/z - Use 0-5 to indicate this
- chennelList = []
-
- print ''
- print 'BVH 1.0 by Campbell Barton (Ideasman) - ideasman@linuxmail.org'
-
- # Get the active object and recursively traverse its kids to build
- # the BVH hierarchy, then eval the string to make a hierarchy list.
- hierarchy = eval(getHierarchy(Object.GetSelected()[0].getName(),''))[0] # somhow this returns a tuple with one list in it.
-
- # Put all data in the file we have selected file.
- file = open(filename, "w")
- file.write('HIERARCHY\n') # all bvh files have this on the first line
-
- # Write the whole hirarchy to a list
- bvhHierarchy, level, chennelList, nodeObjectList = hierarchy2bvh(hierarchy, '', 0, chennelList, nodeObjectList)
- file.write( bvhHierarchy ) # Rwite the var fileBlock to the output.
- bvhHierarchy = None # Save a tit bit of memory
-
- #====================================================#
- # MOTION: Loop through the frames ande write out #
- # the motion data for each #
- #====================================================#
- # Do some basic motion file header stuff
- file.write('MOTION\n')
- file.write( 'Frames: ' + str(1 + context.endFrame() - context.startFrame()) + '\n' )
- file.write( 'Frame Time: ' + saneFloat(frameRate) + '\n' )
-
- #print 'WARNING- exact frames might be stuffed up- inclusive whatever, do some tests later on.'
- frames = range(context.startFrame(), context.endFrame()+1)
- print 'exporting ' + str(len(frames)) + ' of motion...'
-
- for frame in frames:
- context.currentFrame(frame)
- scn.update(1) # Update locations so we can write the new locations
- #Blender.Window.RedrawAll() # causes crash
-
- file.write( motion2bvh(frame, chennelList, nodeObjectList) )
-
- file.write('\n') # newline
- file.close()
- print 'done'
-
+ t = time()
+ if not filename.lower().endswith('.bvh'):
+ filename += '.bvh' # for safety
+
+ # Here we store a serialized list of blender objects as they appier
+ # in the hierarchy, this is refred to when writing motiondata
+ nodeObjectList = []
+
+ # In this list we store a 2 values for each node
+ # 1) An index pointing to a blender object
+ # in objectList
+ # 2) The type if channel x/y/z rot:x/y/z - Use 0-5 to indicate this
+ chennelList = []
+
+ print '\nBVH 1.1 by Campbell Barton (Ideasman) - cbarton@metavr.com'
+
+ # Get the active object and recursively traverse its kids to build
+ # the BVH hierarchy, then eval the string to make a hierarchy list.
+ hierarchy = eval(getHierarchy(scn.getActiveObject(),''))[0] # somhow this returns a tuple with one list in it.
+
+ # Put all data in the file we have selected file.
+ file = open(filename, "w")
+ file.write('HIERARCHY\n') # all bvh files have this on the first line
+
+ # Write the whole hirarchy to a list
+ level = 0 # Indenting level, start with no indent
+ level = hierarchy2bvh(file, hierarchy, level, chennelList, nodeObjectList)
+
+ #====================================================#
+ # MOTION: Loop through the frames ande write out #
+ # the motion data for each #
+ #====================================================#
+ # Do some basic motion file header stuff
+ file.write( 'MOTION\n' )
+ file.write( 'Frames: %i\n' % ( 1 + context.endFrame() - context.startFrame() ) )
+ file.write( 'Frame Time: %.6f\n' % frameRate )
+
+ frames = range(context.startFrame()+1, context.endFrame()+1)
+ print 'exporting %i of motion...' % len(frames)
+
+ for frame in frames:
+ context.currentFrame(frame)
+ scn.update(1) # Update locations so we can write the new locations. This is the SLOW part.
+ # Blender.Window.RedrawAll() # Debugging.
+
+ motion2bvh(file, frame, chennelList, nodeObjectList) # Write the motion to a file.
+
+ file.write('\n') # newline
+ file.close()
+ print '...Done in %.4f seconds.' % (time()-t)
+
Blender.Window.FileSelector(saveBVH, 'Export BVH')