diff options
author | Campbell Barton <ideasman42@gmail.com> | 2005-12-06 06:53:35 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2005-12-06 06:53:35 +0300 |
commit | 9497b6dfb69d449a4fe4dbf2c59cb50baa2aaa7b (patch) | |
tree | 87caa6073b4541477df29e46570f6a1293b86e6c | |
parent | 2d19c4f208f3910d834412a30a2c3cdaae44efcd (diff) |
Updated bvh importer. tested on over 100 bvh files.
* Modified to use Mathutils for matrix math,
* Fixed possible joint naming bug,
* Imports BVH's with bad EOF gracefully
* Fixed duplicate joint names, make them unique
* Use \r as well as \n for newlines
* Added suppot for nodes with 0 motion channels
* Rotation IPOs never cross more then 180d
fixes sub frame tweening and time scaling
* 5x overall speedup.
-rw-r--r-- | release/scripts/bvh_import.py | 877 |
1 files changed, 425 insertions, 452 deletions
diff --git a/release/scripts/bvh_import.py b/release/scripts/bvh_import.py index 17ce01c0cab..05f40f018dc 100644 --- a/release/scripts/bvh_import.py +++ b/release/scripts/bvh_import.py @@ -2,14 +2,14 @@ """ Name: 'Motion Capture (.bvh)...' -Blender: 236 +Blender: 239 Group: 'Import' Tip: 'Import a (.bvh) motion capture file' """ __author__ = "Campbell Barton" -__url__ = ("blender", "elysiun", "http://jmsoler.free.fr/util/blenderfile/py/bvh_import.py") -__version__ = "1.0.2 04/12/28" +__url__ = ("blender", "elysiun") +__version__ = "1.0.4 05/12/04" __bpydoc__ = """\ This script imports BVH motion capture data to Blender. @@ -21,8 +21,8 @@ Missing:<br> Known issues:<br> Notes:<br> - Jean-Michel Soler improved importer to support Poser 3.01 files;<br> - Jean-Baptiste Perin wrote a script to create an armature out of the + Jean-Michel Soler improved importer to support Poser 3.01 files;<br> + Jean-Baptiste Perin wrote a script to create an armature out of the Empties created by this importer, it's in the Scripts window -> Scripts -> Animation menu. """ @@ -30,23 +30,43 @@ Empties created by this importer, it's in the Scripts window -> Scripts -> Anima # #===============================================# +# BVH Import script 1.05 patched by Campbell # +# Modified to use Mathutils for matrix math, # +# Fixed possible joint naming bug, # +# Imports BVH's with bad EOF gracefully # +# Fixed duplicate joint names, make them unique # +# Use \r as well as \n for newlines # +# Added suppot for nodes with 0 motion channels # +# Rotation IPOs never cross more then 180d # +# fixes sub frame tweening and time scaling # +# 5x overall speedup. # +# 06/12/2005, # +#===============================================# + +#===============================================# +# BVH Import script 1.04 patched by jms # +# Small modif for blender 2.40 # +# 04/12/2005, # +#===============================================# + +#===============================================# # BVH Import script 1.03 patched by Campbell # # Small optimizations and scale input # -# 01/01/2005, # +# 01/01/2005, # #===============================================# #===============================================# # BVH Import script 1.02 patched by Jm Soler # -# to the Poser 3.01 bvh file # -# 28/12/2004, # +# to the Poser 3.01 bvh file # +# 28/12/2004, # #===============================================# #===============================================# # BVH Import script 1.0 by Campbell Barton # # 25/03/2004, euler rotation code taken from # # Reevan Mckay's BVH import script v1.1 # -# if you have any questions about this script # -# email me ideasman@linuxmail.org # +# if you have any questions about this scrip. # +# email me cbarton@metavr.com # #===============================================# #===============================================# @@ -57,7 +77,7 @@ Empties created by this importer, it's in the Scripts window -> Scripts -> Anima #===============================================# # -------------------------------------------------------------------------- -# BVH Import v0.9 by Campbell Barton (AKA Ideasman) +# BVH Import v1.05 by Campbell Barton (AKA Ideasman) # -------------------------------------------------------------------------- # ***** BEGIN GPL LICENSE BLOCK ***** # @@ -68,457 +88,410 @@ Empties created by this importer, it's in the Scripts window -> Scripts -> Anima # # 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 ***** # -------------------------------------------------------------------------- - -import string -import math import Blender from Blender import Window, Object, Scene, Ipo, Draw from Blender.Scene import Render -# # PSYCO IS CRASHING ON MY SYSTEM -# # Attempt to load psyco, speed things up -# try: -# print 'using psyco to speed up BVH importing' -# import psyco -# psyco.full() -# -# except: -# print 'psyco is not present on this system' - -# Default scale -scale = 0.01 - -# Update as we load? -debug = 0 - -# Get the current scene. -scn = Scene.GetCurrent() -context = scn.getRenderingContext() - -# Here we store the Ipo curves in the order they load. -channelCurves = [] - -# Object list -# We need this so we can loop through the objects and edit there IPO's -# Chenging there rotation to EULER rotation -objectList = [] - -def getScale(): - return Draw.PupFloatInput('BVH Scale: ', 0.01, 0.001, 10.0, 0.1, 3) - -def MAT(m): - if len(m) == 3: - return Blender.Mathutils.Matrix(m[0], m[1], m[2]) - elif len(m) == 4: - return Blender.Mathutils.Matrix(m[0], m[1], m[2], m[3]) - - - -#===============================================# -# eulerRotation: converts X, Y, Z rotation # -# to eular Rotation. This entire function # -# is copied from Reevan Mckay's BVH script # -#===============================================# -# Vars used in eular rotation funtcion -DEG_TO_RAD = math.pi/180.0 -RAD_TO_DEG = 180.0/math.pi -PI=3.14159 - -def eulerRotate(x,y,z): - #================================= - def RVMatMult3 (mat1,mat2): - #================================= - mat3=[[0.0,0.0,0.0],[0.0,0.0,0.0],[0.0,0.0,0.0]] - for i in range(3): - for k in range(3): - for j in range(3): - mat3[i][k]=mat3[i][k]+mat1[i][j]*mat2[j][k] - return mat3 - - - #================================= - def RVAxisAngleToMat3 (rot4): - # Takes a direction vector and - # a rotation (in rads) and - # returns the rotation matrix. - # Graphics Gems I p. 466: - #================================= - mat3=[[0.0,0.0,0.0],[0.0,0.0,0.0],[0.0,0.0,0.0]] - if math.fabs(rot4[3])>0.01: - s=math.sin(rot4[3]) - c=math.cos(rot4[3]) - t=1.0-math.cos(rot4[3]) - else: - s=rot4[3] - c=1.0 - t=0.0 - - x=rot4[0]; y=rot4[1]; z=rot4[2] - - mat3[0][0]=t*x*x+c - mat3[0][1]=t*x*y+s*z - mat3[0][2]=t*x*z-s*y - - mat3[1][0]=t*x*y-s*z - mat3[1][1]=t*y*y+c - mat3[1][2]=t*y*z+s*x - - mat3[2][0]=t*x*z+s*y - mat3[2][1]=t*y*z-s*x - mat3[2][2]=t*z*z+c - - return mat3 - - eul = [x,y,z] - - for jj in range(3): - while eul[jj] < 0: - eul[jj] = eul[jj] + 360.0 - while eul[jj] >= 360.0: - eul[jj] = eul[jj] - 360.0 - - eul[0] = eul[0]*DEG_TO_RAD - eul[1] = eul[1]*DEG_TO_RAD - eul[2] = eul[2]*DEG_TO_RAD - - xmat=RVAxisAngleToMat3([1,0,0,eul[0]]) - ymat=RVAxisAngleToMat3([0,1,0,eul[1]]) - zmat=RVAxisAngleToMat3([0,0,1,eul[2]]) - - mat=[[1.0,0.0,0.0],[0.0,1.0,0.0],[0.0,0.0,1.0]] - - # Standard BVH multiplication order - mat=RVMatMult3 (zmat,mat) - mat=RVMatMult3 (xmat,mat) - mat=RVMatMult3 (ymat,mat) - - - ''' - # Screwy Animation Master BVH multiplcation order - mat=RVMatMult3 (ymat,mat) - mat=RVMatMult3 (xmat,mat) - mat=RVMatMult3 (zmat,mat) - ''' - mat = MAT(mat) - - eul = mat.toEuler() - x =- eul[0]/-10 - y =- eul[1]/-10 - z =- eul[2]/-10 - - return x, y, z # Returm euler roration values. - - - -#===============================================# -# makeJoint: Here we use the node data # -# from the BVA file to create an empty # -#===============================================# -def makeJoint(name, parent, prefix, offset, channels): - global scale - # Make Empty, with the prefix in front of the name - ob = Object.New('Empty', prefix + name) # New object, ob is shorter and nicer to use. - scn.link(ob) # place the object in the current scene - - # Offset Empty - ob.setLocation(offset[0]*scale, offset[1]*scale, offset[2]*scale) - - # Make me a child of another empty. - # Vale of None will make the empty a root node (no parent) - if parent[-1] != None: - obParent = Object.Get(prefix + parent[-1]) # We use this a bit so refrence it here. - obParent.makeParent([ob], 0, 1) #ojbs, noninverse, 1 = not fast. - - # Add Ipo's for necessary channels - newIpo = Ipo.New('Object', prefix + name) - ob.setIpo(newIpo) - for channelType in channels: - if channelType == 'Xposition': - newIpo.addCurve('LocX') - newIpo.getCurve('LocX').setInterpolation('Linear') - if channelType == 'Yposition': - newIpo.addCurve('LocY') - newIpo.getCurve('LocY').setInterpolation('Linear') - if channelType == 'Zposition': - newIpo.addCurve('LocZ') - newIpo.getCurve('LocZ').setInterpolation('Linear') - - if channelType == 'Zrotation': - newIpo.addCurve('RotZ') - newIpo.getCurve('RotZ').setInterpolation('Linear') - if channelType == 'Yrotation': - newIpo.addCurve('RotY') - newIpo.getCurve('RotY').setInterpolation('Linear') - if channelType == 'Xrotation': - newIpo.addCurve('RotX') - newIpo.getCurve('RotX').setInterpolation('Linear') - - # Add to object list - objectList.append(ob) - - # Redraw if debugging - if debug: Blender.Redraw() - - -#===============================================# -# makeEnd: Here we make an end node # -# This is needed when adding the last bone # -#===============================================# -def makeEnd(parent, prefix, offset): - # Make Empty, with the prefix in front of the name, end nodes have no name so call it its parents name+'_end' - ob = Object.New('Empty', prefix + parent[-1] + '_end') # New object, ob is shorter and nicer to use. - scn.link(ob) - - # Dont check for a parent, an end node MUST have a parent - obParent = Object.Get(prefix + parent[-1]) # We use this a bit so refrence it here. - obParent.makeParent([ob], 0, 1) #ojbs, noninverse, 1 = not fast. - - # Offset Empty - ob.setLocation(offset[0]*scale, offset[1]*scale, offset[2]*scale) - - # Redraw if debugging - if debug: Blender.Redraw() - - - - -#===============================================# -# MAIN FUNCTION - All things are done from here # -#===============================================# -def loadBVH(filename): - global scale - print '' - print 'BVH Importer 1.0 by Campbell Barton (Ideasman) - ideasman@linuxmail.org' - alpha='abcdefghijklmnopqrstuvewxyz' - ALPHA=alpha+alpha.upper() - ALPHA+=' 0123456789+-{}. ' - time1 = Blender.sys.time() - tmpScale = getScale() - if tmpScale != None: - scale = tmpScale - - # File loading stuff - # Open the file for importing - file = open(filename, 'r') - fileData = file.readlines() - # Make a list of lines - lines = [] - for fileLine in fileData: - fileLine=fileLine.replace('..','.') - newLine = string.split(fileLine) - if newLine != []: - t=[] - for n in newLine: - for n0 in n: - if n0 not in ALPHA: - n=n.replace(n0,'') - t.append(n) - lines.append(t) - - - del fileData - # End file loading code - - # Call object names with this prefix, mainly for scenes with multiple BVH's - Can imagine most partr names are the same - # So in future - #prefix = str(len(lines)) + '_' - - prefix = '_' - - # Create Hirachy as empties - if lines[0][0] == 'HIERARCHY': - print 'Importing the BVH Hierarchy for:', filename - else: - return 'ERROR: This is not a BVH file' - - # A liniar list of ancestors to keep track of a single objects heratage - # at any one time, this is appended and removed, dosent store tree- just a liniar list. - # ZERO is a place holder that means we are a root node. (no parents) - parent = [None] - - #channelList [(<objectName>, [channelType1, channelType2...]), (<objectName>, [channelType1, channelType2...)] - channelList = [] - channelIndex = -1 - - - - lineIdx = 1 # An index for the file. - while lineIdx < len(lines) -1: - #... - if lines[lineIdx][0] == 'ROOT' or lines[lineIdx][0] == 'JOINT': - if lines[lineIdx][0] == 'JOINT' and len(lines[lineIdx])>2: - for j in range(2,len(lines[lineIdx])) : - lines[lineIdx][1]+='_'+lines[lineIdx][j] - - # MAY NEED TO SUPPORT MULTIPLE ROOT's HERE!!!, Still unsure weather multiple roots are possible.?? - - print len(parent) * ' ' + 'node:',lines[lineIdx][1],' parent:',parent[-1] - print lineIdx - name = lines[lineIdx][1] - print name,lines[lineIdx+1],lines[lineIdx+2] - lineIdx += 2 # Incriment to the next line (Offset) - offset = ( float(lines[lineIdx][1]), float(lines[lineIdx][2]), float(lines[lineIdx][3]) ) - lineIdx += 1 # Incriment to the next line (Channels) - - # newChannel[Xposition, Yposition, Zposition, Xrotation, Yrotation, Zrotation] - # newChannel has Indecies to the motiondata, - # -1 refers to the last value that will be added on loading at a value of zero - # We'll add a zero value onto the end of the MotionDATA so this is always refers to a value. - newChannel = [-1, -1, -1, -1, -1, -1] - for channel in lines[lineIdx][2:]: - channelIndex += 1 # So the index points to the right channel - if channel == 'Xposition': - newChannel[0] = channelIndex - elif channel == 'Yposition': - newChannel[1] = channelIndex - elif channel == 'Zposition': - newChannel[2] = channelIndex - elif channel == 'Xrotation': - newChannel[3] = channelIndex - elif channel == 'Yrotation': - newChannel[4] = channelIndex - elif channel == 'Zrotation': - newChannel[5] = channelIndex - - channelList.append(newChannel) - - channels = lines[lineIdx][2:] - - # Call funtion that uses the gatrhered data to make an empty. - makeJoint(name, parent, prefix, offset, channels) - - # If we have another child then we can call ourselves a parent, else - parent.append(name) - - # Account for an end node - if lines[lineIdx][0] == 'End' and lines[lineIdx][1] == 'Site': # There is somtimes a name afetr 'End Site' but we will ignore it. - lineIdx += 2 # Incriment to the next line (Offset) - offset = ( float(lines[lineIdx][1]), float(lines[lineIdx][2]), float(lines[lineIdx][3]) ) - makeEnd(parent, prefix, offset) - - # Just so we can remove the Parents in a uniform way- End end never has kids - # so this is a placeholder - parent.append(None) - - if lines[lineIdx] == ['}']: - parent = parent[:-1] # Remove the last item - - - #=============================================# - # BVH Structure loaded, Now import motion # - #=============================================# - if lines[lineIdx] == ['MOTION']: - print '\nImporting motion data' - lineIdx += 3 # Set the cursor to the forst frame - - #=============================================# - # Loop through frames, each line a frame # - #=============================================# - currentFrame = 1 - print 'frames: ', - - - #=============================================# - # Add a ZERO keyframe, this keeps the rig # - # so when we export we know where all the # - # joints start from # - #=============================================# - obIdx = 0 - while obIdx < len(objectList) -1: - if channelList[obIdx][0] != -1: - objectList[obIdx].getIpo().getCurve('LocX').addBezier((currentFrame,0)) - if channelList[obIdx][1] != -1: - objectList[obIdx].getIpo().getCurve('LocY').addBezier((currentFrame,0)) - if channelList[obIdx][2] != -1: - objectList[obIdx].getIpo().getCurve('LocZ').addBezier((currentFrame,0)) - if channelList[obIdx][3] != '-1' or channelList[obIdx][4] != '-1' or channelList[obIdx][5] != '-1': - objectList[obIdx].getIpo().getCurve('RotX').addBezier((currentFrame,0)) - objectList[obIdx].getIpo().getCurve('RotY').addBezier((currentFrame,0)) - objectList[obIdx].getIpo().getCurve('RotZ').addBezier((currentFrame,0)) - obIdx += 1 - - while lineIdx < len(lines): - - # Exit loop if we are past the motiondata. - # Some BVH's have extra tags like 'CONSTRAINTS and MOTIONTAGS' - # I dont know what they do and I dont care, they'll be ignored here. - if len(lines[lineIdx]) < len(objectList): - print '...ending on unknown tags' - break - - - currentFrame += 1 # Incriment to next frame - - #=============================================# - # Import motion data and assign it to an IPO # - #=============================================# - lines[lineIdx].append('0') # Use this as a dummy var for objects that dont have a rotate channel. - obIdx = 0 - if debug: Blender.Redraw() - while obIdx < len(objectList) -1: - if channelList[obIdx][0] != -1: - VAL0=lines[lineIdx][channelList[obIdx][0]] - if VAL0.find('.')==-1: - VAL0=VAL0[:len(VAL0)-6]+'.'+VAL0[-6:] - objectList[obIdx].getIpo().getCurve('LocX').addBezier((currentFrame, scale * float(VAL0))) - - if channelList[obIdx][1] != -1: - VAL1=lines[lineIdx][channelList[obIdx][1]] - if VAL1.find('.')==-1: - VAL1=VAL1[:len(VAL1)-6]+'.'+VAL1[-6:] - objectList[obIdx].getIpo().getCurve('LocY').addBezier((currentFrame, scale * float(VAL1))) - - if channelList[obIdx][2] != -1: - VAL2=lines[lineIdx][channelList[obIdx][2]] - if VAL2.find('.')==-1: - VAL2=VAL2[:len(VAL2)-6]+'.'+VAL2[-6:] - objectList[obIdx].getIpo().getCurve('LocZ').addBezier((currentFrame, scale * float(VAL2))) - - if channelList[obIdx][3] != '-1' or channelList[obIdx][4] != '-1' or channelList[obIdx][5] != '-1': - VAL3=lines[lineIdx][channelList[obIdx][3]] - if VAL3.find('.')==-1: - VAL3=VAL3[:len(VAL3)-6]+'.'+VAL3[-6:] - - VAL4=lines[lineIdx][channelList[obIdx][4]] - if VAL4.find('.')==-1: - VAL4=VAL4[:len(VAL4)-6]+'.'+VAL4[-6:] - - VAL5=lines[lineIdx][channelList[obIdx][5]] - if VAL5.find('.')==-1: - VAL5=VAL5[:len(VAL5)-6]+'.'+VAL5[-6:] - - x, y, z = eulerRotate(float(VAL3), float(VAL4), float(VAL5)) - - objectList[obIdx].getIpo().getCurve('RotX').addBezier((currentFrame, x)) - objectList[obIdx].getIpo().getCurve('RotY').addBezier((currentFrame, y)) - objectList[obIdx].getIpo().getCurve('RotZ').addBezier((currentFrame, z)) - - obIdx += 1 - # Done importing motion data # - - # lines[lineIdx] = None # Scrap old motion data, save some memory? - lineIdx += 1 - # We have finished now - print currentFrame, 'done.' - - # No point in looking further, when this loop is done - # There is nothine else left to do - print 'Imported ', currentFrame, ' frames' - break - - # Main file loop - lineIdx += 1 - print "bvh import time: ", Blender.sys.time() - time1 - -Blender.Window.FileSelector(loadBVH, "Import BVH") +# Attempt to load psyco, speed things up +try: + import psyco + psyco.full() + print 'using psyco to speed up BVH importing' +except: + #print 'psyco is not present on this system' + pass + + + +def main(): + global scale + scale = None + + # Update as we load? + debug = 0 + + def getScale(): + return Draw.PupFloatInput('BVH Scale: ', 0.01, 0.001, 10.0, 0.1, 3) + + + #===============================================# + # MAIN FUNCTION - All things are done from here # + #===============================================# + def loadBVH(filename): + global scale + print '\nBVH Importer 1.05 by Campbell Barton (Ideasman) - cbarton@metavr.com' + + objectCurveMapping = {} + objectNameMapping = {} + objectMotiondataMapping = {} + + # Here we store the Ipo curves in the order they load. + channelCurves = [] + + # Object list + # We need this so we can loop through the objects and edit there IPO's + # Chenging there rotation to EULER rotation + objectList = [] + + if scale == None: + tempscale = getScale() + if tempscale: + scale = tempscale + else: + scale = 0.01 + + Window.WaitCursor(1) + # Unique names, dont reuse any of these names. + uniqueObNames = [ob.name for ob in Object.Get()] + + + # FUNCTIONS ====================================# + def getUniqueObName(name): + i = 0 + newname = name[:min(len(name), 12)] # Concatinate to 12 chars + while newname in uniqueObNames: + newname = name + str(i) + i+=1 + return newname + + # Change the order rotation is applied. + RotationMatrix = Blender.Mathutils.RotationMatrix + MATRIX_IDENTITY_3x3 = Blender.Mathutils.Matrix([1.0,0.0,0.0],[0.0,1.0,0.0],[0.0,0.0,1.0]) + def eulerRotate(x,y,z): + x,y,z = x%360,y%360,z%360 # Clamp all values between 0 and 360, values outside this raise an error. + xmat = RotationMatrix(x,3,'x') + ymat = RotationMatrix(y,3,'y') + zmat = RotationMatrix(z,3,'z') + # Standard BVH multiplication order, apply the rotation in the order Z,X,Y + return (ymat*(xmat * (zmat * MATRIX_IDENTITY_3x3))).toEuler() + + + currentFrame = 1 # Set the initial frame to import all data to. + + #===============================================# + # makeJoint: Here we use the node data # + # from the BVA file to create an empty # + #===============================================# + BVH2BLEND_TX_NAME = {'Xposition':'LocX','Yposition':'LocY','Zposition':'LocZ','Xrotation':'RotX','Yrotation':'RotY','Zrotation':'RotZ'} + def makeJoint(name, parent, offset, channels): + ob = Object.New('Empty', name) # New object, ob is shorter and nicer to use. + + objectNameMapping[name] = ob + scn.link(ob) # place the object in the current scene + ob.sel = 1 + + # Make me a child of another empty. + # Vale of None will make the empty a root node (no parent) + if parent[-1]: # != None + obParent = objectNameMapping[parent[-1]] # We use this a bit so refrence it here. + obParent.makeParent([ob], 1, 0) #ojbs, noninverse, 1 = not fast. + + # Offset Empty from BVH's initial joint location. + ob.setLocation(offset[0]*scale, offset[1]*scale, offset[2]*scale) + + # Add Ipo's for necessary channels + newIpo = Ipo.New('Object', name) + ob.setIpo(newIpo) + obname = ob.name + for channelType in channels: + channelType = BVH2BLEND_TX_NAME[channelType] + curve = newIpo.addCurve(channelType) + curve.setInterpolation('Linear') + objectCurveMapping[(obname, channelType)] = curve + + # Add to object list + objectList.append(ob) + + # Redraw if debugging + if debug: Blender.Redraw() + + + #===============================================# + # makeEnd: Here we make an end node # + # This is needed when adding the last bone # + #===============================================# + def makeEnd(parent, offset): + new_name = parent[-1] + '_end' + ob = Object.New('Empty', new_name) # New object, ob is shorter and nicer to use. + objectNameMapping[new_name] = ob + scn.link(ob) + ob.sel = 1 + + # Dont check for a parent, an end node MUST have a parent + obParent = objectNameMapping[parent[-1]] # We use this a bit so refrence it here. + obParent.makeParent([ob], 1, 0) #ojbs, noninverse, 1 = not fast. + + # Offset Empty + ob.setLocation(offset[0]*scale, offset[1]*scale, offset[2]*scale) + + # Redraw if debugging + if debug: Blender.Redraw() + # END FUNCTION DEFINITIONS ====================================# + + + + + time1 = Blender.sys.time() + + # Get the current scene. + scn = Scene.GetCurrent() + #context = scn.getRenderingContext() + + # DeSelect All + for ob in scn.getChildren(): + ob.sel = 0 + + # File loading stuff + # Open the file for importing + file = open(filename, 'r') + + # Seperate into a list of lists, each line a list of words. + lines = file.readlines() + # Non standard carrage returns? + if len(lines) == 1: + lines = lines[0].split('\r') + + # Split by whitespace. + lines =[ll for ll in [ [w for w in l.split() if w != '\n' ] for l in lines] if ll] + # End file loading code + + + + # Create Hirachy as empties + if lines[0][0] == 'HIERARCHY': + print 'Importing the BVH Hierarchy for:', filename + else: + return 'ERROR: This is not a BVH file' + + # A liniar list of ancestors to keep track of a single objects heratage + # at any one time, this is appended and removed, dosent store tree- just a liniar list. + # ZERO is a place holder that means we are a root node. (no parents) + parent = [None] + + #channelList, sync with objectList: [[channelType1, channelType2...], [channelType1, channelType2...)] + channelList = [] + channelIndex = -1 + + lineIdx = 0 # An index for the file. + while lineIdx < len(lines) -1: + #... + if lines[lineIdx][0] == 'ROOT' or lines[lineIdx][0] == 'JOINT': + + # Join spaces into 1 word with underscores joining it. + if len(lines[lineIdx]) > 2: + lines[lineIdx][1] = '_'.join(lines[lineIdx][1:]) + lines[lineIdx] = lines[lineIdx][:2] + + # MAY NEED TO SUPPORT MULTIPLE ROOT's HERE!!!, Still unsure weather multiple roots are possible.?? + + # Make sure the names are unique- Object names will match joint names exactly and both will be unique. + name = getUniqueObName(lines[lineIdx][1]) + uniqueObNames.append(name) + + print '%snode: %s, parent: %s' % (len(parent) * ' ', name, parent[-1]) + + lineIdx += 2 # Incriment to the next line (Offset) + offset = ( float(lines[lineIdx][1]), float(lines[lineIdx][2]), float(lines[lineIdx][3]) ) + lineIdx += 1 # Incriment to the next line (Channels) + + # newChannel[Xposition, Yposition, Zposition, Xrotation, Yrotation, Zrotation] + # newChannel references indecies to the motiondata, + # if not assigned then -1 refers to the last value that will be added on loading at a value of zero, this is appended + # We'll add a zero value onto the end of the MotionDATA so this is always refers to a value. + newChannel = [-1, -1, -1, -1, -1, -1] + for channel in lines[lineIdx][2:]: + channelIndex += 1 # So the index points to the right channel + if channel == 'Xposition': + newChannel[0] = channelIndex + elif channel == 'Yposition': + newChannel[1] = channelIndex + elif channel == 'Zposition': + newChannel[2] = channelIndex + elif channel == 'Xrotation': + newChannel[3] = channelIndex + elif channel == 'Yrotation': + newChannel[4] = channelIndex + elif channel == 'Zrotation': + newChannel[5] = channelIndex + + channelList.append(newChannel) + + channels = lines[lineIdx][2:] + + # Call funtion that uses the gatrhered data to make an empty. + makeJoint(name, parent, offset, channels) + + # If we have another child then we can call ourselves a parent, else + parent.append(name) + + # Account for an end node + if lines[lineIdx][0] == 'End' and lines[lineIdx][1] == 'Site': # There is somtimes a name after 'End Site' but we will ignore it. + lineIdx += 2 # Incriment to the next line (Offset) + offset = ( float(lines[lineIdx][1]), float(lines[lineIdx][2]), float(lines[lineIdx][3]) ) + makeEnd(parent, offset) + + # Just so we can remove the Parents in a uniform way- End end never has kids + # so this is a placeholder + parent.append(None) + + if len(lines[lineIdx]) == 1 and lines[lineIdx][0] == '}': # == ['}'] + parent.pop() # Remove the last item + + #=============================================# + # BVH Structure loaded, Now import motion # + #=============================================# + if len(lines[lineIdx]) == 1 and lines[lineIdx][0] == 'MOTION': + print '\nImporting motion data' + lineIdx += 3 # Set the cursor to the first frame + + #=============================================# + # Add a ZERO keyframe, this keeps the rig # + # so when we export we know where all the # + # joints start from # + #=============================================# + + for obIdx, ob in enumerate(objectList): + obname = ob.name + if channelList[obIdx][0] != -1: + objectCurveMapping[obname, 'LocX'].addBezier((currentFrame,0)) + objectMotiondataMapping[obname, 'LocX'] = [] + if channelList[obIdx][1] != -1: + objectCurveMapping[obname, 'LocY'].addBezier((currentFrame,0)) + objectMotiondataMapping[obname, 'LocY'] = [] + if channelList[obIdx][2] != -1: + objectCurveMapping[obname, 'LocZ'].addBezier((currentFrame,0)) + objectMotiondataMapping[obname, 'LocZ'] = [] + if\ + channelList[obIdx][3] != -1 or\ + channelList[obIdx][4] != -1 or\ + channelList[obIdx][5] != -1: + objectMotiondataMapping[obname, 'RotX'] = [] + objectMotiondataMapping[obname, 'RotY'] = [] + objectMotiondataMapping[obname, 'RotZ'] = [] + + #=============================================# + # Loop through frames, each line a frame # + #=============================================# + MOTION_DATA_LINE_LEN = len(lines[lineIdx]) + while lineIdx < len(lines): + line = lines[lineIdx] + if MOTION_DATA_LINE_LEN != len(line): + print 'ERROR: Incomplete motion data on line %i, finishing import.' % lineIdx + break + + # Exit loop if we are past the motiondata. + # Some BVH's have extra tags like 'CONSTRAINTS and MOTIONTAGS' + # I dont know what they do and I dont care, they'll be ignored here. + if len(line) < len(objectList): + print '...ending on unknown tags' + break + + + currentFrame += 1 # Incriment to next frame + + #=============================================# + # Import motion data and assign it to an IPO # + #=============================================# + line.append(0.0) # Use this as a dummy var for objects that dont have a loc/rotate channel. + + if debug: Blender.Redraw() + for obIdx, ob in enumerate(objectList): + obname = ob.name + obChannel = channelList[obIdx] + if channelList[obIdx][0] != -1: + objectMotiondataMapping[obname, 'LocX'].append((currentFrame, scale * float( line[obChannel[0]] ))) + + if channelList[obIdx][1] != -1: + objectMotiondataMapping[obname, 'LocY'].append((currentFrame, scale * float( line[obChannel[1]] ))) + + if channelList[obIdx][2] != -1: + objectMotiondataMapping[obname, 'LocZ'].append((currentFrame, scale * float( line[obChannel[2]] ))) + + if obChannel[3] != -1 or obChannel[4] != -1 or obChannel[5] != -1: + x, y, z = eulerRotate(float( line[obChannel[3]] ), float( line[obChannel[4]] ), float( line[obChannel[5]] )) + x,y,z = x/10.0, y/10.0, z/10.0 # For IPO's 36 is 360d + motionMappingRotX = objectMotiondataMapping[obname, 'RotX'] + motionMappingRotY = objectMotiondataMapping[obname, 'RotY'] + motionMappingRotZ = objectMotiondataMapping[obname, 'RotZ'] + + # Make interpolation not cross between 180d, thjis fixes sub frame interpolation and time scaling. + # Will go from (355d to 365d) rather then to (355d to 5d) - inbetween these 2 there will now be a correct interpolation. + if len(motionMappingRotX) > 1: + while (motionMappingRotX[-1][1] - x) > 18: x+=36 + while (motionMappingRotX[-1][1] - x) < -18: x-=36 + + while (motionMappingRotY[-1][1] - y) > 18: y+=36 + while (motionMappingRotY[-1][1] - y) < -18: y-=36 + + while (motionMappingRotZ[-1][1] - z) > 18: z+=36 + while (motionMappingRotZ[-1][1] - z) < -18: z-=36 + + motionMappingRotX.append((currentFrame, x)) + motionMappingRotY.append((currentFrame, y)) + motionMappingRotZ.append((currentFrame, z)) + # Done importing motion data # + + lineIdx += 1 + + #=======================================# + # Now Write the motion to the IPO's # + #=======================================# + for key, motion_data in objectMotiondataMapping.iteritems(): + + # Strip the motion data where all the points have the same falue. + i = len(motion_data) -2 + while i > 0 and len(motion_data) > 2: + if motion_data[i][1] == motion_data[i-1][1] == motion_data[i+1][1]: + motion_data.pop(i) + i-=1 + # Done stripping. + + obname, tx_type = key + curve = objectCurveMapping[obname, tx_type] + for point_data in motion_data: + curve.addBezier( point_data ) + # Imported motion to an IPO + + # No point in looking further, when this loop is done + # There is nothine else left to do + break + + # Main file loop + lineIdx += 1 + + print 'bvh import time for %i frames: %.6f' % (currentFrame, Blender.sys.time() - time1) + Window.RedrawAll() + Window.WaitCursor(0) + + Blender.Window.FileSelector(loadBVH, "Import BVH") + + #=============# + # TESTING # + #=============# + ''' + #loadBVH('/metavr/mocap/bvh/boxer.bvh') + #loadBVH('/metavr/mocap/bvh/dg-306-g.bvh') # Incompleate EOF + #loadBVH('/metavr/mocap/bvh/wa8lk.bvh') # duplicate joint names, \r line endings. + #loadBVH('/metavr/mocap/bvh/walk4.bvh') # 0 channels + scale = 0.01 + import os + DIR = '/metavr/mocap/bvh/' + for f in os.listdir(DIR): + if f.endswith('.bvh'): + s = Scene.New(f) + s.makeCurrent() + loadBVH(DIR + f) + ''' +if __name__ == '__main__': + main()
\ No newline at end of file |