Integrated Freestyle to rendering pipeline
[blender.git] / release / scripts / bvh_import.py
1 #!BPY
2
3 """
4 Name: 'Motion Capture (.bvh)...'
5 Blender: 242
6 Group: 'Import'
7 Tip: 'Import a (.bvh) motion capture file'
8 """
9
10 __author__ = "Campbell Barton"
11 __url__ = ("blender.org", "blenderartists.org")
12 __version__ = "1.90 06/08/01"
13
14 __bpydoc__ = """\
15 This script imports BVH motion capture data to Blender.
16 as empties or armatures.
17 """
18
19 # -------------------------------------------------------------------------- 
20 # BVH Import v2.0 by Campbell Barton (AKA Ideasman) 
21 # -------------------------------------------------------------------------- 
22 # ***** BEGIN GPL LICENSE BLOCK ***** 
23
24 # This program is free software; you can redistribute it and/or 
25 # modify it under the terms of the GNU General Public License 
26 # as published by the Free Software Foundation; either version 2 
27 # of the License, or (at your option) any later version. 
28
29 # This program is distributed in the hope that it will be useful, 
30 # but WITHOUT ANY WARRANTY; without even the implied warranty of 
31 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
32 # GNU General Public License for more details. 
33
34 # You should have received a copy of the GNU General Public License 
35 # along with this program; if not, write to the Free Software Foundation, 
36 # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
37
38 # ***** END GPL LICENCE BLOCK ***** 
39 # -------------------------------------------------------------------------- 
40
41 import Blender
42 import bpy
43 import BPyMessages
44 Vector= Blender.Mathutils.Vector
45 Euler= Blender.Mathutils.Euler
46 Matrix= Blender.Mathutils.Matrix
47 RotationMatrix = Blender.Mathutils.RotationMatrix
48 TranslationMatrix= Blender.Mathutils.TranslationMatrix
49
50 DEG2RAD = 0.017453292519943295
51
52 class bvh_node_class(object):
53         __slots__=(\
54         'name',# bvh joint name
55         'parent',# bvh_node_class type or None for no parent
56         'children',# a list of children of this type.
57         'rest_head_world',# worldspace rest location for the head of this node
58         'rest_head_local',# localspace rest location for the head of this node
59         'rest_tail_world',# # worldspace rest location for the tail of this node
60         'rest_tail_local',# # worldspace rest location for the tail of this node
61         'channels',# list of 6 ints, -1 for an unused channel, otherwise an index for the BVH motion data lines, lock triple then rot triple
62         'rot_order',# a triple of indicies as to the order rotation is applied. [0,1,2] is x/y/z - [None, None, None] if no rotation.
63         'anim_data',# a list one tuple's one for each frame. (locx, locy, locz, rotx, roty, rotz)
64         'has_loc',# Conveinience function, bool, same as (channels[0]!=-1 or channels[1]!=-1 channels[2]!=-1)
65         'has_rot',# Conveinience function, bool, same as (channels[3]!=-1 or channels[4]!=-1 channels[5]!=-1)
66         'temp')# use this for whatever you want
67         
68         def __init__(self, name, rest_head_world, rest_head_local, parent, channels, rot_order):
69                 self.name= name
70                 self.rest_head_world= rest_head_world
71                 self.rest_head_local= rest_head_local
72                 self.rest_tail_world= None
73                 self.rest_tail_local= None
74                 self.parent= parent
75                 self.channels= channels
76                 self.rot_order= rot_order
77                 
78                 # convenience functions
79                 self.has_loc= channels[0] != -1 or channels[1] != -1 or channels[2] != -1
80                 self.has_rot= channels[3] != -1 or channels[4] != -1 or channels[5] != -1
81                 
82                 
83                 self.children= []
84                 
85                 # list of 6 length tuples: (lx,ly,lz, rx,ry,rz)
86                 # even if the channels arnt used they will just be zero
87                 # 
88                 self.anim_data= [(0,0,0,0,0,0)] 
89                 
90         
91         def __repr__(self):
92                 return 'BVH name:"%s", rest_loc:(%.3f,%.3f,%.3f), rest_tail:(%.3f,%.3f,%.3f)' %\
93                 (self.name,\
94                 self.rest_head_world.x, self.rest_head_world.y, self.rest_head_world.z,\
95                 self.rest_head_world.x, self.rest_head_world.y, self.rest_head_world.z)
96         
97
98
99 # Change the order rotation is applied.
100 MATRIX_IDENTITY_3x3 = Matrix([1,0,0],[0,1,0],[0,0,1])
101 MATRIX_IDENTITY_4x4 = Matrix([1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1])
102
103 def eulerRotate(x,y,z, rot_order): 
104         # Clamp all values between 0 and 360, values outside this raise an error.
105         mats=[RotationMatrix(x%360,3,'x'), RotationMatrix(y%360,3,'y'), RotationMatrix(z%360,3,'z')]
106         # print rot_order
107         # Standard BVH multiplication order, apply the rotation in the order Z,X,Y
108         return (mats[rot_order[2]]*(mats[rot_order[1]]* (mats[rot_order[0]]* MATRIX_IDENTITY_3x3))).toEuler()
109
110 def read_bvh(file_path, GLOBAL_SCALE=1.0):
111         # File loading stuff
112         # Open the file for importing
113         file = open(file_path, 'rU')    
114         
115         # Seperate into a list of lists, each line a list of words.
116         file_lines = file.readlines()
117         # Non standard carrage returns?
118         if len(file_lines) == 1:
119                 file_lines = file_lines[0].split('\r')
120         
121         # Split by whitespace.
122         file_lines =[ll for ll in [ l.split() for l in file_lines] if ll]
123         
124         
125         # Create Hirachy as empties
126         
127         if file_lines[0][0].lower() == 'hierarchy':
128                 #print 'Importing the BVH Hierarchy for:', file_path
129                 pass
130         else:
131                 raise 'ERROR: This is not a BVH file'
132         
133         bvh_nodes= {None:None}
134         bvh_nodes_serial = [None]
135         
136         channelIndex = -1
137         
138
139         lineIdx = 0 # An index for the file.
140         while lineIdx < len(file_lines) -1:
141                 #...
142                 if file_lines[lineIdx][0].lower() == 'root' or file_lines[lineIdx][0].lower() == 'joint':
143                         
144                         # Join spaces into 1 word with underscores joining it.
145                         if len(file_lines[lineIdx]) > 2:
146                                 file_lines[lineIdx][1] = '_'.join(file_lines[lineIdx][1:])
147                                 file_lines[lineIdx] = file_lines[lineIdx][:2]
148                         
149                         # MAY NEED TO SUPPORT MULTIPLE ROOT's HERE!!!, Still unsure weather multiple roots are possible.??
150                         
151                         # Make sure the names are unique- Object names will match joint names exactly and both will be unique.
152                         name = file_lines[lineIdx][1]
153                         
154                         #print '%snode: %s, parent: %s' % (len(bvh_nodes_serial) * '  ', name,  bvh_nodes_serial[-1])
155                         
156                         lineIdx += 2 # Incriment to the next line (Offset)
157                         rest_head_local = Vector( GLOBAL_SCALE*float(file_lines[lineIdx][1]), GLOBAL_SCALE*float(file_lines[lineIdx][2]), GLOBAL_SCALE*float(file_lines[lineIdx][3]) )
158                         lineIdx += 1 # Incriment to the next line (Channels)
159                         
160                         # newChannel[Xposition, Yposition, Zposition, Xrotation, Yrotation, Zrotation]
161                         # newChannel references indecies to the motiondata,
162                         # if not assigned then -1 refers to the last value that will be added on loading at a value of zero, this is appended 
163                         # We'll add a zero value onto the end of the MotionDATA so this is always refers to a value.
164                         my_channel = [-1, -1, -1, -1, -1, -1] 
165                         my_rot_order= [None, None, None]
166                         rot_count= 0
167                         for channel in file_lines[lineIdx][2:]:
168                                 channel= channel.lower()
169                                 channelIndex += 1 # So the index points to the right channel
170                                 if   channel == 'xposition':    my_channel[0] = channelIndex
171                                 elif channel == 'yposition':    my_channel[1] = channelIndex
172                                 elif channel == 'zposition':    my_channel[2] = channelIndex
173                                 
174                                 elif channel == 'xrotation':
175                                         my_channel[3] = channelIndex
176                                         my_rot_order[rot_count]= 0
177                                         rot_count+=1
178                                 elif channel == 'yrotation':
179                                         my_channel[4] = channelIndex
180                                         my_rot_order[rot_count]= 1
181                                         rot_count+=1
182                                 elif channel == 'zrotation':
183                                         my_channel[5] = channelIndex
184                                         my_rot_order[rot_count]= 2
185                                         rot_count+=1
186                         
187                         channels = file_lines[lineIdx][2:]
188                         
189                         my_parent= bvh_nodes_serial[-1] # account for none
190                         
191                         
192                         # Apply the parents offset accumletivly
193                         if my_parent==None:
194                                 rest_head_world= Vector(rest_head_local)
195                         else:
196                                 rest_head_world= my_parent.rest_head_world + rest_head_local
197                         
198                         bvh_node= bvh_nodes[name]= bvh_node_class(name, rest_head_world, rest_head_local, my_parent, my_channel, my_rot_order)
199                         
200                         # If we have another child then we can call ourselves a parent, else 
201                         bvh_nodes_serial.append(bvh_node)
202
203                 # Account for an end node
204                 if file_lines[lineIdx][0].lower() == 'end' and file_lines[lineIdx][1].lower() == 'site': # There is somtimes a name after 'End Site' but we will ignore it.
205                         lineIdx += 2 # Incriment to the next line (Offset)
206                         rest_tail = Vector( GLOBAL_SCALE*float(file_lines[lineIdx][1]), GLOBAL_SCALE*float(file_lines[lineIdx][2]), GLOBAL_SCALE*float(file_lines[lineIdx][3]) )
207                         
208                         bvh_nodes_serial[-1].rest_tail_world= bvh_nodes_serial[-1].rest_head_world + rest_tail
209                         bvh_nodes_serial[-1].rest_tail_local= rest_tail
210                         
211                         
212                         # Just so we can remove the Parents in a uniform way- End end never has kids
213                         # so this is a placeholder
214                         bvh_nodes_serial.append(None)
215                 
216                 if len(file_lines[lineIdx]) == 1 and file_lines[lineIdx][0] == '}': # == ['}']
217                         bvh_nodes_serial.pop() # Remove the last item
218                 
219                 if len(file_lines[lineIdx]) == 1 and file_lines[lineIdx][0].lower() == 'motion':
220                         #print '\nImporting motion data'
221                         lineIdx += 3 # Set the cursor to the first frame
222                         break
223                         
224                 lineIdx += 1
225         
226         
227         # Remove the None value used for easy parent reference
228         del bvh_nodes[None]
229         # Dont use anymore
230         del bvh_nodes_serial
231         
232         bvh_nodes_list= bvh_nodes.values()
233         
234         while lineIdx < len(file_lines):
235                 line= file_lines[lineIdx]
236                 for bvh_node in bvh_nodes_list:
237                         #for bvh_node in bvh_nodes_serial:
238                         lx= ly= lz= rx= ry= rz= 0.0
239                         channels= bvh_node.channels
240                         anim_data= bvh_node.anim_data
241                         if channels[0] != -1:
242                                 lx= GLOBAL_SCALE * float(  line[channels[0]] )
243                                 
244                         if channels[1] != -1:
245                                 ly= GLOBAL_SCALE * float(  line[channels[1]] )
246                         
247                         if channels[2] != -1:
248                                 lz= GLOBAL_SCALE * float(  line[channels[2]] )
249                         
250                         if channels[3] != -1 or channels[4] != -1 or channels[5] != -1:                                         
251                                 rx, ry, rz = eulerRotate(float( line[channels[3]] ), float( line[channels[4]] ), float( line[channels[5]] ), bvh_node.rot_order)
252                                 #x,y,z = x/10.0, y/10.0, z/10.0 # For IPO's 36 is 360d
253                                 
254                                 # Make interpolation not cross between 180d, thjis fixes sub frame interpolation and time scaling.
255                                 # Will go from (355d to 365d) rather then to (355d to 5d) - inbetween these 2 there will now be a correct interpolation.
256                                 
257                                 while anim_data[-1][3] - rx >  180: rx+=360
258                                 while anim_data[-1][3] - rx < -180: rx-=360
259                                 
260                                 while anim_data[-1][4] - ry >  180: ry+=360
261                                 while anim_data[-1][4] - ry < -180: ry-=360
262                                 
263                                 while anim_data[-1][5] - rz >  180: rz+=360
264                                 while anim_data[-1][5] - rz < -180: rz-=360
265                                 
266                         # Done importing motion data #
267                         anim_data.append( (lx, ly, lz, rx, ry, rz) )
268                 lineIdx += 1
269         
270         # Assign children
271         for bvh_node in bvh_nodes.itervalues():         
272                 bvh_node_parent= bvh_node.parent
273                 if bvh_node_parent:
274                         bvh_node_parent.children.append(bvh_node)
275         
276         # Now set the tip of each bvh_node
277         for bvh_node in bvh_nodes.itervalues():
278                 
279                 if not bvh_node.rest_tail_world:
280                         if len(bvh_node.children)==0:
281                                 # could just fail here, but rare BVH files have childless nodes
282                                 bvh_node.rest_tail_world = Vector(bvh_node.rest_head_world)
283                                 bvh_node.rest_tail_local = Vector(bvh_node.rest_head_local)
284                         elif len(bvh_node.children)==1:
285                                 bvh_node.rest_tail_world= Vector(bvh_node.children[0].rest_head_world)
286                                 bvh_node.rest_tail_local= Vector(bvh_node.children[0].rest_head_local)
287                         else:
288                                 # allow this, see above
289                                 #if not bvh_node.children:
290                                 #       raise 'error, bvh node has no end and no children. bad file'
291                                         
292                                 # Removed temp for now
293                                 rest_tail_world= Vector(0,0,0)
294                                 rest_tail_local= Vector(0,0,0)
295                                 for bvh_node_child in bvh_node.children:
296                                         rest_tail_world += bvh_node_child.rest_head_world
297                                         rest_tail_local += bvh_node_child.rest_head_local
298                                 
299                                 bvh_node.rest_tail_world= rest_tail_world * (1.0/len(bvh_node.children))
300                                 bvh_node.rest_tail_local= rest_tail_local * (1.0/len(bvh_node.children))
301
302                 # Make sure tail isnt the same location as the head.
303                 if (bvh_node.rest_tail_local-bvh_node.rest_head_local).length <= 0.001*GLOBAL_SCALE:
304                         
305                         bvh_node.rest_tail_local.y= bvh_node.rest_tail_local.y + GLOBAL_SCALE/10
306                         bvh_node.rest_tail_world.y= bvh_node.rest_tail_world.y + GLOBAL_SCALE/10
307                         
308                 
309                 
310         return bvh_nodes
311
312
313
314 def bvh_node_dict2objects(bvh_nodes, IMPORT_START_FRAME= 1, IMPORT_LOOP= False):
315         
316         if IMPORT_START_FRAME<1:
317                 IMPORT_START_FRAME= 1
318                 
319         scn= bpy.data.scenes.active
320         scn.objects.selected = []
321         
322         objects= []
323         
324         def add_ob(name):
325                 ob = scn.objects.new('Empty')
326                 objects.append(ob)
327                 return ob
328         
329         # Add objects
330         for name, bvh_node in bvh_nodes.iteritems():
331                 bvh_node.temp= add_ob(name)
332         
333         # Parent the objects
334         for bvh_node in bvh_nodes.itervalues():
335                 bvh_node.temp.makeParent([ bvh_node_child.temp for bvh_node_child in bvh_node.children ], 1, 0) # ojbs, noninverse, 1 = not fast.
336         
337         # Offset
338         for bvh_node in bvh_nodes.itervalues():
339                 # Make relative to parents offset
340                 bvh_node.temp.loc= bvh_node.rest_head_local
341         
342         # Add tail objects
343         for name, bvh_node in bvh_nodes.iteritems():
344                 if not bvh_node.children:
345                         ob_end= add_ob(name + '_end')
346                         bvh_node.temp.makeParent([ob_end], 1, 0) # ojbs, noninverse, 1 = not fast.
347                         ob_end.loc= bvh_node.rest_tail_local
348         
349         
350         # Animate the data, the last used bvh_node will do since they all have the same number of frames
351         for current_frame in xrange(len(bvh_node.anim_data)):
352                 Blender.Set('curframe', current_frame+IMPORT_START_FRAME)
353                 
354                 for bvh_node in bvh_nodes.itervalues():
355                         lx,ly,lz,rx,ry,rz= bvh_node.anim_data[current_frame]
356                         
357                         rest_head_local= bvh_node.rest_head_local
358                         bvh_node.temp.loc= rest_head_local.x+lx, rest_head_local.y+ly, rest_head_local.z+lz
359                         
360                         bvh_node.temp.rot= rx*DEG2RAD,ry*DEG2RAD,rz*DEG2RAD
361                         
362                         bvh_node.temp.insertIpoKey(Blender.Object.IpoKeyTypes.LOCROT)
363         
364         scn.update(1)
365         return objects
366
367
368
369 def bvh_node_dict2armature(bvh_nodes, IMPORT_START_FRAME= 1, IMPORT_LOOP= False):
370         
371         if IMPORT_START_FRAME<1:
372                 IMPORT_START_FRAME= 1
373                 
374         
375         # Add the new armature, 
376         scn = bpy.data.scenes.active
377         scn.objects.selected = []
378         
379         arm_data= bpy.data.armatures.new()
380         arm_ob = scn.objects.new(arm_data)
381         scn.objects.context = [arm_ob]
382         scn.objects.active = arm_ob
383         
384         # Put us into editmode
385         arm_data.makeEditable()
386         
387         # Get the average bone length for zero length bones, we may not use this.
388         average_bone_length= 0.0
389         nonzero_count= 0
390         for bvh_node in bvh_nodes.itervalues():
391                 l= (bvh_node.rest_head_local-bvh_node.rest_tail_local).length
392                 if l:
393                         average_bone_length+= l
394                         nonzero_count+=1
395         
396         # Very rare cases all bones couldbe zero length???
397         if not average_bone_length:
398                 average_bone_length = 0.1
399         else:
400                 # Normal operation
401                 average_bone_length = average_bone_length/nonzero_count
402         
403         
404         
405         ZERO_AREA_BONES= []
406         for name, bvh_node in bvh_nodes.iteritems():
407                 # New editbone
408                 bone= bvh_node.temp= Blender.Armature.Editbone()
409                 
410                 bone.name= name
411                 arm_data.bones[name]= bone
412                 
413                 bone.head= bvh_node.rest_head_world
414                 bone.tail= bvh_node.rest_tail_world
415                 
416                 # ZERO AREA BONES.
417                 if (bone.head-bone.tail).length < 0.001:
418                         if bvh_node.parent:
419                                 ofs= bvh_node.parent.rest_head_local- bvh_node.parent.rest_tail_local
420                                 if ofs.length: # is our parent zero length also?? unlikely
421                                         bone.tail= bone.tail+ofs
422                                 else:
423                                         bone.tail.y= bone.tail.y+average_bone_length
424                         else:
425                                 bone.tail.y= bone.tail.y+average_bone_length
426                         
427                         ZERO_AREA_BONES.append(bone.name)
428                 
429         
430         for bvh_node in bvh_nodes.itervalues():
431                 if bvh_node.parent:
432                         # bvh_node.temp is the Editbone
433                         
434                         # Set the bone parent
435                         bvh_node.temp.parent= bvh_node.parent.temp
436                         
437                         # Set the connection state
438                         if not bvh_node.has_loc and\
439                         bvh_node.parent and\
440                         bvh_node.parent.temp.name not in ZERO_AREA_BONES and\
441                         bvh_node.parent.rest_tail_local == bvh_node.rest_head_local:
442                                 bvh_node.temp.options= [Blender.Armature.CONNECTED]
443         
444         # Replace the editbone with the editbone name,
445         # to avoid memory errors accessing the editbone outside editmode
446         for bvh_node in bvh_nodes.itervalues():
447                 bvh_node.temp= bvh_node.temp.name
448         
449         arm_data.update()
450         
451         # Now Apply the animation to the armature
452         
453         # Get armature animation data
454         pose= arm_ob.getPose()
455         pose_bones= pose.bones
456         
457         action = Blender.Armature.NLA.NewAction("Action") 
458         action.setActive(arm_ob)
459         #xformConstants= [ Blender.Object.Pose.LOC, Blender.Object.Pose.ROT ]
460         
461         # Replace the bvh_node.temp (currently an editbone)
462         # With a tuple  (pose_bone, armature_bone, bone_rest_matrix, bone_rest_matrix_inv)
463         for bvh_node in bvh_nodes.itervalues():
464                 bone_name= bvh_node.temp # may not be the same name as the bvh_node, could have been shortened.
465                 pose_bone= pose_bones[bone_name]
466                 rest_bone= arm_data.bones[bone_name]
467                 bone_rest_matrix = rest_bone.matrix['ARMATURESPACE'].rotationPart()
468                 
469                 bone_rest_matrix_inv= Matrix(bone_rest_matrix)
470                 bone_rest_matrix_inv.invert()
471                 
472                 bone_rest_matrix_inv.resize4x4()
473                 bone_rest_matrix.resize4x4()
474                 bvh_node.temp= (pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv)
475                 
476         
477         # Make a dict for fast access without rebuilding a list all the time.
478         xformConstants_dict={
479         (True,True):    [Blender.Object.Pose.LOC, Blender.Object.Pose.ROT],\
480         (False,True):   [Blender.Object.Pose.ROT],\
481         (True,False):   [Blender.Object.Pose.LOC],\
482         (False,False):  [],\
483         }
484         
485         
486         # KEYFRAME METHOD, SLOW, USE IPOS DIRECT
487         
488         # Animate the data, the last used bvh_node will do since they all have the same number of frames
489         for current_frame in xrange(len(bvh_node.anim_data)-1): # skip the first frame (rest frame)
490                 # print current_frame
491                 
492                 #if current_frame==40: # debugging
493                 #       break
494                 
495                 # Dont neet to set the current frame
496                 for bvh_node in bvh_nodes.itervalues():
497                         pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv= bvh_node.temp
498                         lx,ly,lz,rx,ry,rz= bvh_node.anim_data[current_frame+1]
499                         
500                         if bvh_node.has_rot:
501                                 # Set the rotation, not so simple                       
502                                 bone_rotation_matrix= Euler(rx,ry,rz).toMatrix()
503                                 bone_rotation_matrix.resize4x4()
504                                 pose_bone.quat= (bone_rest_matrix * bone_rotation_matrix * bone_rest_matrix_inv).toQuat()
505                         
506                         if bvh_node.has_loc:
507                                 # Set the Location, simple too
508                                 pose_bone.loc= (\
509                                 TranslationMatrix(Vector(lx, ly, lz) - bvh_node.rest_head_local ) *\
510                                 bone_rest_matrix_inv).translationPart() # WHY * 10? - just how pose works
511                         
512                         # Get the transform 
513                         xformConstants= xformConstants_dict[bvh_node.has_loc, bvh_node.has_rot]
514                         
515                         
516                         if xformConstants:
517                                 # Insert the keyframe from the loc/quat
518                                 pose_bone.insertKey(arm_ob, current_frame+IMPORT_START_FRAME, xformConstants, True )
519                 
520                 # First time, set the IPO's to linear
521                 if current_frame==0:
522                         for ipo in action.getAllChannelIpos().itervalues():
523                                 if ipo:
524                                         for cur in ipo:
525                                                 cur.interpolation = Blender.IpoCurve.InterpTypes.LINEAR
526                                                 if IMPORT_LOOP:
527                                                         cur.extend = Blender.IpoCurve.ExtendTypes.CYCLIC
528                                                         
529                                                 
530                 
531                 
532         # END KEYFRAME METHOD
533         
534         
535         """
536         # IPO KEYFRAME SETTING
537         # Add in the IPOs by adding keyframes, AFAIK theres no way to add IPOs to an action so I do this :/
538         for bvh_node in bvh_nodes.itervalues():
539                 pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv= bvh_node.temp
540                 
541                 # Get the transform 
542                 xformConstants= xformConstants_dict[bvh_node.has_loc, bvh_node.has_rot]
543                 if xformConstants:
544                         pose_bone.loc[:]= 0,0,0
545                         pose_bone.quat[:]= 0,0,1,0
546                         # Insert the keyframe from the loc/quat
547                         pose_bone.insertKey(arm_ob, IMPORT_START_FRAME, xformConstants)
548
549         
550         action_ipos= action.getAllChannelIpos()
551         
552         
553         for bvh_node in bvh_nodes.itervalues():
554                 has_loc= bvh_node.has_loc
555                 has_rot= bvh_node.has_rot
556                 
557                 if not has_rot and not has_loc:
558                         # No animation data
559                         continue
560                 
561                 ipo= action_ipos[bvh_node.temp[0].name] # posebones name as key
562                 
563                 if has_loc:
564                         curve_xloc= ipo[Blender.Ipo.PO_LOCX]
565                         curve_yloc= ipo[Blender.Ipo.PO_LOCY]
566                         curve_zloc= ipo[Blender.Ipo.PO_LOCZ]
567                         
568                         curve_xloc.interpolation= \
569                         curve_yloc.interpolation= \
570                         curve_zloc.interpolation= \
571                         Blender.IpoCurve.InterpTypes.LINEAR
572                         
573                 
574                 if has_rot:
575                         curve_wquat= ipo[Blender.Ipo.PO_QUATW]
576                         curve_xquat= ipo[Blender.Ipo.PO_QUATX]
577                         curve_yquat= ipo[Blender.Ipo.PO_QUATY]
578                         curve_zquat= ipo[Blender.Ipo.PO_QUATZ]
579                         
580                         curve_wquat.interpolation= \
581                         curve_xquat.interpolation= \
582                         curve_yquat.interpolation= \
583                         curve_zquat.interpolation= \
584                         Blender.IpoCurve.InterpTypes.LINEAR
585                 
586                 # Get the bone 
587                 pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv= bvh_node.temp
588                 
589                 
590                 def pose_rot(anim_data):
591                         bone_rotation_matrix= Euler(anim_data[3], anim_data[4], anim_data[5]).toMatrix()
592                         bone_rotation_matrix.resize4x4()
593                         return tuple((bone_rest_matrix * bone_rotation_matrix * bone_rest_matrix_inv).toQuat()) # qw,qx,qy,qz
594                 
595                 def pose_loc(anim_data):
596                         return tuple((TranslationMatrix(Vector(anim_data[0], anim_data[1], anim_data[2])) * bone_rest_matrix_inv).translationPart())
597                 
598                 
599                 last_frame= len(bvh_node.anim_data)+IMPORT_START_FRAME-1
600                 
601                 if has_loc:
602                         pose_locations= [pose_loc(anim_key) for anim_key in bvh_node.anim_data]
603                         
604                         # Add the start at the end, we know the start is just 0,0,0 anyway
605                         curve_xloc.append((last_frame, pose_locations[-1][0]))
606                         curve_yloc.append((last_frame, pose_locations[-1][1]))
607                         curve_zloc.append((last_frame, pose_locations[-1][2]))
608                         
609                         if len(pose_locations) > 1:
610                                 ox,oy,oz= pose_locations[0]
611                                 x,y,z= pose_locations[1]
612                                 
613                                 for i in xrange(1, len(pose_locations)-1): # from second frame to second last frame
614                                         
615                                         nx,ny,nz= pose_locations[i+1]
616                                         xset= yset= zset= True # we set all these by default
617                                         if abs((ox+nx)/2 - x) < 0.00001:        xset= False
618                                         if abs((oy+ny)/2 - y) < 0.00001:        yset= False
619                                         if abs((oz+nz)/2 - z) < 0.00001:        zset= False
620                                         
621                                         if xset: curve_xloc.append((i+IMPORT_START_FRAME, x))
622                                         if yset: curve_yloc.append((i+IMPORT_START_FRAME, y))
623                                         if zset: curve_zloc.append((i+IMPORT_START_FRAME, z))
624                                         
625                                         # Set the old and use the new
626                                         ox,oy,oz=       x,y,z
627                                         x,y,z=          nx,ny,nz
628                 
629                 
630                 if has_rot:
631                         pose_rotations= [pose_rot(anim_key) for anim_key in bvh_node.anim_data]
632                         
633                         # Add the start at the end, we know the start is just 0,0,0 anyway
634                         curve_wquat.append((last_frame, pose_rotations[-1][0]))
635                         curve_xquat.append((last_frame, pose_rotations[-1][1]))
636                         curve_yquat.append((last_frame, pose_rotations[-1][2]))
637                         curve_zquat.append((last_frame, pose_rotations[-1][3]))
638                         
639                         
640                         if len(pose_rotations) > 1:
641                                 ow,ox,oy,oz= pose_rotations[0]
642                                 w,x,y,z= pose_rotations[1]
643                                 
644                                 for i in xrange(1, len(pose_rotations)-1): # from second frame to second last frame
645                                         
646                                         nw, nx,ny,nz= pose_rotations[i+1]
647                                         wset= xset= yset= zset= True # we set all these by default
648                                         if abs((ow+nw)/2 - w) < 0.00001:        wset= False
649                                         if abs((ox+nx)/2 - x) < 0.00001:        xset= False
650                                         if abs((oy+ny)/2 - y) < 0.00001:        yset= False
651                                         if abs((oz+nz)/2 - z) < 0.00001:        zset= False
652                                         
653                                         if wset: curve_wquat.append((i+IMPORT_START_FRAME, w))
654                                         if xset: curve_xquat.append((i+IMPORT_START_FRAME, x))
655                                         if yset: curve_yquat.append((i+IMPORT_START_FRAME, y))
656                                         if zset: curve_zquat.append((i+IMPORT_START_FRAME, z))
657                                         
658                                         # Set the old and use the new
659                                         ow,ox,oy,oz=    w,x,y,z
660                                         w,x,y,z=                nw,nx,ny,nz
661
662         # IPO KEYFRAME SETTING
663         """
664         pose.update()
665         return arm_ob
666
667
668 #=============#
669 # TESTING     #
670 #=============#
671
672 #('/metavr/mocap/bvh/boxer.bvh')
673 #('/d/staggered_walk.bvh')
674 #('/metavr/mocap/bvh/dg-306-g.bvh') # Incompleate EOF
675 #('/metavr/mocap/bvh/wa8lk.bvh') # duplicate joint names, \r line endings.
676 #('/metavr/mocap/bvh/walk4.bvh') # 0 channels
677
678 '''
679 import os
680 DIR = '/metavr/mocap/bvh/'
681 for f in ('/d/staggered_walk.bvh',):
682         #for f in os.listdir(DIR)[5:6]:
683         #for f in os.listdir(DIR):
684         if f.endswith('.bvh'):
685                 s = Blender.Scene.New(f)
686                 s.makeCurrent()
687                 #file= DIR + f
688                 file= f
689                 print f
690                 bvh_nodes= read_bvh(file, 1.0)
691                 bvh_node_dict2armature(bvh_nodes, 1)
692 '''
693
694 def load_bvh_ui(file, PREF_UI= True):
695         
696         if BPyMessages.Error_NoFile(file):
697                 return
698         
699         Draw= Blender.Draw
700         
701         IMPORT_SCALE = Draw.Create(0.1)
702         IMPORT_START_FRAME = Draw.Create(1)
703         IMPORT_AS_ARMATURE = Draw.Create(1)
704         IMPORT_AS_EMPTIES = Draw.Create(0)
705         IMPORT_LOOP = Draw.Create(0)
706         
707         # Get USER Options
708         if PREF_UI:
709                 pup_block = [\
710                 ('As Armature', IMPORT_AS_ARMATURE, 'Imports the BVH as an armature'),\
711                 ('As Empties', IMPORT_AS_EMPTIES, 'Imports the BVH as empties'),\
712                 ('Scale: ', IMPORT_SCALE, 0.001, 100.0, 'Scale the BVH, Use 0.01 when 1.0 is 1 metre'),\
713                 ('Start Frame: ', IMPORT_START_FRAME, 1, 30000, 'Frame to start BVH motion'),\
714                 ('Loop Animation', IMPORT_LOOP, 'Enable cyclic IPOs'),\
715                 ]
716                 
717                 if not Draw.PupBlock('BVH Import...', pup_block):
718                         return
719         
720         print 'Attempting import BVH', file
721         
722         IMPORT_SCALE = IMPORT_SCALE.val
723         IMPORT_START_FRAME = IMPORT_START_FRAME.val
724         IMPORT_AS_ARMATURE = IMPORT_AS_ARMATURE.val
725         IMPORT_AS_EMPTIES = IMPORT_AS_EMPTIES.val
726         IMPORT_LOOP = IMPORT_LOOP.val
727         
728         if not IMPORT_AS_ARMATURE and not IMPORT_AS_EMPTIES:
729                 Blender.Draw.PupMenu('No import option selected')
730                 return
731         Blender.Window.WaitCursor(1)
732         # Get the BVH data and act on it.
733         t1= Blender.sys.time()
734         print '\tparsing bvh...',
735         bvh_nodes= read_bvh(file, IMPORT_SCALE)
736         print '%.4f' % (Blender.sys.time()-t1)
737         t1= Blender.sys.time()
738         print '\timporting to blender...',
739         if IMPORT_AS_ARMATURE:  bvh_node_dict2armature(bvh_nodes, IMPORT_START_FRAME, IMPORT_LOOP)
740         if IMPORT_AS_EMPTIES:   bvh_node_dict2objects(bvh_nodes,  IMPORT_START_FRAME, IMPORT_LOOP)
741         
742         print 'Done in %.4f\n' % (Blender.sys.time()-t1)
743         Blender.Window.WaitCursor(0)
744
745 def main():
746         Blender.Window.FileSelector(load_bvh_ui, 'Import BVH', '*.bvh')
747
748 if __name__ == '__main__':
749         #def foo():
750         main()
751         '''
752         scn = bpy.data.scenes.active
753         for ob in list(scn.objects):
754                 if ob.name!='arm__':
755                         scn.objects.unlink(ob)
756         load_bvh_ui('/test.bvh', False)
757         '''