From 08d13c024f60b03e7f525082db9fb12fbc576502 Mon Sep 17 00:00:00 2001 From: Julien Duroure <julien.duroure@gmail.com> Date: Wed, 13 Jul 2022 12:10:03 +0200 Subject: [PATCH] glTF exporter: Fixed bezier control points to cubic spline tangents conversion Fix when animation are not baked --- io_scene_gltf2/__init__.py | 2 +- ...nder_gather_animation_sampler_keyframes.py | 22 +++++++++++-------- ...gltf2_blender_gather_animation_samplers.py | 13 ++++++----- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 5c8872f3d..be8aa8edf 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin SchmithĂĽsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 15), + "version": (3, 3, 16), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', 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 e1ed19ea1..ba331b74d 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 @@ -398,11 +398,17 @@ def gather_keyframes(blender_obj_uuid: str, key.set_first_tangent() else: # otherwise construct an in tangent coordinate from the keyframes control points. We intermediately - # use a point at t-1 to define the tangent. This allows the tangent control point to be transformed - # normally + # use a point at t+1 to define the tangent. This allows the tangent control point to be transformed + # normally, but only works for locally linear transformation. The more non-linear a transform, the + # more imprecise this method is. + # We could use any other (v1, t1) for which (v1 - v0) / (t1 - t0) equals the tangent. By using t+1 + # for both in and out tangents, we guarantee that (even if there are errors or numerical imprecisions) + # symmetrical control points translate to symmetrical tangents. + # Note: I am not sure that linearity is never broken with quaternions and their normalization. + # Especially at sign swap it might occure that the value gets negated but the control point not. + # I have however not once encountered an issue with this. key.in_tangent = [ - c.keyframe_points[i].co[1] + ((c.keyframe_points[i].co[1] - c.keyframe_points[i].handle_left[1] - ) / (frame - frames[i - 1])) + c.keyframe_points[i].co[1] + (c.keyframe_points[i].handle_left[1] - c.keyframe_points[i].co[1]) / (c.keyframe_points[i].handle_left[0] - c.keyframe_points[i].co[0]) for c in channels if c is not None ] # Construct the out tangent @@ -410,12 +416,10 @@ def gather_keyframes(blender_obj_uuid: str, # end out-tangent should become all zero key.set_last_tangent() else: - # otherwise construct an in tangent coordinate from the keyframes control points. We intermediately - # use a point at t+1 to define the tangent. This allows the tangent control point to be transformed - # normally + # otherwise construct an in tangent coordinate from the keyframes control points. + # This happens the same way how in tangents are handled above. key.out_tangent = [ - c.keyframe_points[i].co[1] + ((c.keyframe_points[i].handle_right[1] - c.keyframe_points[i].co[1] - ) / (frames[i + 1] - frame)) + c.keyframe_points[i].co[1] + (c.keyframe_points[i].handle_right[1] - c.keyframe_points[i].co[1]) / (c.keyframe_points[i].handle_right[0] - c.keyframe_points[i].co[0]) for c in channels if c is not None ] diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py index 1ee98a29f..5c8011ed5 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py @@ -414,6 +414,7 @@ def __gather_output(channels: typing.Tuple[bpy.types.FCurve], transform = parent_inverse values = [] + fps = bpy.context.scene.render.fps for keyframe in keyframes: # Transform the data and build gltf control points value = gltf2_blender_math.transform(keyframe.value, target_datapath, transform, need_rotation_correction) @@ -426,11 +427,11 @@ def __gather_output(channels: typing.Tuple[bpy.types.FCurve], in_tangent = gltf2_blender_math.transform(keyframe.in_tangent, target_datapath, transform, need_rotation_correction) if is_yup and blender_object_if_armature is None: in_tangent = gltf2_blender_math.swizzle_yup(in_tangent, target_datapath) - # the tangent in glTF is relative to the keyframe value + # the tangent in glTF is relative to the keyframe value and uses seconds if not isinstance(value, list): - in_tangent = value - in_tangent + in_tangent = fps * (in_tangent - value) else: - in_tangent = [value[i] - in_tangent[i] for i in range(len(value))] + in_tangent = [fps * (in_tangent[i] - value[i]) for i in range(len(value))] keyframe_value = gltf2_blender_math.mathutils_to_gltf(in_tangent) + keyframe_value # append if keyframe.out_tangent is not None: @@ -438,11 +439,11 @@ def __gather_output(channels: typing.Tuple[bpy.types.FCurve], out_tangent = gltf2_blender_math.transform(keyframe.out_tangent, target_datapath, transform, need_rotation_correction) if is_yup and blender_object_if_armature is None: out_tangent = gltf2_blender_math.swizzle_yup(out_tangent, target_datapath) - # the tangent in glTF is relative to the keyframe value + # the tangent in glTF is relative to the keyframe value and uses seconds if not isinstance(value, list): - out_tangent = value - out_tangent + out_tangent = fps * (out_tangent - value) else: - out_tangent = [value[i] - out_tangent[i] for i in range(len(value))] + out_tangent = [fps * (out_tangent[i] - value[i]) for i in range(len(value))] keyframe_value = keyframe_value + gltf2_blender_math.mathutils_to_gltf(out_tangent) # append values += keyframe_value -- GitLab