Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'io_scene_gltf2/blender/imp/gltf2_blender_animation_bone.py')
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_animation_bone.py194
1 files changed, 194 insertions, 0 deletions
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_animation_bone.py b/io_scene_gltf2/blender/imp/gltf2_blender_animation_bone.py
new file mode 100755
index 00000000..c0884966
--- /dev/null
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_animation_bone.py
@@ -0,0 +1,194 @@
+# Copyright 2018 The glTF-Blender-IO authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import bpy
+from mathutils import Matrix
+
+from ..com.gltf2_blender_conversion import loc_gltf_to_blender, quaternion_gltf_to_blender, scale_to_matrix
+from ...io.imp.gltf2_io_binary import BinaryData
+
+
+class BlenderBoneAnim():
+ """Blender Bone Animation."""
+ def __new__(cls, *args, **kwargs):
+ raise RuntimeError("%s should not be instantiated" % cls)
+
+ @staticmethod
+ def set_interpolation(interpolation, kf):
+ """Set interpolation."""
+ if interpolation == "LINEAR":
+ kf.interpolation = 'LINEAR'
+ elif interpolation == "STEP":
+ kf.interpolation = 'CONSTANT'
+ elif interpolation == "CUBICSPLINE":
+ kf.interpolation = 'BEZIER'
+ else:
+ kf.interpolation = 'BEZIER'
+
+ @staticmethod
+ def parse_translation_channel(gltf, node, obj, bone, channel, animation):
+ """Manage Location animation."""
+ fps = bpy.context.scene.render.fps
+ blender_path = "location"
+
+ keys = BinaryData.get_data_from_accessor(gltf, animation.samplers[channel.sampler].input)
+ values = BinaryData.get_data_from_accessor(gltf, animation.samplers[channel.sampler].output)
+ inv_bind_matrix = node.blender_bone_matrix.to_quaternion().to_matrix().to_4x4().inverted() \
+ @ Matrix.Translation(node.blender_bone_matrix.to_translation()).inverted()
+
+ for idx, key in enumerate(keys):
+ if animation.samplers[channel.sampler].interpolation == "CUBICSPLINE":
+ # TODO manage tangent?
+ translation_keyframe = loc_gltf_to_blender(values[idx * 3 + 1])
+ else:
+ translation_keyframe = loc_gltf_to_blender(values[idx])
+ if not node.parent:
+ parent_mat = Matrix()
+ else:
+ if not gltf.data.nodes[node.parent].is_joint:
+ parent_mat = Matrix()
+ else:
+ parent_mat = gltf.data.nodes[node.parent].blender_bone_matrix
+
+ # Pose is in object (armature) space and it's value if the offset from the bind pose
+ # (which is also in object space)
+ # Scale is not taken into account
+ final_trans = (parent_mat @ Matrix.Translation(translation_keyframe)).to_translation()
+ bone.location = inv_bind_matrix @ final_trans
+ bone.keyframe_insert(blender_path, frame=key[0] * fps, group="location")
+
+ for fcurve in [curve for curve in obj.animation_data.action.fcurves if curve.group.name == "location"]:
+ for kf in fcurve.keyframe_points:
+ BlenderBoneAnim.set_interpolation(animation.samplers[channel.sampler].interpolation, kf)
+
+ @staticmethod
+ def parse_rotation_channel(gltf, node, obj, bone, channel, animation):
+ """Manage rotation animation."""
+ # Note: some operations lead to issue with quaternions. Converting to matrix and then back to quaternions breaks
+ # quaternion continuity
+ # (see antipodal quaternions). Blender interpolates between two antipodal quaternions, which causes glitches in
+ # animation.
+ # Converting to euler and then back to quaternion is a dirty fix preventing this issue in animation, until a
+ # better solution is found
+ # This fix is skipped when parent matrix is identity
+ fps = bpy.context.scene.render.fps
+ blender_path = "rotation_quaternion"
+
+ keys = BinaryData.get_data_from_accessor(gltf, animation.samplers[channel.sampler].input)
+ values = BinaryData.get_data_from_accessor(gltf, animation.samplers[channel.sampler].output)
+ bind_rotation = node.blender_bone_matrix.to_quaternion()
+
+ for idx, key in enumerate(keys):
+ if animation.samplers[channel.sampler].interpolation == "CUBICSPLINE":
+ # TODO manage tangent?
+ quat_keyframe = quaternion_gltf_to_blender(values[idx * 3 + 1])
+ else:
+ quat_keyframe = quaternion_gltf_to_blender(values[idx])
+ if not node.parent:
+ bone.rotation_quaternion = bind_rotation.inverted() @ quat_keyframe
+ else:
+ if not gltf.data.nodes[node.parent].is_joint:
+ parent_mat = Matrix()
+ else:
+ parent_mat = gltf.data.nodes[node.parent].blender_bone_matrix
+
+ if parent_mat != parent_mat.inverted():
+ final_rot = (parent_mat @ quat_keyframe.to_matrix().to_4x4()).to_quaternion()
+ bone.rotation_quaternion = bind_rotation.rotation_difference(final_rot).to_euler().to_quaternion()
+ else:
+ bone.rotation_quaternion = \
+ bind_rotation.rotation_difference(quat_keyframe).to_euler().to_quaternion()
+
+ bone.keyframe_insert(blender_path, frame=key[0] * fps, group='rotation')
+
+ for fcurve in [curve for curve in obj.animation_data.action.fcurves if curve.group.name == "rotation"]:
+ for kf in fcurve.keyframe_points:
+ BlenderBoneAnim.set_interpolation(animation.samplers[channel.sampler].interpolation, kf)
+
+ @staticmethod
+ def parse_scale_channel(gltf, node, obj, bone, channel, animation):
+ """Manage scaling animation."""
+ fps = bpy.context.scene.render.fps
+ blender_path = "scale"
+
+ keys = BinaryData.get_data_from_accessor(gltf, animation.samplers[channel.sampler].input)
+ values = BinaryData.get_data_from_accessor(gltf, animation.samplers[channel.sampler].output)
+ bind_scale = scale_to_matrix(node.blender_bone_matrix.to_scale())
+
+ for idx, key in enumerate(keys):
+ if animation.samplers[channel.sampler].interpolation == "CUBICSPLINE":
+ # TODO manage tangent?
+ scale_mat = scale_to_matrix(loc_gltf_to_blender(values[idx * 3 + 1]))
+ else:
+ scale_mat = scale_to_matrix(loc_gltf_to_blender(values[idx]))
+ if not node.parent:
+ bone.scale = (bind_scale.inverted() @ scale_mat).to_scale()
+ else:
+ if not gltf.data.nodes[node.parent].is_joint:
+ parent_mat = Matrix()
+ else:
+ parent_mat = gltf.data.nodes[node.parent].blender_bone_matrix
+
+ bone.scale = (
+ bind_scale.inverted() @ scale_to_matrix(parent_mat.to_scale()) @ scale_mat
+ ).to_scale()
+
+ bone.keyframe_insert(blender_path, frame=key[0] * fps, group='scale')
+
+ for fcurve in [curve for curve in obj.animation_data.action.fcurves if curve.group.name == "scale"]:
+ for kf in fcurve.keyframe_points:
+ BlenderBoneAnim.set_interpolation(animation.samplers[channel.sampler].interpolation, kf)
+
+ @staticmethod
+ def anim(gltf, anim_idx, node_idx):
+ """Manage animation."""
+ node = gltf.data.nodes[node_idx]
+ obj = bpy.data.objects[gltf.data.skins[node.skin_id].blender_armature_name]
+ bone = obj.pose.bones[node.blender_bone_name]
+
+ if anim_idx not in node.animations.keys():
+ return
+
+ animation = gltf.data.animations[anim_idx]
+
+ if animation.name:
+ name = animation.name + "_" + obj.name
+ else:
+ name = "Animation_" + str(anim_idx) + "_" + obj.name
+ if name not in bpy.data.actions:
+ action = bpy.data.actions.new(name)
+ else:
+ action = bpy.data.actions[name]
+ # Check if this action has some users.
+ # If no user (only 1 indeed), that means that this action must be deleted
+ # (is an action from a deleted object)
+ if action.users == 1:
+ bpy.data.actions.remove(action)
+ action = bpy.data.actions.new(name)
+ if not obj.animation_data:
+ obj.animation_data_create()
+ obj.animation_data.action = bpy.data.actions[action.name]
+
+ for channel_idx in node.animations[anim_idx]:
+ channel = animation.channels[channel_idx]
+
+ if channel.target.path == "translation":
+ BlenderBoneAnim.parse_translation_channel(gltf, node, obj, bone, channel, animation)
+
+ elif channel.target.path == "rotation":
+ BlenderBoneAnim.parse_rotation_channel(gltf, node, obj, bone, channel, animation)
+
+ elif channel.target.path == "scale":
+ BlenderBoneAnim.parse_scale_channel(gltf, node, obj, bone, channel, animation)
+