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-06 06:53:35 +0300
committerCampbell Barton <ideasman42@gmail.com>2005-12-06 06:53:35 +0300
commit9497b6dfb69d449a4fe4dbf2c59cb50baa2aaa7b (patch)
tree87caa6073b4541477df29e46570f6a1293b86e6c
parent2d19c4f208f3910d834412a30a2c3cdaae44efcd (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.py877
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