1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
21 # Script copyright (C) Campbell Barton
23 from math
import radians
, ceil
26 from mathutils
import Vector
, Euler
, Matrix
33 # BVH_Node type or None for no parent.
35 # A list of children of this type..
37 # Worldspace rest location for the head of this node.
39 # Localspace rest location for the head of this node.
41 # Worldspace rest location for the tail of this node.
43 # Worldspace rest location for the tail of this node.
45 # List of 6 ints, -1 for an unused channel,
46 # otherwise an index for the BVH motion data lines,
47 # loc triple then rot triple.
49 # A triple of indices as to the order rotation is applied.
50 # [0,1,2] is x/y/z - [None, None, None] if no rotation..
52 # Same as above but a string 'XYZ' format..
54 # A list one tuple's one for each frame: (locx, locy, locz, rotx, roty, rotz),
55 # euler rotation ALWAYS stored xyz order, even when native used.
57 # Convenience function, bool, same as: (channels[0] != -1 or channels[1] != -1 or channels[2] != -1).
59 # Convenience function, bool, same as: (channels[3] != -1 or channels[4] != -1 or channels[5] != -1).
61 # Index from the file, not strictly needed but nice to maintain order.
63 # Use this for whatever you want.
68 (None, None, None): 'XYZ', # XXX Dummy one, no rotation anyway!
77 def __init__(self
, name
, rest_head_world
, rest_head_local
, parent
, channels
, rot_order
, index
):
79 self
.rest_head_world
= rest_head_world
80 self
.rest_head_local
= rest_head_local
81 self
.rest_tail_world
= None
82 self
.rest_tail_local
= None
84 self
.channels
= channels
85 self
.rot_order
= tuple(rot_order
)
86 self
.rot_order_str
= BVH_Node
._eul
_order
_lookup
[self
.rot_order
]
89 # convenience functions
90 self
.has_loc
= channels
[0] != -1 or channels
[1] != -1 or channels
[2] != -1
91 self
.has_rot
= channels
[3] != -1 or channels
[4] != -1 or channels
[5] != -1
95 # List of 6 length tuples: (lx, ly, lz, rx, ry, rz)
96 # even if the channels aren't used they will just be zero.
97 self
.anim_data
= [(0, 0, 0, 0, 0, 0)]
101 "BVH name: '%s', rest_loc:(%.3f,%.3f,%.3f), rest_tail:(%.3f,%.3f,%.3f)" % (
103 *self
.rest_head_world
,
104 *self
.rest_head_world
,
109 def sorted_nodes(bvh_nodes
):
110 bvh_nodes_list
= list(bvh_nodes
.values())
111 bvh_nodes_list
.sort(key
=lambda bvh_node
: bvh_node
.index
)
112 return bvh_nodes_list
115 def read_bvh(context
, file_path
, rotate_mode
='XYZ', global_scale
=1.0):
117 # Open the file for importing
118 file = open(file_path
, 'rU')
120 # Seperate into a list of lists, each line a list of words.
121 file_lines
= file.readlines()
122 # Non standard carrage returns?
123 if len(file_lines
) == 1:
124 file_lines
= file_lines
[0].split('\r')
126 # Split by whitespace.
127 file_lines
= [ll
for ll
in [l
.split() for l
in file_lines
] if ll
]
129 # Create hierarchy as empties
130 if file_lines
[0][0].lower() == 'hierarchy':
131 # print 'Importing the BVH Hierarchy for:', file_path
134 raise Exception("This is not a BVH file")
136 bvh_nodes
= {None: None}
137 bvh_nodes_serial
= [None]
138 bvh_frame_count
= None
139 bvh_frame_time
= None
143 lineIdx
= 0 # An index for the file.
144 while lineIdx
< len(file_lines
) - 1:
145 if file_lines
[lineIdx
][0].lower() in {'root', 'joint'}:
147 # Join spaces into 1 word with underscores joining it.
148 if len(file_lines
[lineIdx
]) > 2:
149 file_lines
[lineIdx
][1] = '_'.join(file_lines
[lineIdx
][1:])
150 file_lines
[lineIdx
] = file_lines
[lineIdx
][:2]
152 # MAY NEED TO SUPPORT MULTIPLE ROOTS HERE! Still unsure weather multiple roots are possible?
154 # Make sure the names are unique - Object names will match joint names exactly and both will be unique.
155 name
= file_lines
[lineIdx
][1]
157 # print '%snode: %s, parent: %s' % (len(bvh_nodes_serial) * ' ', name, bvh_nodes_serial[-1])
159 lineIdx
+= 2 # Increment to the next line (Offset)
160 rest_head_local
= Vector((float(file_lines
[lineIdx
][1]), float(file_lines
[lineIdx
][2]), float(file_lines
[lineIdx
][3]))) * global_scale
161 lineIdx
+= 1 # Increment to the next line (Channels)
163 # newChannel[Xposition, Yposition, Zposition, Xrotation, Yrotation, Zrotation]
164 # newChannel references indices to the motiondata,
165 # if not assigned then -1 refers to the last value that will be added on loading at a value of zero, this is appended
166 # We'll add a zero value onto the end of the MotionDATA so this always refers to a value.
167 my_channel
= [-1, -1, -1, -1, -1, -1]
168 my_rot_order
= [None, None, None]
170 for channel
in file_lines
[lineIdx
][2:]:
171 channel
= channel
.lower()
172 channelIndex
+= 1 # So the index points to the right channel
173 if channel
== 'xposition':
174 my_channel
[0] = channelIndex
175 elif channel
== 'yposition':
176 my_channel
[1] = channelIndex
177 elif channel
== 'zposition':
178 my_channel
[2] = channelIndex
180 elif channel
== 'xrotation':
181 my_channel
[3] = channelIndex
182 my_rot_order
[rot_count
] = 0
184 elif channel
== 'yrotation':
185 my_channel
[4] = channelIndex
186 my_rot_order
[rot_count
] = 1
188 elif channel
== 'zrotation':
189 my_channel
[5] = channelIndex
190 my_rot_order
[rot_count
] = 2
193 channels
= file_lines
[lineIdx
][2:]
195 my_parent
= bvh_nodes_serial
[-1] # account for none
197 # Apply the parents offset accumulatively
198 if my_parent
is None:
199 rest_head_world
= Vector(rest_head_local
)
201 rest_head_world
= my_parent
.rest_head_world
+ rest_head_local
203 bvh_node
= bvh_nodes
[name
] = BVH_Node(name
, rest_head_world
, rest_head_local
, my_parent
, my_channel
, my_rot_order
, len(bvh_nodes
) - 1)
205 # If we have another child then we can call ourselves a parent, else
206 bvh_nodes_serial
.append(bvh_node
)
208 # Account for an end node.
209 # There is sometimes a name after 'End Site' but we will ignore it.
210 if file_lines
[lineIdx
][0].lower() == 'end' and file_lines
[lineIdx
][1].lower() == 'site':
211 # Increment to the next line (Offset)
213 rest_tail
= Vector((float(file_lines
[lineIdx
][1]), float(file_lines
[lineIdx
][2]), float(file_lines
[lineIdx
][3]))) * global_scale
215 bvh_nodes_serial
[-1].rest_tail_world
= bvh_nodes_serial
[-1].rest_head_world
+ rest_tail
216 bvh_nodes_serial
[-1].rest_tail_local
= bvh_nodes_serial
[-1].rest_head_local
+ rest_tail
218 # Just so we can remove the parents in a uniform way,
219 # the end has kids so this is a placeholder.
220 bvh_nodes_serial
.append(None)
222 if len(file_lines
[lineIdx
]) == 1 and file_lines
[lineIdx
][0] == '}': # == ['}']
223 bvh_nodes_serial
.pop() # Remove the last item
225 # End of the hierarchy. Begin the animation section of the file with
226 # the following header.
230 if len(file_lines
[lineIdx
]) == 1 and file_lines
[lineIdx
][0].lower() == 'motion':
231 lineIdx
+= 1 # Read frame count.
232 if (len(file_lines
[lineIdx
]) == 2 and
233 file_lines
[lineIdx
][0].lower() == 'frames:'):
235 bvh_frame_count
= int(file_lines
[lineIdx
][1])
237 lineIdx
+= 1 # Read frame rate.
239 len(file_lines
[lineIdx
]) == 3 and
240 file_lines
[lineIdx
][0].lower() == 'frame' and
241 file_lines
[lineIdx
][1].lower() == 'time:'
243 bvh_frame_time
= float(file_lines
[lineIdx
][2])
245 lineIdx
+= 1 # Set the cursor to the first frame
251 # Remove the None value used for easy parent reference
256 # importing world with any order but nicer to maintain order
257 # second life expects it, which isn't to spec.
258 bvh_nodes_list
= sorted_nodes(bvh_nodes
)
260 while lineIdx
< len(file_lines
):
261 line
= file_lines
[lineIdx
]
262 for bvh_node
in bvh_nodes_list
:
263 # for bvh_node in bvh_nodes_serial:
264 lx
= ly
= lz
= rx
= ry
= rz
= 0.0
265 channels
= bvh_node
.channels
266 anim_data
= bvh_node
.anim_data
267 if channels
[0] != -1:
268 lx
= global_scale
* float(line
[channels
[0]])
270 if channels
[1] != -1:
271 ly
= global_scale
* float(line
[channels
[1]])
273 if channels
[2] != -1:
274 lz
= global_scale
* float(line
[channels
[2]])
276 if channels
[3] != -1 or channels
[4] != -1 or channels
[5] != -1:
278 rx
= radians(float(line
[channels
[3]]))
279 ry
= radians(float(line
[channels
[4]]))
280 rz
= radians(float(line
[channels
[5]]))
282 # Done importing motion data #
283 anim_data
.append((lx
, ly
, lz
, rx
, ry
, rz
))
287 for bvh_node
in bvh_nodes_list
:
288 bvh_node_parent
= bvh_node
.parent
290 bvh_node_parent
.children
.append(bvh_node
)
292 # Now set the tip of each bvh_node
293 for bvh_node
in bvh_nodes_list
:
295 if not bvh_node
.rest_tail_world
:
296 if len(bvh_node
.children
) == 0:
297 # could just fail here, but rare BVH files have childless nodes
298 bvh_node
.rest_tail_world
= Vector(bvh_node
.rest_head_world
)
299 bvh_node
.rest_tail_local
= Vector(bvh_node
.rest_head_local
)
300 elif len(bvh_node
.children
) == 1:
301 bvh_node
.rest_tail_world
= Vector(bvh_node
.children
[0].rest_head_world
)
302 bvh_node
.rest_tail_local
= bvh_node
.rest_head_local
+ bvh_node
.children
[0].rest_head_local
304 # allow this, see above
305 # if not bvh_node.children:
306 # raise Exception("bvh node has no end and no children. bad file")
308 # Removed temp for now
309 rest_tail_world
= Vector((0.0, 0.0, 0.0))
310 rest_tail_local
= Vector((0.0, 0.0, 0.0))
311 for bvh_node_child
in bvh_node
.children
:
312 rest_tail_world
+= bvh_node_child
.rest_head_world
313 rest_tail_local
+= bvh_node_child
.rest_head_local
315 bvh_node
.rest_tail_world
= rest_tail_world
* (1.0 / len(bvh_node
.children
))
316 bvh_node
.rest_tail_local
= rest_tail_local
* (1.0 / len(bvh_node
.children
))
318 # Make sure tail isn't the same location as the head.
319 if (bvh_node
.rest_tail_local
- bvh_node
.rest_head_local
).length
<= 0.001 * global_scale
:
320 print("\tzero length node found:", bvh_node
.name
)
321 bvh_node
.rest_tail_local
.y
= bvh_node
.rest_tail_local
.y
+ global_scale
/ 10
322 bvh_node
.rest_tail_world
.y
= bvh_node
.rest_tail_world
.y
+ global_scale
/ 10
324 return bvh_nodes
, bvh_frame_time
, bvh_frame_count
327 def bvh_node_dict2objects(context
, bvh_name
, bvh_nodes
, rotate_mode
='NATIVE', frame_start
=1, IMPORT_LOOP
=False):
332 scene
= context
.scene
333 for obj
in scene
.objects
:
339 obj
= bpy
.data
.objects
.new(name
, None)
340 scene
.objects
.link(obj
)
345 obj
.empty_draw_type
= 'CUBE'
346 obj
.empty_draw_size
= 0.1
351 for name
, bvh_node
in bvh_nodes
.items():
352 bvh_node
.temp
= add_ob(name
)
353 bvh_node
.temp
.rotation_mode
= bvh_node
.rot_order_str
[::-1]
356 for bvh_node
in bvh_nodes
.values():
357 for bvh_node_child
in bvh_node
.children
:
358 bvh_node_child
.temp
.parent
= bvh_node
.temp
361 for bvh_node
in bvh_nodes
.values():
362 # Make relative to parents offset
363 bvh_node
.temp
.location
= bvh_node
.rest_head_local
366 for name
, bvh_node
in bvh_nodes
.items():
367 if not bvh_node
.children
:
368 ob_end
= add_ob(name
+ '_end')
369 ob_end
.parent
= bvh_node
.temp
370 ob_end
.location
= bvh_node
.rest_tail_world
- bvh_node
.rest_head_world
372 for name
, bvh_node
in bvh_nodes
.items():
375 for frame_current
in range(len(bvh_node
.anim_data
)):
377 lx
, ly
, lz
, rx
, ry
, rz
= bvh_node
.anim_data
[frame_current
]
380 obj
.delta_location
= Vector((lx
, ly
, lz
)) - bvh_node
.rest_head_world
381 obj
.keyframe_insert("delta_location", index
=-1, frame
=frame_start
+ frame_current
)
384 obj
.delta_rotation_euler
= rx
, ry
, rz
385 obj
.keyframe_insert("delta_rotation_euler", index
=-1, frame
=frame_start
+ frame_current
)
390 def bvh_node_dict2armature(
405 # Add the new armature,
406 scene
= context
.scene
407 for obj
in scene
.objects
:
410 arm_data
= bpy
.data
.armatures
.new(bvh_name
)
411 arm_ob
= bpy
.data
.objects
.new(bvh_name
, arm_data
)
413 scene
.objects
.link(arm_ob
)
416 scene
.objects
.active
= arm_ob
418 bpy
.ops
.object.mode_set(mode
='OBJECT', toggle
=False)
419 bpy
.ops
.object.mode_set(mode
='EDIT', toggle
=False)
421 bvh_nodes_list
= sorted_nodes(bvh_nodes
)
423 # Get the average bone length for zero length bones, we may not use this.
424 average_bone_length
= 0.0
426 for bvh_node
in bvh_nodes_list
:
427 l
= (bvh_node
.rest_head_local
- bvh_node
.rest_tail_local
).length
429 average_bone_length
+= l
432 # Very rare cases all bones could be zero length???
433 if not average_bone_length
:
434 average_bone_length
= 0.1
437 average_bone_length
= average_bone_length
/ nonzero_count
439 # XXX, annoying, remove bone.
440 while arm_data
.edit_bones
:
441 arm_ob
.edit_bones
.remove(arm_data
.edit_bones
[-1])
444 for bvh_node
in bvh_nodes_list
:
447 bone
= bvh_node
.temp
= arm_data
.edit_bones
.new(bvh_node
.name
)
449 bone
.head
= bvh_node
.rest_head_world
450 bone
.tail
= bvh_node
.rest_tail_world
452 # Zero Length Bones! (an exceptional case)
453 if (bone
.head
- bone
.tail
).length
< 0.001:
454 print("\tzero length bone found:", bone
.name
)
456 ofs
= bvh_node
.parent
.rest_head_local
- bvh_node
.parent
.rest_tail_local
457 if ofs
.length
: # is our parent zero length also?? unlikely
458 bone
.tail
= bone
.tail
- ofs
460 bone
.tail
.y
= bone
.tail
.y
+ average_bone_length
462 bone
.tail
.y
= bone
.tail
.y
+ average_bone_length
464 ZERO_AREA_BONES
.append(bone
.name
)
466 for bvh_node
in bvh_nodes_list
:
468 # bvh_node.temp is the Editbone
470 # Set the bone parent
471 bvh_node
.temp
.parent
= bvh_node
.parent
.temp
473 # Set the connection state
474 if((not bvh_node
.has_loc
) and
475 (bvh_node
.parent
.temp
.name
not in ZERO_AREA_BONES
) and
476 (bvh_node
.parent
.rest_tail_local
== bvh_node
.rest_head_local
)):
478 bvh_node
.temp
.use_connect
= True
480 # Replace the editbone with the editbone name,
481 # to avoid memory errors accessing the editbone outside editmode
482 for bvh_node
in bvh_nodes_list
:
483 bvh_node
.temp
= bvh_node
.temp
.name
485 # Now Apply the animation to the armature
487 # Get armature animation data
488 bpy
.ops
.object.mode_set(mode
='OBJECT', toggle
=False)
491 pose_bones
= pose
.bones
493 if rotate_mode
== 'NATIVE':
494 for bvh_node
in bvh_nodes_list
:
495 bone_name
= bvh_node
.temp
# may not be the same name as the bvh_node, could have been shortened.
496 pose_bone
= pose_bones
[bone_name
]
497 pose_bone
.rotation_mode
= bvh_node
.rot_order_str
499 elif rotate_mode
!= 'QUATERNION':
500 for pose_bone
in pose_bones
:
501 pose_bone
.rotation_mode
= rotate_mode
506 context
.scene
.update()
508 arm_ob
.animation_data_create()
509 action
= bpy
.data
.actions
.new(name
=bvh_name
)
510 arm_ob
.animation_data
.action
= action
512 # Replace the bvh_node.temp (currently an editbone)
513 # With a tuple (pose_bone, armature_bone, bone_rest_matrix, bone_rest_matrix_inv)
515 for bvh_node
in bvh_nodes_list
:
516 bone_name
= bvh_node
.temp
# may not be the same name as the bvh_node, could have been shortened.
517 pose_bone
= pose_bones
[bone_name
]
518 rest_bone
= arm_data
.bones
[bone_name
]
519 bone_rest_matrix
= rest_bone
.matrix_local
.to_3x3()
521 bone_rest_matrix_inv
= Matrix(bone_rest_matrix
)
522 bone_rest_matrix_inv
.invert()
524 bone_rest_matrix_inv
.resize_4x4()
525 bone_rest_matrix
.resize_4x4()
526 bvh_node
.temp
= (pose_bone
, bone
, bone_rest_matrix
, bone_rest_matrix_inv
)
529 num_frame
= len(bvh_node
.anim_data
)
531 # Choose to skip some frames at the beginning. Frame 0 is the rest pose
532 # used internally by this importer. Frame 1, by convention, is also often
533 # the rest pose of the skeleton exported by the motion capture system.
535 if num_frame
> skip_frame
:
536 num_frame
= num_frame
- skip_frame
538 # Create a shared time axis for all animation curves.
539 time
= [float(frame_start
)] * num_frame
541 dt
= scene
.render
.fps
* bvh_frame_time
542 for frame_i
in range(1, num_frame
):
543 time
[frame_i
] += float(frame_i
) * dt
545 for frame_i
in range(1, num_frame
):
546 time
[frame_i
] += float(frame_i
)
548 # print("bvh_frame_time = %f, dt = %f, num_frame = %d"
549 # % (bvh_frame_time, dt, num_frame]))
551 for i
, bvh_node
in enumerate(bvh_nodes_list
):
552 pose_bone
, bone
, bone_rest_matrix
, bone_rest_matrix_inv
= bvh_node
.temp
555 # Not sure if there is a way to query this or access it in the
556 # PoseBone structure.
557 data_path
= 'pose.bones["%s"].location' % pose_bone
.name
559 location
= [(0.0, 0.0, 0.0)] * num_frame
560 for frame_i
in range(num_frame
):
561 bvh_loc
= bvh_node
.anim_data
[frame_i
+ skip_frame
][:3]
563 bone_translate_matrix
= Matrix
.Translation(
564 Vector(bvh_loc
) - bvh_node
.rest_head_local
)
565 location
[frame_i
] = (bone_rest_matrix_inv
*
566 bone_translate_matrix
).to_translation()
568 # For each location x, y, z.
569 for axis_i
in range(3):
570 curve
= action
.fcurves
.new(data_path
=data_path
, index
=axis_i
)
571 keyframe_points
= curve
.keyframe_points
572 keyframe_points
.add(num_frame
)
574 for frame_i
in range(num_frame
):
575 keyframe_points
[frame_i
].co
= \
576 (time
[frame_i
], location
[frame_i
][axis_i
])
582 if 'QUATERNION' == rotate_mode
:
583 rotate
= [(1.0, 0.0, 0.0, 0.0)] * num_frame
584 data_path
= ('pose.bones["%s"].rotation_quaternion'
587 rotate
= [(0.0, 0.0, 0.0)] * num_frame
588 data_path
= ('pose.bones["%s"].rotation_euler' %
591 prev_euler
= Euler((0.0, 0.0, 0.0))
592 for frame_i
in range(num_frame
):
593 bvh_rot
= bvh_node
.anim_data
[frame_i
+ skip_frame
][3:]
595 # apply rotation order and convert to XYZ
596 # note that the rot_order_str is reversed.
597 euler
= Euler(bvh_rot
, bvh_node
.rot_order_str
[::-1])
598 bone_rotation_matrix
= euler
.to_matrix().to_4x4()
599 bone_rotation_matrix
= (bone_rest_matrix_inv
*
600 bone_rotation_matrix
*
603 if 4 == len(rotate
[frame_i
]):
604 rotate
[frame_i
] = bone_rotation_matrix
.to_quaternion()
606 rotate
[frame_i
] = bone_rotation_matrix
.to_euler(
607 pose_bone
.rotation_mode
, prev_euler
)
608 prev_euler
= rotate
[frame_i
]
610 # For each Euler angle x, y, z (or Quaternion w, x, y, z).
611 for axis_i
in range(len(rotate
[0])):
612 curve
= action
.fcurves
.new(data_path
=data_path
, index
=axis_i
)
613 keyframe_points
= curve
.keyframe_points
614 curve
.keyframe_points
.add(num_frame
)
616 for frame_i
in range(0, num_frame
):
617 keyframe_points
[frame_i
].co
= \
618 (time
[frame_i
], rotate
[frame_i
][axis_i
])
620 for cu
in action
.fcurves
:
622 pass # 2.5 doenst have cyclic now?
624 for bez
in cu
.keyframe_points
:
625 bez
.interpolation
= 'LINEAR'
627 # finally apply matrix
628 arm_ob
.matrix_world
= global_matrix
629 bpy
.ops
.object.transform_apply(rotation
=True)
639 rotate_mode
='NATIVE',
645 update_scene_fps
=False,
646 update_scene_duration
=False,
651 print("\tparsing bvh %r..." % filepath
, end
="")
653 bvh_nodes
, bvh_frame_time
, bvh_frame_count
= read_bvh(
655 rotate_mode
=rotate_mode
,
656 global_scale
=global_scale
,
659 print("%.4f" % (time
.time() - t1
))
661 scene
= context
.scene
662 frame_orig
= scene
.frame_current
664 # Broken BVH handling: guess frame rate when it is not contained in the file.
665 if bvh_frame_time
is None:
668 "The BVH file does not contain frame duration in its MOTION "
669 "section, assuming the BVH and Blender scene have the same "
672 bvh_frame_time
= scene
.render
.fps_base
/ scene
.render
.fps
673 # No need to scale the frame rate, as they're equal now anyway.
674 use_fps_scale
= False
677 _update_scene_fps(context
, report
, bvh_frame_time
)
679 # Now that we have a 1-to-1 mapping of Blender frames and BVH frames, there is no need
680 # to scale the FPS any more. It's even better not to, to prevent roundoff errors.
681 use_fps_scale
= False
683 if update_scene_duration
:
684 _update_scene_duration(context
, report
, bvh_frame_count
, bvh_frame_time
, frame_start
, use_fps_scale
)
687 print("\timporting to blender...", end
="")
689 bvh_name
= bpy
.path
.display_name_from_filepath(filepath
)
691 if target
== 'ARMATURE':
692 bvh_node_dict2armature(
693 context
, bvh_name
, bvh_nodes
, bvh_frame_time
,
694 rotate_mode
=rotate_mode
,
695 frame_start
=frame_start
,
696 IMPORT_LOOP
=use_cyclic
,
697 global_matrix
=global_matrix
,
698 use_fps_scale
=use_fps_scale
,
701 elif target
== 'OBJECT':
702 bvh_node_dict2objects(
703 context
, bvh_name
, bvh_nodes
,
704 rotate_mode
=rotate_mode
,
705 frame_start
=frame_start
,
706 IMPORT_LOOP
=use_cyclic
,
707 # global_matrix=global_matrix, # TODO
711 report({'ERROR'}, "Invalid target %r (must be 'ARMATURE' or 'OBJECT')" % target
)
714 print('Done in %.4f\n' % (time
.time() - t1
))
716 context
.scene
.frame_set(frame_orig
)
721 def _update_scene_fps(context
, report
, bvh_frame_time
):
722 """Update the scene's FPS settings from the BVH, but only if the BVH contains enough info."""
724 # Broken BVH handling: prevent division by zero.
725 if bvh_frame_time
== 0.0:
728 "Unable to update scene frame rate, as the BVH file "
729 "contains a zero frame duration in its MOTION section",
733 scene
= context
.scene
734 scene_fps
= scene
.render
.fps
/ scene
.render
.fps_base
735 new_fps
= 1.0 / bvh_frame_time
737 if scene
.render
.fps
!= new_fps
or scene
.render
.fps_base
!= 1.0:
738 print("\tupdating scene FPS (was %f) to BVH FPS (%f)" % (scene_fps
, new_fps
))
739 scene
.render
.fps
= new_fps
740 scene
.render
.fps_base
= 1.0
743 def _update_scene_duration(
744 context
, report
, bvh_frame_count
, bvh_frame_time
, frame_start
,
746 """Extend the scene's duration so that the BVH file fits in its entirety."""
748 if bvh_frame_count
is None:
751 "Unable to extend the scene duration, as the BVH file does not "
752 "contain the number of frames in its MOTION section",
756 # Not likely, but it can happen when a BVH is just used to store an armature.
757 if bvh_frame_count
== 0:
761 scene_fps
= context
.scene
.render
.fps
/ context
.scene
.render
.fps_base
762 scaled_frame_count
= int(ceil(bvh_frame_count
* bvh_frame_time
* scene_fps
))
763 bvh_last_frame
= frame_start
+ scaled_frame_count
765 bvh_last_frame
= frame_start
+ bvh_frame_count
767 # Only extend the scene, never shorten it.
768 if context
.scene
.frame_end
< bvh_last_frame
:
769 context
.scene
.frame_end
= bvh_last_frame