7 Vector= Mathutils.Vector
9 Matrix= Mathutils.Matrix
10 RotationMatrix= Mathutils.RotationMatrix
11 TranslationMatrix= Mathutils.TranslationMatrix
16 DEG2RAD = 0.017453292519943295
18 class bvh_node_class(object):
20 'name',# bvh joint name
21 'parent',# bvh_node_class type or None for no parent
22 'children',# a list of children of this type.
23 'rest_head_world',# worldspace rest location for the head of this node
24 'rest_head_local',# localspace rest location for the head of this node
25 'rest_tail_world',# # worldspace rest location for the tail of this node
26 'rest_tail_local',# # worldspace rest location for the tail of this node
27 'channels',# list of 6 ints, -1 for an unused channel, otherwise an index for the BVH motion data lines, lock triple then rot triple
28 '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.
29 'anim_data',# a list one tuple's one for each frame. (locx, locy, locz, rotx, roty, rotz)
30 'has_loc',# Conveinience function, bool, same as (channels[0]!=-1 or channels[1]!=-1 channels[2]!=-1)
31 'has_rot',# Conveinience function, bool, same as (channels[3]!=-1 or channels[4]!=-1 channels[5]!=-1)
32 'temp')# use this for whatever you want
34 def __init__(self, name, rest_head_world, rest_head_local, parent, channels, rot_order):
36 self.rest_head_world= rest_head_world
37 self.rest_head_local= rest_head_local
38 self.rest_tail_world= None
39 self.rest_tail_local= None
41 self.channels= channels
42 self.rot_order= rot_order
44 # convenience functions
45 self.has_loc= channels[0] != -1 or channels[1] != -1 or channels[2] != -1
46 self.has_rot= channels[3] != -1 or channels[4] != -1 or channels[5] != -1
51 # list of 6 length tuples: (lx,ly,lz, rx,ry,rz)
52 # even if the channels arnt used they will just be zero
54 self.anim_data= [(0,0,0,0,0,0)]
58 return 'BVH name:"%s", rest_loc:(%.3f,%.3f,%.3f), rest_tail:(%.3f,%.3f,%.3f)' %\
60 self.rest_head_world.x, self.rest_head_world.y, self.rest_head_world.z,\
61 self.rest_head_world.x, self.rest_head_world.y, self.rest_head_world.z)
65 # Change the order rotation is applied.
66 MATRIX_IDENTITY_3x3 = Matrix([1,0,0],[0,1,0],[0,0,1])
67 MATRIX_IDENTITY_4x4 = Matrix([1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1])
69 def eulerRotate(x,y,z, rot_order):
71 # Clamp all values between 0 and 360, values outside this raise an error.
72 mats=[RotationMatrix(math.radians(x%360),3,'x'), RotationMatrix(math.radians(y%360),3,'y'), RotationMatrix(math.radians(z%360),3,'z')]
74 # Standard BVH multiplication order, apply the rotation in the order Z,X,Y
76 #XXX, order changes???
77 #eul = (mats[rot_order[2]]*(mats[rot_order[1]]* (mats[rot_order[0]]* MATRIX_IDENTITY_3x3))).toEuler()
78 eul = (MATRIX_IDENTITY_3x3*mats[rot_order[0]]*(mats[rot_order[1]]* (mats[rot_order[2]]))).toEuler()
80 eul = math.degrees(eul.x), math.degrees(eul.y), math.degrees(eul.z)
84 def read_bvh(context, file_path, GLOBAL_SCALE=1.0):
86 # Open the file for importing
87 file = open(file_path, 'rU')
89 # Seperate into a list of lists, each line a list of words.
90 file_lines = file.readlines()
91 # Non standard carrage returns?
92 if len(file_lines) == 1:
93 file_lines = file_lines[0].split('\r')
95 # Split by whitespace.
96 file_lines =[ll for ll in [ l.split() for l in file_lines] if ll]
99 # Create Hirachy as empties
101 if file_lines[0][0].lower() == 'hierarchy':
102 #print 'Importing the BVH Hierarchy for:', file_path
105 raise 'ERROR: This is not a BVH file'
107 bvh_nodes= {None:None}
108 bvh_nodes_serial = [None]
113 lineIdx = 0 # An index for the file.
114 while lineIdx < len(file_lines) -1:
116 if file_lines[lineIdx][0].lower() == 'root' or file_lines[lineIdx][0].lower() == 'joint':
118 # Join spaces into 1 word with underscores joining it.
119 if len(file_lines[lineIdx]) > 2:
120 file_lines[lineIdx][1] = '_'.join(file_lines[lineIdx][1:])
121 file_lines[lineIdx] = file_lines[lineIdx][:2]
123 # MAY NEED TO SUPPORT MULTIPLE ROOT's HERE!!!, Still unsure weather multiple roots are possible.??
125 # Make sure the names are unique- Object names will match joint names exactly and both will be unique.
126 name = file_lines[lineIdx][1]
128 #print '%snode: %s, parent: %s' % (len(bvh_nodes_serial) * ' ', name, bvh_nodes_serial[-1])
130 lineIdx += 2 # Incriment to the next line (Offset)
131 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]) )
132 lineIdx += 1 # Incriment to the next line (Channels)
134 # newChannel[Xposition, Yposition, Zposition, Xrotation, Yrotation, Zrotation]
135 # newChannel references indecies to the motiondata,
136 # if not assigned then -1 refers to the last value that will be added on loading at a value of zero, this is appended
137 # We'll add a zero value onto the end of the MotionDATA so this is always refers to a value.
138 my_channel = [-1, -1, -1, -1, -1, -1]
139 my_rot_order= [None, None, None]
141 for channel in file_lines[lineIdx][2:]:
142 channel= channel.lower()
143 channelIndex += 1 # So the index points to the right channel
144 if channel == 'xposition': my_channel[0] = channelIndex
145 elif channel == 'yposition': my_channel[1] = channelIndex
146 elif channel == 'zposition': my_channel[2] = channelIndex
148 elif channel == 'xrotation':
149 my_channel[3] = channelIndex
150 my_rot_order[rot_count]= 0
152 elif channel == 'yrotation':
153 my_channel[4] = channelIndex
154 my_rot_order[rot_count]= 1
156 elif channel == 'zrotation':
157 my_channel[5] = channelIndex
158 my_rot_order[rot_count]= 2
161 channels = file_lines[lineIdx][2:]
163 my_parent= bvh_nodes_serial[-1] # account for none
166 # Apply the parents offset accumletivly
168 rest_head_world= Vector(rest_head_local)
170 rest_head_world= my_parent.rest_head_world + rest_head_local
172 bvh_node= bvh_nodes[name]= bvh_node_class(name, rest_head_world, rest_head_local, my_parent, my_channel, my_rot_order)
174 # If we have another child then we can call ourselves a parent, else
175 bvh_nodes_serial.append(bvh_node)
177 # Account for an end node
178 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.
179 lineIdx += 2 # Incriment to the next line (Offset)
180 rest_tail = Vector( GLOBAL_SCALE*float(file_lines[lineIdx][1]), GLOBAL_SCALE*float(file_lines[lineIdx][2]), GLOBAL_SCALE*float(file_lines[lineIdx][3]) )
182 bvh_nodes_serial[-1].rest_tail_world= bvh_nodes_serial[-1].rest_head_world + rest_tail
183 bvh_nodes_serial[-1].rest_tail_local= rest_tail
186 # Just so we can remove the Parents in a uniform way- End end never has kids
187 # so this is a placeholder
188 bvh_nodes_serial.append(None)
190 if len(file_lines[lineIdx]) == 1 and file_lines[lineIdx][0] == '}': # == ['}']
191 bvh_nodes_serial.pop() # Remove the last item
193 if len(file_lines[lineIdx]) == 1 and file_lines[lineIdx][0].lower() == 'motion':
194 #print '\nImporting motion data'
195 lineIdx += 3 # Set the cursor to the first frame
201 # Remove the None value used for easy parent reference
206 bvh_nodes_list= bvh_nodes.values()
208 while lineIdx < len(file_lines):
209 line= file_lines[lineIdx]
210 for bvh_node in bvh_nodes_list:
211 #for bvh_node in bvh_nodes_serial:
212 lx= ly= lz= rx= ry= rz= 0.0
213 channels= bvh_node.channels
214 anim_data= bvh_node.anim_data
215 if channels[0] != -1:
216 lx= GLOBAL_SCALE * float( line[channels[0]] )
218 if channels[1] != -1:
219 ly= GLOBAL_SCALE * float( line[channels[1]] )
221 if channels[2] != -1:
222 lz= GLOBAL_SCALE * float( line[channels[2]] )
224 if channels[3] != -1 or channels[4] != -1 or channels[5] != -1:
225 rx, ry, rz = float( line[channels[3]] ), float( line[channels[4]] ), float( line[channels[5]] )
227 if ROT_STYLE != 'NATIVE':
228 rx, ry, rz = eulerRotate(rx, ry, rz, bvh_node.rot_order)
230 #x,y,z = x/10.0, y/10.0, z/10.0 # For IPO's 36 is 360d
232 # Make interpolation not cross between 180d, thjis fixes sub frame interpolation and time scaling.
233 # Will go from (355d to 365d) rather then to (355d to 5d) - inbetween these 2 there will now be a correct interpolation.
235 while anim_data[-1][3] - rx > 180: rx+=360
236 while anim_data[-1][3] - rx < -180: rx-=360
238 while anim_data[-1][4] - ry > 180: ry+=360
239 while anim_data[-1][4] - ry < -180: ry-=360
241 while anim_data[-1][5] - rz > 180: rz+=360
242 while anim_data[-1][5] - rz < -180: rz-=360
244 # Done importing motion data #
245 anim_data.append( (lx, ly, lz, rx, ry, rz) )
249 for bvh_node in bvh_nodes.values():
250 bvh_node_parent= bvh_node.parent
252 bvh_node_parent.children.append(bvh_node)
254 # Now set the tip of each bvh_node
255 for bvh_node in bvh_nodes.values():
257 if not bvh_node.rest_tail_world:
258 if len(bvh_node.children)==0:
259 # could just fail here, but rare BVH files have childless nodes
260 bvh_node.rest_tail_world = Vector(bvh_node.rest_head_world)
261 bvh_node.rest_tail_local = Vector(bvh_node.rest_head_local)
262 elif len(bvh_node.children)==1:
263 bvh_node.rest_tail_world= Vector(bvh_node.children[0].rest_head_world)
264 bvh_node.rest_tail_local= Vector(bvh_node.children[0].rest_head_local)
266 # allow this, see above
267 #if not bvh_node.children:
268 # raise 'error, bvh node has no end and no children. bad file'
270 # Removed temp for now
271 rest_tail_world= Vector(0,0,0)
272 rest_tail_local= Vector(0,0,0)
273 for bvh_node_child in bvh_node.children:
274 rest_tail_world += bvh_node_child.rest_head_world
275 rest_tail_local += bvh_node_child.rest_head_local
277 bvh_node.rest_tail_world= rest_tail_world * (1.0/len(bvh_node.children))
278 bvh_node.rest_tail_local= rest_tail_local * (1.0/len(bvh_node.children))
280 # Make sure tail isnt the same location as the head.
281 if (bvh_node.rest_tail_local-bvh_node.rest_head_local).length <= 0.001*GLOBAL_SCALE:
283 bvh_node.rest_tail_local.y= bvh_node.rest_tail_local.y + GLOBAL_SCALE/10
284 bvh_node.rest_tail_world.y= bvh_node.rest_tail_world.y + GLOBAL_SCALE/10
292 def bvh_node_dict2objects(context, bvh_nodes, IMPORT_START_FRAME= 1, IMPORT_LOOP= False):
294 if IMPORT_START_FRAME<1:
295 IMPORT_START_FRAME= 1
298 scn.objects.selected = []
303 ob = scn.objects.new('Empty')
308 for name, bvh_node in bvh_nodes.items():
309 bvh_node.temp= add_ob(name)
312 for bvh_node in bvh_nodes.values():
313 bvh_node.temp.makeParent([ bvh_node_child.temp for bvh_node_child in bvh_node.children ], 1, 0) # ojbs, noninverse, 1 = not fast.
316 for bvh_node in bvh_nodes.values():
317 # Make relative to parents offset
318 bvh_node.temp.loc= bvh_node.rest_head_local
321 for name, bvh_node in bvh_nodes.items():
322 if not bvh_node.children:
323 ob_end= add_ob(name + '_end')
324 bvh_node.temp.makeParent([ob_end], 1, 0) # ojbs, noninverse, 1 = not fast.
325 ob_end.loc= bvh_node.rest_tail_local
328 # Animate the data, the last used bvh_node will do since they all have the same number of frames
329 for current_frame in range(len(bvh_node.anim_data)):
330 Blender.Set('curframe', current_frame+IMPORT_START_FRAME)
332 for bvh_node in bvh_nodes.values():
333 lx,ly,lz,rx,ry,rz= bvh_node.anim_data[current_frame]
335 rest_head_local= bvh_node.rest_head_local
336 bvh_node.temp.loc= rest_head_local.x+lx, rest_head_local.y+ly, rest_head_local.z+lz
338 bvh_node.temp.rot= rx*DEG2RAD,ry*DEG2RAD,rz*DEG2RAD
340 bvh_node.temp.insertIpoKey(Blender.Object.IpoKeyTypes.LOCROT)
347 def bvh_node_dict2armature(context, bvh_nodes, IMPORT_START_FRAME= 1, IMPORT_LOOP= False):
349 if IMPORT_START_FRAME<1:
350 IMPORT_START_FRAME= 1
353 # Add the new armature,
355 #XXX scn.objects.selected = []
356 for ob in scn.objects:
360 #XXX arm_data= bpy.data.armatures.new()
361 #XXX arm_ob = scn.objects.new(arm_data)
362 bpy.ops.object.armature_add()
363 arm_ob= scn.objects[-1]
364 arm_data= arm_ob.data
369 #XXX scn.objects.context = [arm_ob]
370 #XXX scn.objects.active = arm_ob
371 arm_ob.selected= True
372 scn.objects.active= arm_ob
373 print(scn.objects.active)
376 # Put us into editmode
377 #XXX arm_data.makeEditable()
379 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
380 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
384 # Get the average bone length for zero length bones, we may not use this.
385 average_bone_length= 0.0
387 for bvh_node in bvh_nodes.values():
388 l= (bvh_node.rest_head_local-bvh_node.rest_tail_local).length
390 average_bone_length+= l
393 # Very rare cases all bones couldbe zero length???
394 if not average_bone_length:
395 average_bone_length = 0.1
398 average_bone_length = average_bone_length/nonzero_count
401 #XXX - sloppy operator code
403 bpy.ops.armature.delete()
404 bpy.ops.armature.select_all_toggle()
405 bpy.ops.armature.delete()
408 for name, bvh_node in bvh_nodes.items():
410 bpy.ops.armature.bone_primitive_add(name="Bone")
412 #XXX bone= bvh_node.temp= Blender.Armature.Editbone()
413 bone= bvh_node.temp= arm_data.edit_bones[-1]
416 # arm_data.bones[name]= bone
418 bone.head= bvh_node.rest_head_world
419 bone.tail= bvh_node.rest_tail_world
422 if (bone.head-bone.tail).length < 0.001:
424 ofs= bvh_node.parent.rest_head_local- bvh_node.parent.rest_tail_local
425 if ofs.length: # is our parent zero length also?? unlikely
426 bone.tail= bone.tail+ofs
428 bone.tail.y= bone.tail.y+average_bone_length
430 bone.tail.y= bone.tail.y+average_bone_length
432 ZERO_AREA_BONES.append(bone.name)
435 for bvh_node in bvh_nodes.values():
437 # bvh_node.temp is the Editbone
439 # Set the bone parent
440 bvh_node.temp.parent= bvh_node.parent.temp
442 # Set the connection state
443 if not bvh_node.has_loc and\
445 bvh_node.parent.temp.name not in ZERO_AREA_BONES and\
446 bvh_node.parent.rest_tail_local == bvh_node.rest_head_local:
447 bvh_node.temp.connected= True
449 # Replace the editbone with the editbone name,
450 # to avoid memory errors accessing the editbone outside editmode
451 for bvh_node in bvh_nodes.values():
452 bvh_node.temp= bvh_node.temp.name
454 #XXX arm_data.update()
456 # Now Apply the animation to the armature
458 # Get armature animation data
459 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
460 bpy.ops.object.mode_set(mode='POSE', toggle=False)
463 pose_bones= pose.bones
466 if ROT_STYLE=='NATIVE':
467 eul_order_lookup = {\
476 for bvh_node in bvh_nodes.values():
477 bone_name= bvh_node.temp # may not be the same name as the bvh_node, could have been shortened.
478 pose_bone= pose_bones[bone_name]
479 pose_bone.rotation_mode = eul_order_lookup[tuple(bvh_node.rot_order)]
481 elif ROT_STYLE=='XYZ':
482 for pose_bone in pose_bones:
483 pose_bone.rotation_mode = 'XYZ'
489 bpy.ops.pose.select_all_toggle() # set
490 bpy.ops.anim.insert_keyframe_menu(type=-4) # XXX - -4 ???
496 #for p in pose_bones:
500 #XXX action = Blender.Armature.NLA.NewAction("Action")
501 #XXX action.setActive(arm_ob)
504 #action = bpy.data.actions[-1]
506 # arm_ob.animation_data.action = action
507 action = arm_ob.animation_data.action
512 #xformConstants= [ Blender.Object.Pose.LOC, Blender.Object.Pose.ROT ]
514 # Replace the bvh_node.temp (currently an editbone)
515 # With a tuple (pose_bone, armature_bone, bone_rest_matrix, bone_rest_matrix_inv)
516 for bvh_node in bvh_nodes.values():
517 bone_name= bvh_node.temp # may not be the same name as the bvh_node, could have been shortened.
518 pose_bone= pose_bones[bone_name]
519 rest_bone= arm_data.bones[bone_name]
520 #XXX bone_rest_matrix = rest_bone.matrix['ARMATURESPACE'].rotationPart()
521 bone_rest_matrix = rest_bone.matrix.rotationPart()
524 bone_rest_matrix_inv= Matrix(bone_rest_matrix)
525 bone_rest_matrix_inv.invert()
527 bone_rest_matrix_inv.resize4x4()
528 bone_rest_matrix.resize4x4()
529 bvh_node.temp= (pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv)
532 # Make a dict for fast access without rebuilding a list all the time.
534 xformConstants_dict={
535 (True,True): [Blender.Object.Pose.LOC, Blender.Object.Pose.ROT],\
536 (False,True): [Blender.Object.Pose.ROT],\
537 (True,False): [Blender.Object.Pose.LOC],\
542 # KEYFRAME METHOD, SLOW, USE IPOS DIRECT
544 # Animate the data, the last used bvh_node will do since they all have the same number of frames
545 for current_frame in range(len(bvh_node.anim_data)-1): # skip the first frame (rest frame)
546 # print current_frame
548 #if current_frame==150: # debugging
551 # Dont neet to set the current frame
552 for bvh_node in bvh_nodes.values():
553 pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv= bvh_node.temp
554 lx,ly,lz,rx,ry,rz= bvh_node.anim_data[current_frame+1]
558 if ROT_STYLE=='QUAT':
559 # Set the rotation, not so simple
560 bone_rotation_matrix= Euler(math.radians(rx), math.radians(ry), math.radians(rz)).toMatrix()
562 bone_rotation_matrix.resize4x4()
564 #pose_bone.rotation_quaternion= (bone_rest_matrix * bone_rotation_matrix * bone_rest_matrix_inv).toQuat() # ORIGINAL
565 # pose_bone.rotation_quaternion= (bone_rest_matrix_inv * bone_rotation_matrix * bone_rest_matrix).toQuat()
566 # pose_bone.rotation_quaternion= (bone_rotation_matrix * bone_rest_matrix).toQuat() # BAD
567 # pose_bone.rotation_quaternion= bone_rotation_matrix.toQuat() # NOT GOOD
568 # pose_bone.rotation_quaternion= bone_rotation_matrix.toQuat() # NOT GOOD
570 #pose_bone.rotation_quaternion= (bone_rotation_matrix * bone_rest_matrix_inv * bone_rest_matrix).toQuat()
571 #pose_bone.rotation_quaternion= (bone_rest_matrix_inv * bone_rest_matrix * bone_rotation_matrix).toQuat()
572 #pose_bone.rotation_quaternion= (bone_rest_matrix * bone_rotation_matrix * bone_rest_matrix_inv).toQuat()
574 #pose_bone.rotation_quaternion= ( bone_rest_matrix* bone_rest_matrix_inv * bone_rotation_matrix).toQuat()
575 #pose_bone.rotation_quaternion= (bone_rotation_matrix * bone_rest_matrix * bone_rest_matrix_inv).toQuat()
576 #pose_bone.rotation_quaternion= (bone_rest_matrix_inv * bone_rotation_matrix * bone_rest_matrix ).toQuat()
578 pose_bone.rotation_quaternion= (bone_rest_matrix_inv * bone_rotation_matrix * bone_rest_matrix).toQuat()
581 bone_rotation_matrix= Euler(math.radians(rx), math.radians(ry), math.radians(rz)).toMatrix()
582 bone_rotation_matrix.resize4x4()
584 eul= (bone_rest_matrix * bone_rotation_matrix * bone_rest_matrix_inv).toEuler()
586 #pose_bone.rotation_euler = math.radians(rx), math.radians(ry), math.radians(rz)
587 pose_bone.rotation_euler = eul
589 print("ROTATION" + str(Euler(math.radians(rx), math.radians(ry), math.radians(rz))))
592 # Set the Location, simple too
595 # pose_bone.location= (TranslationMatrix(Vector(lx, ly, lz) - bvh_node.rest_head_local ) * bone_rest_matrix_inv).translationPart() # WHY * 10? - just how pose works
596 # pose_bone.location= (bone_rest_matrix_inv * TranslationMatrix(Vector(lx, ly, lz) - bvh_node.rest_head_local )).translationPart()
597 # pose_bone.location= lx, ly, lz
598 pose_bone.location= Vector(lx, ly, lz) - bvh_node.rest_head_local
601 #XXX # TODO- add in 2.5
604 xformConstants= xformConstants_dict[bvh_node.has_loc, bvh_node.has_rot]
607 # Insert the keyframe from the loc/quat
608 pose_bone.insertKey(arm_ob, current_frame+IMPORT_START_FRAME, xformConstants, True )
612 pose_bone.keyframe_insert("location")
614 if ROT_STYLE=='QUAT':
615 pose_bone.keyframe_insert("rotation_quaternion")
617 pose_bone.keyframe_insert("rotation_euler")
621 # bpy.ops.anim.insert_keyframe_menu(type=-4) # XXX - -4 ???
622 bpy.ops.screen.frame_offset(delta=1)
624 # First time, set the IPO's to linear
628 for ipo in action.getAllChannelIpos().values():
631 cur.interpolation = Blender.IpoCurve.InterpTypes.LINEAR
633 cur.extend = Blender.IpoCurve.ExtendTypes.CYCLIC
637 for cu in action.fcurves:
638 for bez in cu.keyframe_points:
639 bez.interpolation = 'CONSTANT'
641 # END KEYFRAME METHOD
645 # IPO KEYFRAME SETTING
646 # Add in the IPOs by adding keyframes, AFAIK theres no way to add IPOs to an action so I do this :/
647 for bvh_node in bvh_nodes.values():
648 pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv= bvh_node.temp
651 xformConstants= xformConstants_dict[bvh_node.has_loc, bvh_node.has_rot]
653 pose_bone.loc[:]= 0,0,0
654 pose_bone.quat[:]= 0,0,1,0
655 # Insert the keyframe from the loc/quat
656 pose_bone.insertKey(arm_ob, IMPORT_START_FRAME, xformConstants)
659 action_ipos= action.getAllChannelIpos()
662 for bvh_node in bvh_nodes.values():
663 has_loc= bvh_node.has_loc
664 has_rot= bvh_node.has_rot
666 if not has_rot and not has_loc:
670 ipo= action_ipos[bvh_node.temp[0].name] # posebones name as key
673 curve_xloc= ipo[Blender.Ipo.PO_LOCX]
674 curve_yloc= ipo[Blender.Ipo.PO_LOCY]
675 curve_zloc= ipo[Blender.Ipo.PO_LOCZ]
677 curve_xloc.interpolation= \
678 curve_yloc.interpolation= \
679 curve_zloc.interpolation= \
680 Blender.IpoCurve.InterpTypes.LINEAR
684 curve_wquat= ipo[Blender.Ipo.PO_QUATW]
685 curve_xquat= ipo[Blender.Ipo.PO_QUATX]
686 curve_yquat= ipo[Blender.Ipo.PO_QUATY]
687 curve_zquat= ipo[Blender.Ipo.PO_QUATZ]
689 curve_wquat.interpolation= \
690 curve_xquat.interpolation= \
691 curve_yquat.interpolation= \
692 curve_zquat.interpolation= \
693 Blender.IpoCurve.InterpTypes.LINEAR
696 pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv= bvh_node.temp
699 def pose_rot(anim_data):
700 bone_rotation_matrix= Euler(anim_data[3], anim_data[4], anim_data[5]).toMatrix()
701 bone_rotation_matrix.resize4x4()
702 return tuple((bone_rest_matrix * bone_rotation_matrix * bone_rest_matrix_inv).toQuat()) # qw,qx,qy,qz
704 def pose_loc(anim_data):
705 return tuple((TranslationMatrix(Vector(anim_data[0], anim_data[1], anim_data[2])) * bone_rest_matrix_inv).translationPart())
708 last_frame= len(bvh_node.anim_data)+IMPORT_START_FRAME-1
711 pose_locations= [pose_loc(anim_key) for anim_key in bvh_node.anim_data]
713 # Add the start at the end, we know the start is just 0,0,0 anyway
714 curve_xloc.append((last_frame, pose_locations[-1][0]))
715 curve_yloc.append((last_frame, pose_locations[-1][1]))
716 curve_zloc.append((last_frame, pose_locations[-1][2]))
718 if len(pose_locations) > 1:
719 ox,oy,oz= pose_locations[0]
720 x,y,z= pose_locations[1]
722 for i in range(1, len(pose_locations)-1): # from second frame to second last frame
724 nx,ny,nz= pose_locations[i+1]
725 xset= yset= zset= True # we set all these by default
726 if abs((ox+nx)/2 - x) < 0.00001: xset= False
727 if abs((oy+ny)/2 - y) < 0.00001: yset= False
728 if abs((oz+nz)/2 - z) < 0.00001: zset= False
730 if xset: curve_xloc.append((i+IMPORT_START_FRAME, x))
731 if yset: curve_yloc.append((i+IMPORT_START_FRAME, y))
732 if zset: curve_zloc.append((i+IMPORT_START_FRAME, z))
734 # Set the old and use the new
740 pose_rotations= [pose_rot(anim_key) for anim_key in bvh_node.anim_data]
742 # Add the start at the end, we know the start is just 0,0,0 anyway
743 curve_wquat.append((last_frame, pose_rotations[-1][0]))
744 curve_xquat.append((last_frame, pose_rotations[-1][1]))
745 curve_yquat.append((last_frame, pose_rotations[-1][2]))
746 curve_zquat.append((last_frame, pose_rotations[-1][3]))
749 if len(pose_rotations) > 1:
750 ow,ox,oy,oz= pose_rotations[0]
751 w,x,y,z= pose_rotations[1]
753 for i in range(1, len(pose_rotations)-1): # from second frame to second last frame
755 nw, nx,ny,nz= pose_rotations[i+1]
756 wset= xset= yset= zset= True # we set all these by default
757 if abs((ow+nw)/2 - w) < 0.00001: wset= False
758 if abs((ox+nx)/2 - x) < 0.00001: xset= False
759 if abs((oy+ny)/2 - y) < 0.00001: yset= False
760 if abs((oz+nz)/2 - z) < 0.00001: zset= False
762 if wset: curve_wquat.append((i+IMPORT_START_FRAME, w))
763 if xset: curve_xquat.append((i+IMPORT_START_FRAME, x))
764 if yset: curve_yquat.append((i+IMPORT_START_FRAME, y))
765 if zset: curve_zquat.append((i+IMPORT_START_FRAME, z))
767 # Set the old and use the new
771 # IPO KEYFRAME SETTING
774 # XXX NOT NEEDED NOW?
783 #('/metavr/mocap/bvh/boxer.bvh')
784 #('/d/staggered_walk.bvh')
785 #('/metavr/mocap/bvh/dg-306-g.bvh') # Incompleate EOF
786 #('/metavr/mocap/bvh/wa8lk.bvh') # duplicate joint names, \r line endings.
787 #('/metavr/mocap/bvh/walk4.bvh') # 0 channels
791 DIR = '/metavr/mocap/bvh/'
792 for f in ('/d/staggered_walk.bvh',):
793 #for f in os.listdir(DIR)[5:6]:
794 #for f in os.listdir(DIR):
795 if f.endswith('.bvh'):
796 s = Blender.Scene.New(f)
801 bvh_nodes= read_bvh(file, 1.0)
802 bvh_node_dict2armature(bvh_nodes, 1)
805 def load_bvh_ui(context, file, PREF_UI= False):
807 #XXX if BPyMessages.Error_NoFile(file):
810 #XXX Draw= Blender.Draw
813 IMPORT_START_FRAME = 1
814 IMPORT_AS_ARMATURE = 1
815 IMPORT_AS_EMPTIES = 0
821 ('As Armature', IMPORT_AS_ARMATURE, 'Imports the BVH as an armature'),\
822 ('As Empties', IMPORT_AS_EMPTIES, 'Imports the BVH as empties'),\
823 ('Scale: ', IMPORT_SCALE, 0.001, 100.0, 'Scale the BVH, Use 0.01 when 1.0 is 1 metre'),\
824 ('Start Frame: ', IMPORT_START_FRAME, 1, 30000, 'Frame to start BVH motion'),\
825 ('Loop Animation', IMPORT_LOOP, 'Enable cyclic IPOs'),\
828 #XXX if not Draw.PupBlock('BVH Import...', pup_block):
831 # print('Attempting import BVH', file)
833 if not IMPORT_AS_ARMATURE and not IMPORT_AS_EMPTIES:
834 raise('No import option selected')
836 #XXX Blender.Window.WaitCursor(1)
837 # Get the BVH data and act on it.
840 print('\tparsing bvh...', end= "")
841 bvh_nodes= read_bvh(context, file, IMPORT_SCALE)
842 print('%.4f' % (time.time()-t1))
844 print('\timporting to blender...', end="")
845 if IMPORT_AS_ARMATURE: bvh_node_dict2armature(context, bvh_nodes, IMPORT_START_FRAME, IMPORT_LOOP)
846 if IMPORT_AS_EMPTIES: bvh_node_dict2objects(context, bvh_nodes, IMPORT_START_FRAME, IMPORT_LOOP)
848 print('Done in %.4f\n' % (time.time()-t1))
849 #XXX Blender.Window.WaitCursor(0)
852 Blender.Window.FileSelector(load_bvh_ui, 'Import BVH', '*.bvh')
854 from bpy.props import *
856 class BvhImporter(bpy.types.Operator):
857 '''Load a Wavefront OBJ File.'''
858 bl_idname = "import_anim.bvh"
859 bl_label = "Import BVH"
861 path = StringProperty(name="File Path", description="File path used for importing the OBJ file", maxlen= 1024, default= "")
863 def execute(self, context):
864 # print("Selected: " + context.active_object.name)
866 read_bvh(context, self.path)
870 def invoke(self, context, event):
872 wm.add_fileselect(self)
873 return ('RUNNING_MODAL',)
876 bpy.ops.add(BvhImporter)
880 menu_func = lambda self, context: self.layout.itemO(BvhImporter.bl_idname, text="Motion Capture (.bvh)...")
881 menu_item = dynamic_menu.add(bpy.types.INFO_MT_file_import, menu_func)