Merge branch 'cb/fixs'
[plumiferos.git] / release / scripts / bvh_import.py
blob0fa714996d66c6cab5d4eaa8fc922faeae4d13da
1 #!BPY
3 """
4 Name: 'Motion Capture (.bvh)...'
5 Blender: 242
6 Group: 'Import'
7 Tip: 'Import a (.bvh) motion capture file'
8 """
10 __author__ = "Campbell Barton"
11 __url__ = ("blender.org", "blenderartists.org")
12 __version__ = "1.90 06/08/01"
14 __bpydoc__ = """\
15 This script imports BVH motion capture data to Blender.
16 as empties or armatures.
17 """
19 # --------------------------------------------------------------------------
20 # BVH Import v2.0 by Campbell Barton (AKA Ideasman)
21 # --------------------------------------------------------------------------
22 # ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
38 # ***** END GPL LICENCE BLOCK *****
39 # --------------------------------------------------------------------------
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
50 DEG2RAD = 0.017453292519943295
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
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
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
83 self.children= []
85 # list of 6 length tuples: (lx,ly,lz, rx,ry,rz)
86 # even if the channels arnt used they will just be zero
88 self.anim_data= [(0,0,0,0,0,0)]
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)
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])
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()
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')
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')
121 # Split by whitespace.
122 file_lines =[ll for ll in [ l.split() for l in file_lines] if ll]
125 # Create Hirachy as empties
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'
133 bvh_nodes= {None:None}
134 bvh_nodes_serial = [None]
136 channelIndex = -1
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':
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]
149 # MAY NEED TO SUPPORT MULTIPLE ROOT's HERE!!!, Still unsure weather multiple roots are possible.??
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]
154 #print '%snode: %s, parent: %s' % (len(bvh_nodes_serial) * ' ', name, bvh_nodes_serial[-1])
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)
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
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
187 channels = file_lines[lineIdx][2:]
189 my_parent= bvh_nodes_serial[-1] # account for none
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
198 bvh_node= bvh_nodes[name]= bvh_node_class(name, rest_head_world, rest_head_local, my_parent, my_channel, my_rot_order)
200 # If we have another child then we can call ourselves a parent, else
201 bvh_nodes_serial.append(bvh_node)
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]) )
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
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)
216 if len(file_lines[lineIdx]) == 1 and file_lines[lineIdx][0] == '}': # == ['}']
217 bvh_nodes_serial.pop() # Remove the last item
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
224 lineIdx += 1
227 # Remove the None value used for easy parent reference
228 del bvh_nodes[None]
229 # Dont use anymore
230 del bvh_nodes_serial
232 bvh_nodes_list= bvh_nodes.values()
234 while lineIdx < len(file_lines) -1:
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]] )
244 if channels[1] != -1:
245 ly= GLOBAL_SCALE * float( line[channels[1]] )
247 if channels[2] != -1:
248 lz= GLOBAL_SCALE * float( line[channels[2]] )
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
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.
257 while anim_data[-1][3] - rx > 180: rx+=360
258 while anim_data[-1][3] - rx < -180: rx-=360
260 while anim_data[-1][4] - ry > 180: ry+=360
261 while anim_data[-1][4] - ry < -180: ry-=360
263 while anim_data[-1][5] - rz > 180: rz+=360
264 while anim_data[-1][5] - rz < -180: rz-=360
266 # Done importing motion data #
267 anim_data.append( (lx, ly, lz, rx, ry, rz) )
268 lineIdx += 1
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)
276 # Now set the tip of each bvh_node
277 for bvh_node in bvh_nodes.itervalues():
279 if not bvh_node.rest_tail_world:
280 if len(bvh_node.children)==1:
281 bvh_node.rest_tail_world= Vector(bvh_node.children[0].rest_head_world)
282 bvh_node.rest_tail_local= Vector(bvh_node.children[0].rest_head_local)
283 else:
284 if not bvh_node.children:
285 raise 'error, bvh node has no end and no children. bad file'
287 # Removed temp for now
288 rest_tail_world= Vector(0,0,0)
289 rest_tail_local= Vector(0,0,0)
290 for bvh_node_child in bvh_node.children:
291 rest_tail_world += bvh_node_child.rest_head_world
292 rest_tail_local += bvh_node_child.rest_head_local
294 bvh_node.rest_tail_world= rest_tail_world * (1.0/len(bvh_node.children))
295 bvh_node.rest_tail_local= rest_tail_local * (1.0/len(bvh_node.children))
297 # Make sure tail isnt the same location as the head.
298 if (bvh_node.rest_tail_local-bvh_node.rest_head_local).length <= 0.001*GLOBAL_SCALE:
300 bvh_node.rest_tail_local.y= bvh_node.rest_tail_local.y + GLOBAL_SCALE/10
301 bvh_node.rest_tail_world.y= bvh_node.rest_tail_world.y + GLOBAL_SCALE/10
305 return bvh_nodes
309 def bvh_node_dict2objects(bvh_nodes, IMPORT_START_FRAME= 1, IMPORT_LOOP= False):
311 if IMPORT_START_FRAME<1:
312 IMPORT_START_FRAME= 1
314 scn= bpy.data.scenes.active
315 scn.objects.selected = []
317 objects= []
319 def add_ob(name):
320 ob = scn.objects.new('Empty')
321 objects.append(ob)
322 return ob
324 # Add objects
325 for name, bvh_node in bvh_nodes.iteritems():
326 bvh_node.temp= add_ob(name)
328 # Parent the objects
329 for bvh_node in bvh_nodes.itervalues():
330 bvh_node.temp.makeParent([ bvh_node_child.temp for bvh_node_child in bvh_node.children ], 1, 0) # ojbs, noninverse, 1 = not fast.
332 # Offset
333 for bvh_node in bvh_nodes.itervalues():
334 # Make relative to parents offset
335 bvh_node.temp.loc= bvh_node.rest_head_local
337 # Add tail objects
338 for name, bvh_node in bvh_nodes.iteritems():
339 if not bvh_node.children:
340 ob_end= add_ob(name + '_end')
341 bvh_node.temp.makeParent([ob_end], 1, 0) # ojbs, noninverse, 1 = not fast.
342 ob_end.loc= bvh_node.rest_tail_local
345 # Animate the data, the last used bvh_node will do since they all have the same number of frames
346 for current_frame in xrange(len(bvh_node.anim_data)):
347 Blender.Set('curframe', current_frame+IMPORT_START_FRAME)
349 for bvh_node in bvh_nodes.itervalues():
350 lx,ly,lz,rx,ry,rz= bvh_node.anim_data[current_frame]
352 rest_head_local= bvh_node.rest_head_local
353 bvh_node.temp.loc= rest_head_local.x+lx, rest_head_local.y+ly, rest_head_local.z+lz
355 bvh_node.temp.rot= rx*DEG2RAD,ry*DEG2RAD,rz*DEG2RAD
357 bvh_node.temp.insertIpoKey(Blender.Object.IpoKeys.LOCROT)
359 scn.update(1)
360 return objects
364 def bvh_node_dict2armature(bvh_nodes, IMPORT_START_FRAME= 1, IMPORT_LOOP= False):
366 if IMPORT_START_FRAME<1:
367 IMPORT_START_FRAME= 1
370 # Add the new armature,
371 scn = bpy.data.scenes.active
372 scn.objects.selected = []
374 arm_data= bpy.data.armatures.new()
375 arm_ob = scn.objects.new(arm_data)
376 scn.objects.context = [arm_ob]
377 scn.objects.active = arm_ob
379 # Put us into editmode
380 arm_data.makeEditable()
382 # Get the average bone length for zero length bones, we may not use this.
383 average_bone_length= 0.0
384 nonzero_count= 0
385 for bvh_node in bvh_nodes.itervalues():
386 l= (bvh_node.rest_head_local-bvh_node.rest_tail_local).length
387 if l:
388 average_bone_length+= l
389 nonzero_count+=1
391 # Very rare cases all bones couldbe zero length???
392 if not average_bone_length:
393 average_bone_length = 0.1
394 else:
395 # Normal operation
396 average_bone_length = average_bone_length/nonzero_count
400 ZERO_AREA_BONES= []
401 for name, bvh_node in bvh_nodes.iteritems():
402 # New editbone
403 bone= bvh_node.temp= Blender.Armature.Editbone()
405 bone.name= name
406 arm_data.bones[name]= bone
408 bone.head= bvh_node.rest_head_world
409 bone.tail= bvh_node.rest_tail_world
411 # ZERO AREA BONES.
412 if (bone.head-bone.tail).length < 0.001:
413 if bvh_node.parent:
414 ofs= bvh_node.parent.rest_head_local- bvh_node.parent.rest_tail_local
415 if ofs.length: # is our parent zero length also?? unlikely
416 bone.tail= bone.tail+ofs
417 else:
418 bone.tail.y= bone.tail.y+average_bone_length
419 else:
420 bone.tail.y= bone.tail.y+average_bone_length
422 ZERO_AREA_BONES.append(bone.name)
425 for bvh_node in bvh_nodes.itervalues():
426 if bvh_node.parent:
427 # bvh_node.temp is the Editbone
429 # Set the bone parent
430 bvh_node.temp.parent= bvh_node.parent.temp
432 # Set the connection state
433 if not bvh_node.has_loc and\
434 bvh_node.parent and\
435 bvh_node.parent.temp.name not in ZERO_AREA_BONES and\
436 bvh_node.parent.rest_tail_local == bvh_node.rest_head_local:
437 bvh_node.temp.options= [Blender.Armature.CONNECTED]
439 # Replace the editbone with the editbone name,
440 # to avoid memory errors accessing the editbone outside editmode
441 for bvh_node in bvh_nodes.itervalues():
442 bvh_node.temp= bvh_node.temp.name
444 arm_data.update()
446 # Now Apply the animation to the armature
448 # Get armature animation data
449 pose= arm_ob.getPose()
450 pose_bones= pose.bones
452 action = Blender.Armature.NLA.NewAction("Action")
453 action.setActive(arm_ob)
454 #xformConstants= [ Blender.Object.Pose.LOC, Blender.Object.Pose.ROT ]
456 # Replace the bvh_node.temp (currently an editbone)
457 # With a tuple (pose_bone, armature_bone, bone_rest_matrix, bone_rest_matrix_inv)
458 for bvh_node in bvh_nodes.itervalues():
459 bone_name= bvh_node.temp # may not be the same name as the bvh_node, could have been shortened.
460 pose_bone= pose_bones[bone_name]
461 rest_bone= arm_data.bones[bone_name]
462 bone_rest_matrix = rest_bone.matrix['ARMATURESPACE'].rotationPart()
464 bone_rest_matrix_inv= Matrix(bone_rest_matrix)
465 bone_rest_matrix_inv.invert()
467 bone_rest_matrix_inv.resize4x4()
468 bone_rest_matrix.resize4x4()
469 bvh_node.temp= (pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv)
472 # Make a dict for fast access without rebuilding a list all the time.
473 xformConstants_dict={
474 (True,True): [Blender.Object.Pose.LOC, Blender.Object.Pose.ROT],\
475 (False,True): [Blender.Object.Pose.ROT],\
476 (True,False): [Blender.Object.Pose.LOC],\
477 (False,False): [],\
481 # KEYFRAME METHOD, SLOW, USE IPOS DIRECT
483 # Animate the data, the last used bvh_node will do since they all have the same number of frames
484 for current_frame in xrange(len(bvh_node.anim_data)-1): # skip the first frame (rest frame)
485 # print current_frame
487 #if current_frame==40: # debugging
488 # break
490 # Dont neet to set the current frame
491 for bvh_node in bvh_nodes.itervalues():
492 pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv= bvh_node.temp
493 lx,ly,lz,rx,ry,rz= bvh_node.anim_data[current_frame+1]
495 if bvh_node.has_rot:
496 # Set the rotation, not so simple
497 bone_rotation_matrix= Euler(rx,ry,rz).toMatrix()
498 bone_rotation_matrix.resize4x4()
499 pose_bone.quat= (bone_rest_matrix * bone_rotation_matrix * bone_rest_matrix_inv).toQuat()
501 if bvh_node.has_loc:
502 # Set the Location, simple too
503 pose_bone.loc= (\
504 TranslationMatrix(Vector(lx, ly, lz) - bvh_node.rest_head_local ) *\
505 bone_rest_matrix_inv).translationPart() # WHY * 10? - just how pose works
507 # Get the transform
508 xformConstants= xformConstants_dict[bvh_node.has_loc, bvh_node.has_rot]
511 if xformConstants:
512 # Insert the keyframe from the loc/quat
513 pose_bone.insertKey(arm_ob, current_frame+IMPORT_START_FRAME, xformConstants, True )
515 # First time, set the IPO's to linear
516 if current_frame==0:
517 for ipo in action.getAllChannelIpos().itervalues():
518 if ipo:
519 for cur in ipo:
520 cur.interpolation = Blender.IpoCurve.InterpTypes.LINEAR
521 if IMPORT_LOOP:
522 cur.extend = Blender.IpoCurve.ExtendTypes.CYCLIC
527 # END KEYFRAME METHOD
531 # IPO KEYFRAME SETTING
532 # Add in the IPOs by adding keyframes, AFAIK theres no way to add IPOs to an action so I do this :/
533 for bvh_node in bvh_nodes.itervalues():
534 pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv= bvh_node.temp
536 # Get the transform
537 xformConstants= xformConstants_dict[bvh_node.has_loc, bvh_node.has_rot]
538 if xformConstants:
539 pose_bone.loc[:]= 0,0,0
540 pose_bone.quat[:]= 0,0,1,0
541 # Insert the keyframe from the loc/quat
542 pose_bone.insertKey(arm_ob, IMPORT_START_FRAME, xformConstants)
545 action_ipos= action.getAllChannelIpos()
548 for bvh_node in bvh_nodes.itervalues():
549 has_loc= bvh_node.has_loc
550 has_rot= bvh_node.has_rot
552 if not has_rot and not has_loc:
553 # No animation data
554 continue
556 ipo= action_ipos[bvh_node.temp[0].name] # posebones name as key
558 if has_loc:
559 curve_xloc= ipo[Blender.Ipo.PO_LOCX]
560 curve_yloc= ipo[Blender.Ipo.PO_LOCY]
561 curve_zloc= ipo[Blender.Ipo.PO_LOCZ]
563 curve_xloc.interpolation= \
564 curve_yloc.interpolation= \
565 curve_zloc.interpolation= \
566 Blender.IpoCurve.InterpTypes.LINEAR
569 if has_rot:
570 curve_wquat= ipo[Blender.Ipo.PO_QUATW]
571 curve_xquat= ipo[Blender.Ipo.PO_QUATX]
572 curve_yquat= ipo[Blender.Ipo.PO_QUATY]
573 curve_zquat= ipo[Blender.Ipo.PO_QUATZ]
575 curve_wquat.interpolation= \
576 curve_xquat.interpolation= \
577 curve_yquat.interpolation= \
578 curve_zquat.interpolation= \
579 Blender.IpoCurve.InterpTypes.LINEAR
581 # Get the bone
582 pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv= bvh_node.temp
585 def pose_rot(anim_data):
586 bone_rotation_matrix= Euler(anim_data[3], anim_data[4], anim_data[5]).toMatrix()
587 bone_rotation_matrix.resize4x4()
588 return tuple((bone_rest_matrix * bone_rotation_matrix * bone_rest_matrix_inv).toQuat()) # qw,qx,qy,qz
590 def pose_loc(anim_data):
591 return tuple((TranslationMatrix(Vector(anim_data[0], anim_data[1], anim_data[2])) * bone_rest_matrix_inv).translationPart())
594 last_frame= len(bvh_node.anim_data)+IMPORT_START_FRAME-1
596 if has_loc:
597 pose_locations= [pose_loc(anim_key) for anim_key in bvh_node.anim_data]
599 # Add the start at the end, we know the start is just 0,0,0 anyway
600 curve_xloc.append((last_frame, pose_locations[-1][0]))
601 curve_yloc.append((last_frame, pose_locations[-1][1]))
602 curve_zloc.append((last_frame, pose_locations[-1][2]))
604 if len(pose_locations) > 1:
605 ox,oy,oz= pose_locations[0]
606 x,y,z= pose_locations[1]
608 for i in xrange(1, len(pose_locations)-1): # from second frame to second last frame
610 nx,ny,nz= pose_locations[i+1]
611 xset= yset= zset= True # we set all these by default
612 if abs((ox+nx)/2 - x) < 0.00001: xset= False
613 if abs((oy+ny)/2 - y) < 0.00001: yset= False
614 if abs((oz+nz)/2 - z) < 0.00001: zset= False
616 if xset: curve_xloc.append((i+IMPORT_START_FRAME, x))
617 if yset: curve_yloc.append((i+IMPORT_START_FRAME, y))
618 if zset: curve_zloc.append((i+IMPORT_START_FRAME, z))
620 # Set the old and use the new
621 ox,oy,oz= x,y,z
622 x,y,z= nx,ny,nz
625 if has_rot:
626 pose_rotations= [pose_rot(anim_key) for anim_key in bvh_node.anim_data]
628 # Add the start at the end, we know the start is just 0,0,0 anyway
629 curve_wquat.append((last_frame, pose_rotations[-1][0]))
630 curve_xquat.append((last_frame, pose_rotations[-1][1]))
631 curve_yquat.append((last_frame, pose_rotations[-1][2]))
632 curve_zquat.append((last_frame, pose_rotations[-1][3]))
635 if len(pose_rotations) > 1:
636 ow,ox,oy,oz= pose_rotations[0]
637 w,x,y,z= pose_rotations[1]
639 for i in xrange(1, len(pose_rotations)-1): # from second frame to second last frame
641 nw, nx,ny,nz= pose_rotations[i+1]
642 wset= xset= yset= zset= True # we set all these by default
643 if abs((ow+nw)/2 - w) < 0.00001: wset= False
644 if abs((ox+nx)/2 - x) < 0.00001: xset= False
645 if abs((oy+ny)/2 - y) < 0.00001: yset= False
646 if abs((oz+nz)/2 - z) < 0.00001: zset= False
648 if wset: curve_wquat.append((i+IMPORT_START_FRAME, w))
649 if xset: curve_xquat.append((i+IMPORT_START_FRAME, x))
650 if yset: curve_yquat.append((i+IMPORT_START_FRAME, y))
651 if zset: curve_zquat.append((i+IMPORT_START_FRAME, z))
653 # Set the old and use the new
654 ow,ox,oy,oz= w,x,y,z
655 w,x,y,z= nw,nx,ny,nz
657 # IPO KEYFRAME SETTING
659 pose.update()
660 return arm_ob
663 #=============#
664 # TESTING #
665 #=============#
667 #('/metavr/mocap/bvh/boxer.bvh')
668 #('/d/staggered_walk.bvh')
669 #('/metavr/mocap/bvh/dg-306-g.bvh') # Incompleate EOF
670 #('/metavr/mocap/bvh/wa8lk.bvh') # duplicate joint names, \r line endings.
671 #('/metavr/mocap/bvh/walk4.bvh') # 0 channels
674 import os
675 DIR = '/metavr/mocap/bvh/'
676 for f in ('/d/staggered_walk.bvh',):
677 #for f in os.listdir(DIR)[5:6]:
678 #for f in os.listdir(DIR):
679 if f.endswith('.bvh'):
680 s = Blender.Scene.New(f)
681 s.makeCurrent()
682 #file= DIR + f
683 file= f
684 print f
685 bvh_nodes= read_bvh(file, 1.0)
686 bvh_node_dict2armature(bvh_nodes, 1)
689 def load_bvh_ui(file, PREF_UI= True):
691 if BPyMessages.Error_NoFile(file):
692 return
694 Draw= Blender.Draw
696 IMPORT_SCALE = Draw.Create(0.1)
697 IMPORT_START_FRAME = Draw.Create(1)
698 IMPORT_AS_ARMATURE = Draw.Create(1)
699 IMPORT_AS_EMPTIES = Draw.Create(0)
700 IMPORT_LOOP = Draw.Create(0)
702 # Get USER Options
703 if PREF_UI:
704 pup_block = [\
705 ('As Armature', IMPORT_AS_ARMATURE, 'Imports the BVH as an armature'),\
706 ('As Empties', IMPORT_AS_EMPTIES, 'Imports the BVH as empties'),\
707 ('Scale: ', IMPORT_SCALE, 0.001, 100.0, 'Scale the BVH, Use 0.01 when 1.0 is 1 metre'),\
708 ('Start Frame: ', IMPORT_START_FRAME, 1, 30000, 'Frame to start BVH motion'),\
709 ('Loop Animation', IMPORT_LOOP, 'Enable cyclic IPOs'),\
712 if not Draw.PupBlock('BVH Import...', pup_block):
713 return
715 print 'Attempting import BVH', file
717 IMPORT_SCALE = IMPORT_SCALE.val
718 IMPORT_START_FRAME = IMPORT_START_FRAME.val
719 IMPORT_AS_ARMATURE = IMPORT_AS_ARMATURE.val
720 IMPORT_AS_EMPTIES = IMPORT_AS_EMPTIES.val
721 IMPORT_LOOP = IMPORT_LOOP.val
723 if not IMPORT_AS_ARMATURE and not IMPORT_AS_EMPTIES:
724 Blender.Draw.PupMenu('No import option selected')
725 return
726 Blender.Window.WaitCursor(1)
727 # Get the BVH data and act on it.
728 t1= Blender.sys.time()
729 print '\tpassing bvh...',
730 bvh_nodes= read_bvh(file, IMPORT_SCALE)
731 print '%.4f' % (Blender.sys.time()-t1)
732 t1= Blender.sys.time()
733 print '\timporting to blender...',
734 if IMPORT_AS_ARMATURE: bvh_node_dict2armature(bvh_nodes, IMPORT_START_FRAME, IMPORT_LOOP)
735 if IMPORT_AS_EMPTIES: bvh_node_dict2objects(bvh_nodes, IMPORT_START_FRAME, IMPORT_LOOP)
737 print 'Done in %.4f\n' % (Blender.sys.time()-t1)
738 Blender.Window.WaitCursor(0)
740 def main():
741 Blender.Window.FileSelector(load_bvh_ui, 'Import BVH', '*.bvh')
743 if __name__ == '__main__':
744 #def foo():
745 main()
747 scn = bpy.data.scenes.active
748 for ob in list(scn.objects):
749 if ob.name!='arm__':
750 scn.objects.unlink(ob)
751 load_bvh_ui('/test.bvh', False)