4 Name: 'Motion Capture (.bvh)...'
7 Tip: 'Import a (.bvh) motion capture file'
10 __author__
= "Campbell Barton"
11 __url__
= ("blender.org", "blenderartists.org")
12 __version__
= "1.90 06/08/01"
15 This script imports BVH motion capture data to Blender.
16 as empties or armatures.
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 # --------------------------------------------------------------------------
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):
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
):
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
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
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)]
92 return 'BVH name:"%s", rest_loc:(%.3f,%.3f,%.3f), rest_tail:(%.3f,%.3f,%.3f)' %\
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')]
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):
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
131 raise 'ERROR: This is not a BVH file'
133 bvh_nodes
= {None:None}
134 bvh_nodes_serial
= [None]
139 lineIdx
= 0 # An index for the file.
140 while lineIdx
< len(file_lines
) -1:
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]
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
178 elif channel
== 'yrotation':
179 my_channel
[4] = channelIndex
180 my_rot_order
[rot_count
]= 1
182 elif channel
== 'zrotation':
183 my_channel
[5] = channelIndex
184 my_rot_order
[rot_count
]= 2
187 channels
= file_lines
[lineIdx
][2:]
189 my_parent
= bvh_nodes_serial
[-1] # account for none
192 # Apply the parents offset accumletivly
194 rest_head_world
= Vector(rest_head_local
)
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
227 # Remove the None value used for easy parent reference
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
) )
271 for bvh_node
in bvh_nodes
.itervalues():
272 bvh_node_parent
= 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
)
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
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
= []
320 ob
= scn
.objects
.new('Empty')
325 for name
, bvh_node
in bvh_nodes
.iteritems():
326 bvh_node
.temp
= add_ob(name
)
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.
333 for bvh_node
in bvh_nodes
.itervalues():
334 # Make relative to parents offset
335 bvh_node
.temp
.loc
= bvh_node
.rest_head_local
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
)
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
385 for bvh_node
in bvh_nodes
.itervalues():
386 l
= (bvh_node
.rest_head_local
-bvh_node
.rest_tail_local
).length
388 average_bone_length
+= l
391 # Very rare cases all bones couldbe zero length???
392 if not average_bone_length
:
393 average_bone_length
= 0.1
396 average_bone_length
= average_bone_length
/nonzero_count
401 for name
, bvh_node
in bvh_nodes
.iteritems():
403 bone
= bvh_node
.temp
= Blender
.Armature
.Editbone()
406 arm_data
.bones
[name
]= bone
408 bone
.head
= bvh_node
.rest_head_world
409 bone
.tail
= bvh_node
.rest_tail_world
412 if (bone
.head
-bone
.tail
).length
< 0.001:
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
418 bone
.tail
.y
= bone
.tail
.y
+average_bone_length
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():
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\
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
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
],\
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
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]
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()
502 # Set the Location, simple too
504 TranslationMatrix(Vector(lx
, ly
, lz
) - bvh_node
.rest_head_local
) *\
505 bone_rest_matrix_inv
).translationPart() # WHY * 10? - just how pose works
508 xformConstants
= xformConstants_dict
[bvh_node
.has_loc
, bvh_node
.has_rot
]
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
517 for ipo
in action
.getAllChannelIpos().itervalues():
520 cur
.interpolation
= Blender
.IpoCurve
.InterpTypes
.LINEAR
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
537 xformConstants= xformConstants_dict[bvh_node.has_loc, bvh_node.has_rot]
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:
556 ipo= action_ipos[bvh_node.temp[0].name] # posebones name as key
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
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
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
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
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
657 # IPO KEYFRAME SETTING
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
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)
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):
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)
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
):
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')
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)
741 Blender
.Window
.FileSelector(load_bvh_ui
, 'Import BVH', '*.bvh')
743 if __name__
== '__main__':
747 scn = bpy.data.scenes.active
748 for ob in list(scn.objects):
750 scn.objects.unlink(ob)
751 load_bvh_ui('/test.bvh', False)