~ubuntu-branches/ubuntu/gutsy/blender/gutsy-security

« back to all changes in this revision

Viewing changes to release/scripts/bvh_import.py

  • Committer: Bazaar Package Importer
  • Author(s): Florian Ernst
  • Date: 2005-11-06 12:40:03 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20051106124003-3pgs7tcg5rox96xg
Tags: 2.37a-1.1
* Non-maintainer upload.
* Split out parts of 01_SConstruct_debian.dpatch again: root_build_dir
  really needs to get adjusted before the clean target runs - closes: #333958,
  see #288882 for reference

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!BPY
 
2
 
 
3
"""
 
4
Name: 'Motion Capture (.bvh)...'
 
5
Blender: 236
 
6
Group: 'Import'
 
7
Tip: 'Import a (.bvh) motion capture file'
 
8
"""
 
9
 
 
10
__author__ = "Campbell Barton"
 
11
__url__ = ("blender", "elysiun", "http://jmsoler.free.fr/util/blenderfile/py/bvh_import.py")
 
12
__version__ = "1.0.2 04/12/28"
 
13
 
 
14
__bpydoc__ = """\
 
15
This script imports BVH motion capture data to Blender.
 
16
 
 
17
Supported: Poser 3.01<br>
 
18
 
 
19
Missing:<br>
 
20
 
 
21
Known issues:<br>
 
22
 
 
23
Notes:<br>
 
24
   Jean-Michel Soler improved importer to support Poser 3.01 files;<br>
 
25
   Jean-Baptiste Perin wrote a script to create an armature out of the
 
26
Empties created by this importer, it's in the Scripts window -> Scripts -> Animation menu.
 
27
"""
 
28
 
 
29
# $Id: bvh_import.py,v 1.6 2005/05/17 07:17:52 ianwill Exp $
 
30
#
 
31
 
 
32
#===============================================#
 
33
# BVH Import script 1.03 patched by Campbell    #
 
34
# Small optimizations and scale input           #
 
35
# 01/01/2005,                                   #  
 
36
#===============================================#
 
37
 
 
38
#===============================================#
 
39
# BVH Import script 1.02 patched by Jm Soler    #
 
40
# to the Poser 3.01 bvh file                    # 
 
41
# 28/12/2004,                                   #  
 
42
#===============================================#
 
43
 
 
44
#===============================================#
 
45
# BVH Import script 1.0 by Campbell Barton      #
 
46
# 25/03/2004, euler rotation code taken from    #
 
47
# Reevan Mckay's BVH import script v1.1         #
 
48
# if you have any questions about this script   #
 
49
# email me ideasman@linuxmail.org               #
 
50
#===============================================#
 
51
 
 
52
#===============================================#
 
53
# TODO:                                         #
 
54
# * Create bones when importing                 #
 
55
# * Make an IPO jitter removal script           #
 
56
# * Work out a better naming system             #
 
57
#===============================================#
 
58
 
 
59
# -------------------------------------------------------------------------- 
 
60
# BVH Import v0.9 by Campbell Barton (AKA Ideasman) 
 
61
# -------------------------------------------------------------------------- 
 
62
# ***** BEGIN GPL LICENSE BLOCK ***** 
 
63
 
64
# This program is free software; you can redistribute it and/or 
 
65
# modify it under the terms of the GNU General Public License 
 
66
# as published by the Free Software Foundation; either version 2 
 
67
# of the License, or (at your option) any later version. 
 
68
 
69
# This program is distributed in the hope that it will be useful, 
 
70
# but WITHOUT ANY WARRANTY; without even the implied warranty of 
 
71
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 
72
# GNU General Public License for more details. 
 
73
 
74
# You should have received a copy of the GNU General Public License 
 
75
# along with this program; if not, write to the Free Software Foundation, 
 
76
# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
 
77
 
78
# ***** END GPL LICENCE BLOCK ***** 
 
79
# -------------------------------------------------------------------------- 
 
80
 
 
81
 
 
82
import string
 
83
import math
 
84
import Blender
 
85
from Blender import Window, Object, Scene, Ipo, Draw
 
86
from Blender.Scene import Render
 
87
 
 
88
 
 
89
# # PSYCO IS CRASHING ON MY SYSTEM
 
90
# # Attempt to load psyco, speed things up
 
91
# try:
 
92
#   print 'using psyco to speed up BVH importing'
 
93
#   import psyco
 
94
#   psyco.full()
 
95
#  
 
96
# except:
 
97
#   print 'psyco is not present on this system'
 
98
 
 
99
# Default scale
 
100
scale = 0.01
 
101
 
 
102
# Update as we load?
 
103
debug = 0
 
104
 
 
105
# Get the current scene.
 
106
scn = Scene.GetCurrent()
 
107
context = scn.getRenderingContext()
 
108
 
 
109
# Here we store the Ipo curves in the order they load.
 
110
channelCurves = []
 
111
 
 
112
# Object list
 
113
# We need this so we can loop through the objects and edit there IPO's 
 
114
# Chenging there rotation to EULER rotation
 
115
objectList = []
 
116
 
 
117
def getScale():
 
118
        return Draw.PupFloatInput('BVH Scale: ', 0.01, 0.001, 10.0, 0.1, 3)
 
119
 
 
120
def MAT(m):
 
121
        if len(m) == 3:
 
122
                return Blender.Mathutils.Matrix(m[0], m[1], m[2])
 
123
        elif len(m) == 4:
 
124
                return Blender.Mathutils.Matrix(m[0], m[1], m[2], m[3])
 
125
 
 
126
 
 
127
 
 
128
#===============================================#
 
129
# eulerRotation: converts X, Y, Z rotation      #
 
130
# to eular Rotation. This entire function       #
 
131
# is copied from Reevan Mckay's BVH script      #
 
132
#===============================================#
 
133
# Vars used in eular rotation funtcion
 
134
DEG_TO_RAD = math.pi/180.0
 
135
RAD_TO_DEG = 180.0/math.pi
 
136
PI=3.14159
 
137
 
 
138
def eulerRotate(x,y,z): 
 
139
  #=================================
 
140
  def RVMatMult3 (mat1,mat2):
 
141
  #=================================
 
142
    mat3=[[0.0,0.0,0.0],[0.0,0.0,0.0],[0.0,0.0,0.0]]
 
143
    for i in range(3):
 
144
      for k in range(3):
 
145
        for j in range(3):
 
146
          mat3[i][k]=mat3[i][k]+mat1[i][j]*mat2[j][k]
 
147
    return mat3
 
148
  
 
149
  
 
150
  #=================================
 
151
  def   RVAxisAngleToMat3 (rot4):
 
152
  #     Takes a direction vector and
 
153
  #     a rotation (in rads) and
 
154
  #     returns the rotation matrix.
 
155
  #     Graphics Gems I p. 466:
 
156
  #=================================
 
157
    mat3=[[0.0,0.0,0.0],[0.0,0.0,0.0],[0.0,0.0,0.0]]
 
158
    if math.fabs(rot4[3])>0.01:
 
159
      s=math.sin(rot4[3])
 
160
      c=math.cos(rot4[3])
 
161
      t=1.0-math.cos(rot4[3])
 
162
    else:
 
163
      s=rot4[3]
 
164
      c=1.0
 
165
      t=0.0
 
166
 
 
167
    x=rot4[0]; y=rot4[1]; z=rot4[2]
 
168
    
 
169
    mat3[0][0]=t*x*x+c
 
170
    mat3[0][1]=t*x*y+s*z
 
171
    mat3[0][2]=t*x*z-s*y 
 
172
    
 
173
    mat3[1][0]=t*x*y-s*z
 
174
    mat3[1][1]=t*y*y+c
 
175
    mat3[1][2]=t*y*z+s*x
 
176
    
 
177
    mat3[2][0]=t*x*z+s*y
 
178
    mat3[2][1]=t*y*z-s*x
 
179
    mat3[2][2]=t*z*z+c
 
180
    
 
181
    return mat3
 
182
 
 
183
  eul = [x,y,z]
 
184
  
 
185
  for jj in range(3):
 
186
    while eul[jj] < 0:
 
187
      eul[jj] = eul[jj] + 360.0
 
188
    while eul[jj] >= 360.0:
 
189
      eul[jj] = eul[jj] - 360.0
 
190
 
 
191
  eul[0] = eul[0]*DEG_TO_RAD
 
192
  eul[1] = eul[1]*DEG_TO_RAD
 
193
  eul[2] = eul[2]*DEG_TO_RAD
 
194
  
 
195
  xmat=RVAxisAngleToMat3([1,0,0,eul[0]])
 
196
  ymat=RVAxisAngleToMat3([0,1,0,eul[1]])
 
197
  zmat=RVAxisAngleToMat3([0,0,1,eul[2]])
 
198
  
 
199
  mat=[[1.0,0.0,0.0],[0.0,1.0,0.0],[0.0,0.0,1.0]]  
 
200
  
 
201
  # Standard BVH multiplication order
 
202
  mat=RVMatMult3 (zmat,mat)
 
203
  mat=RVMatMult3 (xmat,mat)
 
204
  mat=RVMatMult3 (ymat,mat)
 
205
  
 
206
  
 
207
  '''
 
208
  # Screwy Animation Master BVH multiplcation order
 
209
  mat=RVMatMult3 (ymat,mat)
 
210
  mat=RVMatMult3 (xmat,mat)
 
211
  mat=RVMatMult3 (zmat,mat)
 
212
  '''
 
213
  mat = MAT(mat)
 
214
  
 
215
  eul = mat.toEuler()
 
216
  x =- eul[0]/-10
 
217
  y =- eul[1]/-10
 
218
  z =- eul[2]/-10
 
219
  
 
220
  return x, y, z # Returm euler roration values.
 
221
 
 
222
 
 
223
 
 
224
#===============================================#
 
225
# makeJoint: Here we use the node data          #
 
226
# from the BVA file to create an empty          #
 
227
#===============================================#
 
228
def makeJoint(name, parent, prefix, offset, channels):
 
229
  global scale
 
230
  # Make Empty, with the prefix in front of the name
 
231
  ob = Object.New('Empty', prefix + name) # New object, ob is shorter and nicer to use.
 
232
  scn.link(ob) # place the object in the current scene
 
233
  
 
234
  # Offset Empty
 
235
  ob.setLocation(offset[0]*scale, offset[1]*scale, offset[2]*scale)
 
236
 
 
237
  # Make me a child of another empty.
 
238
  # Vale of None will make the empty a root node (no parent)
 
239
  if parent[-1] != None:
 
240
    obParent = Object.Get(prefix + parent[-1]) # We use this a bit so refrence it here.
 
241
    obParent.makeParent([ob], 0, 1) #ojbs, noninverse, 1 = not fast.
 
242
 
 
243
  # Add Ipo's for necessary channels
 
244
  newIpo = Ipo.New('Object', prefix + name)
 
245
  ob.setIpo(newIpo)
 
246
  for channelType in channels:
 
247
    if channelType == 'Xposition':
 
248
      newIpo.addCurve('LocX')
 
249
      newIpo.getCurve('LocX').setInterpolation('Linear')
 
250
    if channelType == 'Yposition':
 
251
      newIpo.addCurve('LocY')
 
252
      newIpo.getCurve('LocY').setInterpolation('Linear')
 
253
    if channelType == 'Zposition':
 
254
      newIpo.addCurve('LocZ')
 
255
      newIpo.getCurve('LocZ').setInterpolation('Linear')
 
256
 
 
257
    if channelType == 'Zrotation':
 
258
      newIpo.addCurve('RotZ')
 
259
      newIpo.getCurve('RotZ').setInterpolation('Linear')
 
260
    if channelType == 'Yrotation':
 
261
      newIpo.addCurve('RotY')
 
262
      newIpo.getCurve('RotY').setInterpolation('Linear')
 
263
    if channelType == 'Xrotation':
 
264
      newIpo.addCurve('RotX')
 
265
      newIpo.getCurve('RotX').setInterpolation('Linear')
 
266
 
 
267
  # Add to object list
 
268
  objectList.append(ob)
 
269
  
 
270
  # Redraw if debugging
 
271
  if debug: Blender.Redraw()
 
272
  
 
273
 
 
274
#===============================================#
 
275
# makeEnd: Here we make an end node             #
 
276
# This is needed when adding the last bone      #
 
277
#===============================================#
 
278
def makeEnd(parent, prefix, offset):
 
279
  # Make Empty, with the prefix in front of the name, end nodes have no name so call it its parents name+'_end'
 
280
  ob = Object.New('Empty', prefix + parent[-1] + '_end') # New object, ob is shorter and nicer to use.
 
281
  scn.link(ob)
 
282
  
 
283
  # Dont check for a parent, an end node MUST have a parent
 
284
  obParent = Object.Get(prefix + parent[-1]) # We use this a bit so refrence it here.
 
285
  obParent.makeParent([ob], 0, 1) #ojbs, noninverse, 1 = not fast.
 
286
 
 
287
  # Offset Empty
 
288
  ob.setLocation(offset[0]*scale, offset[1]*scale, offset[2]*scale) 
 
289
  
 
290
  # Redraw if debugging
 
291
  if debug: Blender.Redraw()  
 
292
  
 
293
 
 
294
 
 
295
 
 
296
#===============================================#
 
297
# MAIN FUNCTION - All things are done from here #
 
298
#===============================================#
 
299
def loadBVH(filename):
 
300
  global scale
 
301
  print ''
 
302
  print 'BVH Importer 1.0 by Campbell Barton (Ideasman) - ideasman@linuxmail.org'
 
303
  alpha='abcdefghijklmnopqrstuvewxyz'
 
304
  ALPHA=alpha+alpha.upper()
 
305
  ALPHA+=' 0123456789+-{}. '  
 
306
  time1 = Blender.sys.time()
 
307
  tmpScale = getScale()
 
308
  if tmpScale != None:
 
309
    scale = tmpScale
 
310
  
 
311
  # File loading stuff
 
312
  # Open the file for importing
 
313
  file = open(filename, 'r')  
 
314
  fileData = file.readlines()
 
315
  # Make a list of lines
 
316
  lines = []
 
317
  for fileLine in fileData:
 
318
    fileLine=fileLine.replace('..','.')
 
319
    newLine = string.split(fileLine)
 
320
    if newLine != []:
 
321
      t=[]
 
322
      for n in newLine:
 
323
         for n0 in n:
 
324
           if n0 not in ALPHA:
 
325
              n=n.replace(n0,'')  
 
326
         t.append(n)
 
327
      lines.append(t)
 
328
 
 
329
    
 
330
  del fileData
 
331
  # End file loading code
 
332
 
 
333
  # Call object names with this prefix, mainly for scenes with multiple BVH's - Can imagine most partr names are the same
 
334
  # So in future
 
335
  #prefix = str(len(lines)) + '_'
 
336
  
 
337
  prefix = '_'
 
338
  
 
339
  # Create Hirachy as empties
 
340
  if lines[0][0] == 'HIERARCHY':
 
341
    print 'Importing the BVH Hierarchy for:', filename
 
342
  else:
 
343
    return 'ERROR: This is not a BVH file'
 
344
  
 
345
  # A liniar list of ancestors to keep track of a single objects heratage
 
346
  # at any one time, this is appended and removed, dosent store tree- just a liniar list.
 
347
  # ZERO is a place holder that means we are a root node. (no parents)
 
348
  parent = [None]  
 
349
  
 
350
  #channelList [(<objectName>, [channelType1, channelType2...]),  (<objectName>, [channelType1, channelType2...)]
 
351
  channelList = []
 
352
  channelIndex = -1
 
353
 
 
354
  
 
355
 
 
356
  lineIdx = 1 # An index for the file.
 
357
  while lineIdx < len(lines) -1:
 
358
    #...
 
359
    if lines[lineIdx][0] == 'ROOT' or lines[lineIdx][0] == 'JOINT':
 
360
      if lines[lineIdx][0] == 'JOINT' and len(lines[lineIdx])>2:
 
361
         for j in range(2,len(lines[lineIdx])) :
 
362
             lines[lineIdx][1]+='_'+lines[lineIdx][j]
 
363
 
 
364
      # MAY NEED TO SUPPORT MULTIPLE ROOT's HERE!!!, Still unsure weather multiple roots are possible.??
 
365
 
 
366
      print len(parent) * '  ' + 'node:',lines[lineIdx][1],' parent:',parent[-1]
 
367
      print lineIdx
 
368
      name = lines[lineIdx][1]
 
369
      print name,lines[lineIdx+1],lines[lineIdx+2]
 
370
      lineIdx += 2 # Incriment to the next line (Offset)
 
371
      offset = ( float(lines[lineIdx][1]), float(lines[lineIdx][2]), float(lines[lineIdx][3]) )
 
372
      lineIdx += 1 # Incriment to the next line (Channels)
 
373
      
 
374
      # newChannel[Xposition, Yposition, Zposition, Xrotation, Yrotation, Zrotation]
 
375
      # newChannel has Indecies to the motiondata,
 
376
      # -1 refers to the last value that will be added on loading at a value of zero
 
377
      # We'll add a zero value onto the end of the MotionDATA so this is always refers to a value.
 
378
      newChannel = [-1, -1, -1, -1, -1, -1] 
 
379
      for channel in lines[lineIdx][2:]:
 
380
        channelIndex += 1 # So the index points to the right channel
 
381
        if channel == 'Xposition':
 
382
          newChannel[0] = channelIndex
 
383
        elif channel == 'Yposition':
 
384
          newChannel[1] = channelIndex
 
385
        elif channel == 'Zposition':
 
386
          newChannel[2] = channelIndex
 
387
        elif channel == 'Xrotation':
 
388
          newChannel[3] = channelIndex
 
389
        elif channel == 'Yrotation':
 
390
          newChannel[4] = channelIndex
 
391
        elif channel == 'Zrotation':
 
392
          newChannel[5] = channelIndex
 
393
      
 
394
      channelList.append(newChannel)
 
395
      
 
396
      channels = lines[lineIdx][2:]
 
397
      
 
398
      # Call funtion that uses the gatrhered data to make an empty.
 
399
      makeJoint(name, parent, prefix, offset, channels)
 
400
      
 
401
      # If we have another child then we can call ourselves a parent, else 
 
402
      parent.append(name)
 
403
 
 
404
    # Account for an end node
 
405
    if lines[lineIdx][0] == 'End' and lines[lineIdx][1] == 'Site': # There is somtimes a name afetr 'End Site' but we will ignore it.
 
406
      lineIdx += 2 # Incriment to the next line (Offset)
 
407
      offset = ( float(lines[lineIdx][1]), float(lines[lineIdx][2]), float(lines[lineIdx][3]) )
 
408
      makeEnd(parent, prefix, offset)
 
409
 
 
410
      # Just so we can remove the Parents in a uniform way- End end never has kids
 
411
      # so this is a placeholder
 
412
      parent.append(None)
 
413
 
 
414
    if lines[lineIdx] == ['}']:
 
415
      parent = parent[:-1] # Remove the last item
 
416
 
 
417
 
 
418
    #=============================================#
 
419
    # BVH Structure loaded, Now import motion     #
 
420
    #=============================================#    
 
421
    if lines[lineIdx] == ['MOTION']:
 
422
      print '\nImporting motion data'
 
423
      lineIdx += 3 # Set the cursor to the forst frame
 
424
      
 
425
      #=============================================#
 
426
      # Loop through frames, each line a frame      #
 
427
      #=============================================#      
 
428
      currentFrame = 1
 
429
      print 'frames: ',
 
430
      
 
431
      
 
432
      #=============================================#
 
433
      # Add a ZERO keyframe, this keeps the rig     #
 
434
      # so when we export we know where all the     #
 
435
      # joints start from                           #
 
436
      #=============================================#  
 
437
      obIdx = 0
 
438
      while obIdx < len(objectList) -1:
 
439
        if channelList[obIdx][0] != -1:
 
440
          objectList[obIdx].getIpo().getCurve('LocX').addBezier((currentFrame,0))
 
441
        if channelList[obIdx][1] != -1:
 
442
          objectList[obIdx].getIpo().getCurve('LocY').addBezier((currentFrame,0))
 
443
        if channelList[obIdx][2] != -1:
 
444
          objectList[obIdx].getIpo().getCurve('LocZ').addBezier((currentFrame,0))
 
445
        if channelList[obIdx][3] != '-1' or channelList[obIdx][4] != '-1' or channelList[obIdx][5] != '-1':
 
446
          objectList[obIdx].getIpo().getCurve('RotX').addBezier((currentFrame,0))
 
447
          objectList[obIdx].getIpo().getCurve('RotY').addBezier((currentFrame,0))
 
448
          objectList[obIdx].getIpo().getCurve('RotZ').addBezier((currentFrame,0))
 
449
        obIdx += 1
 
450
      
 
451
      while lineIdx < len(lines):
 
452
        
 
453
        # Exit loop if we are past the motiondata.
 
454
        # Some BVH's have extra tags like 'CONSTRAINTS and MOTIONTAGS'
 
455
        # I dont know what they do and I dont care, they'll be ignored here.
 
456
        if len(lines[lineIdx]) < len(objectList):
 
457
          print '...ending on unknown tags'
 
458
          break
 
459
        
 
460
        
 
461
        currentFrame += 1 # Incriment to next frame
 
462
                
 
463
        #=============================================#
 
464
        # Import motion data and assign it to an IPO  #
 
465
        #=============================================#
 
466
        lines[lineIdx].append('0') # Use this as a dummy var for objects that dont have a rotate channel.
 
467
        obIdx = 0
 
468
        if debug: Blender.Redraw() 
 
469
        while obIdx < len(objectList) -1:
 
470
          if channelList[obIdx][0] != -1:
 
471
            VAL0=lines[lineIdx][channelList[obIdx][0]]  
 
472
            if VAL0.find('.')==-1:
 
473
               VAL0=VAL0[:len(VAL0)-6]+'.'+VAL0[-6:] 
 
474
            objectList[obIdx].getIpo().getCurve('LocX').addBezier((currentFrame, scale * float(VAL0)))
 
475
 
 
476
          if channelList[obIdx][1] != -1:
 
477
            VAL1=lines[lineIdx][channelList[obIdx][0]]  
 
478
            if VAL1.find('.')==-1:
 
479
               VAL1=VAL1[:len(VAL1)-6]+'.'+VAL1[-6:] 
 
480
            objectList[obIdx].getIpo().getCurve('LocY').addBezier((currentFrame, scale * float(VAL1)))
 
481
 
 
482
          if channelList[obIdx][2] != -1:
 
483
            VAL2=lines[lineIdx][channelList[obIdx][0]]  
 
484
            if VAL2.find('.')==-1:
 
485
               VAL2=VAL2[:len(VAL2)-6]+'.'+VAL2[-6:] 
 
486
            objectList[obIdx].getIpo().getCurve('LocZ').addBezier((currentFrame, scale * float(VAL2)))
 
487
          
 
488
          if channelList[obIdx][3] != '-1' or channelList[obIdx][4] != '-1' or channelList[obIdx][5] != '-1':
 
489
            VAL3=lines[lineIdx][channelList[obIdx][3]]  
 
490
            if VAL3.find('.')==-1:
 
491
               VAL3=VAL3[:len(VAL3)-6]+'.'+VAL3[-6:]
 
492
 
 
493
            VAL4=lines[lineIdx][channelList[obIdx][4]]
 
494
            if VAL4.find('.')==-1:
 
495
               VAL4=VAL4[:len(VAL4)-6]+'.'+VAL4[-6:]
 
496
 
 
497
            VAL5=lines[lineIdx][channelList[obIdx][5]] 
 
498
            if VAL5.find('.')==-1:
 
499
               VAL5=VAL5[:len(VAL5)-6]+'.'+VAL5[-6:]
 
500
 
 
501
            x, y, z = eulerRotate(float(VAL3), float(VAL4), float(VAL5))
 
502
 
 
503
            objectList[obIdx].getIpo().getCurve('RotX').addBezier((currentFrame, x))
 
504
            objectList[obIdx].getIpo().getCurve('RotY').addBezier((currentFrame, y))
 
505
            objectList[obIdx].getIpo().getCurve('RotZ').addBezier((currentFrame, z))
 
506
 
 
507
          obIdx += 1
 
508
          # Done importing motion data #
 
509
        
 
510
        # lines[lineIdx] = None # Scrap old motion data, save some memory?
 
511
        lineIdx += 1
 
512
      # We have finished now
 
513
      print currentFrame, 'done.'
 
514
     
 
515
      # No point in looking further, when this loop is done
 
516
      # There is nothine else left to do      
 
517
      print 'Imported ', currentFrame, ' frames'
 
518
      break
 
519
      
 
520
    # Main file loop
 
521
    lineIdx += 1
 
522
  print "bvh import time: ", Blender.sys.time() - time1
 
523
 
 
524
Blender.Window.FileSelector(loadBVH, "Import BVH")