From 8d9e3c94aff9e366d9a4804d98b2517bd04244f6 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Wed, 11 Sep 2019 22:17:38 +0200 Subject: glTF exporter: performance: huge speedup when exporting using sample animations --- ...2_blender_gather_animation_sampler_keyframes.py | 65 +++++++++++++++++++--- .../blender/exp/gltf2_blender_gather_cache.py | 21 +++++++ 2 files changed, 77 insertions(+), 9 deletions(-) (limited to 'io_scene_gltf2/blender') diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py index 5d7763d0..20e7dbb6 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py @@ -16,7 +16,7 @@ import bpy import mathutils import typing -from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached +from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached, bonecache from io_scene_gltf2.blender.com import gltf2_blender_math from io_scene_gltf2.blender.exp import gltf2_blender_get from io_scene_gltf2.blender.exp import gltf2_blender_extract @@ -111,6 +111,47 @@ class Keyframe: self.__out_tangent = self.__set_indexed(value) + +@bonecache +def get_bone_matrix(blender_object_if_armature: typing.Optional[bpy.types.Object], + channels: typing.Tuple[bpy.types.FCurve], + bake_bone: typing.Union[str, None], + bake_channel: typing.Union[str, None], + bake_range_start, + bake_range_end, + action_name: str, + current_frame: int, + step: int + ): + + data = {} + + if bake_bone is None: + # Find the start and end of the whole action group + ranges = [channel.range() for channel in channels] + + start_frame = min([channel.range()[0] for channel in channels]) + end_frame = max([channel.range()[1] for channel in channels]) + else: + start_frame = bake_range_start + end_frame = bake_range_end + + frame = start_frame + while frame <= end_frame: + data[frame] = {} + # we need to bake in the constraints + bpy.context.scene.frame_set(frame) + for pbone in blender_object_if_armature.pose.bones: + if bake_bone is None: + matrix = pbone.matrix_basis + else: + matrix = pbone.matrix + matrix = blender_object_if_armature.convert_space(pose_bone=pbone, matrix=matrix, from_space='POSE', to_space='LOCAL') + data[frame][pbone.name] = matrix + frame += step + + return data + # cache for performance reasons @cached def gather_keyframes(blender_object_if_armature: typing.Optional[bpy.types.Object], @@ -154,14 +195,20 @@ def gather_keyframes(blender_object_if_armature: typing.Optional[bpy.types.Objec while frame <= end_frame: key = Keyframe(channels, frame, bake_channel) if isinstance(pose_bone_if_armature, bpy.types.PoseBone): - # we need to bake in the constraints - bpy.context.scene.frame_set(frame) - if bake_bone is None: - trans, rot, scale = pose_bone_if_armature.matrix_basis.decompose() - else: - matrix = pose_bone_if_armature.matrix - new_matrix = blender_object_if_armature.convert_space(pose_bone=pose_bone_if_armature, matrix=matrix, from_space='POSE', to_space='LOCAL') - trans, rot, scale = new_matrix.decompose() + + mat = get_bone_matrix( + blender_object_if_armature, + channels, + bake_bone, + bake_channel, + bake_range_start, + bake_range_end, + action_name, + frame, + step + ) + trans, rot, scale = mat.decompose() + if bake_channel is None: target_property = channels[0].data_path.split('.')[-1] else: diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_cache.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_cache.py index 99286aea..e73e5522 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_cache.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_cache.py @@ -65,6 +65,27 @@ def cached(func): return result return wrapper_cached +def bonecache(func): + + @functools.wraps(func) + def wrapper_bonecache(*args, **kwargs): + if args[2] is None: + pose_bone_if_armature = gltf2_blender_get.get_object_from_datapath(args[0], + args[1][0].data_path) + else: + pose_bone_if_armature = args[0].pose.bones[args[2]] + + if not hasattr(func, "__current_action_name"): + func.__current_action_name = None + func.__bonecache = {} + if args[6] != func.__current_action_name: + result = func(*args) + func.__bonecache = result + func.__current_action_name = args[6] + return result[args[7]][pose_bone_if_armature.name] + else: + return func.__bonecache[args[7]][pose_bone_if_armature.name] + return wrapper_bonecache # TODO: replace "cached" with "unique" in all cases where the caching is functional and not only for performance reasons call_or_fetch = cached -- cgit v1.2.3