glTF exporter: performance: huge speedup when exporting using sample animations

This commit is contained in:
Julien Duroure 2019-09-11 22:17:38 +02:00
parent 14f1c99e96
commit 8d9e3c94af
3 changed files with 78 additions and 10 deletions

View File

@ -15,7 +15,7 @@
bl_info = {
'name': 'glTF 2.0 format',
'author': 'Julien Duroure, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
"version": (0, 9, 62),
"version": (0, 9, 63),
'blender': (2, 81, 6),
'location': 'File > Import-Export',
'description': 'Import-Export as glTF 2.0',

View File

@ -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:

View File

@ -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