[Bf-extensions-cvs] [75855d72] master: glTF importer: fix skinning & hierarchy issues
Julien Duroure
noreply at git.blender.org
Thu Jan 23 22:12:13 CET 2020
Commit: 75855d723895e25da855087bccbd0266773bad15
Author: Julien Duroure
Date: Thu Jan 23 22:10:48 2020 +0100
Branches: master
https://developer.blender.org/rBA75855d723895e25da855087bccbd0266773bad15
glTF importer: fix skinning & hierarchy issues
See https://github.com/KhronosGroup/glTF-Blender-IO/pull/857 for details
===================================================================
M io_scene_gltf2/__init__.py
M io_scene_gltf2/blender/com/gltf2_blender_conversion.py
M io_scene_gltf2/blender/imp/gltf2_blender_animation.py
M io_scene_gltf2/blender/imp/gltf2_blender_animation_bone.py
M io_scene_gltf2/blender/imp/gltf2_blender_animation_node.py
M io_scene_gltf2/blender/imp/gltf2_blender_animation_weight.py
M io_scene_gltf2/blender/imp/gltf2_blender_camera.py
M io_scene_gltf2/blender/imp/gltf2_blender_gltf.py
M io_scene_gltf2/blender/imp/gltf2_blender_light.py
M io_scene_gltf2/blender/imp/gltf2_blender_mesh.py
M io_scene_gltf2/blender/imp/gltf2_blender_node.py
M io_scene_gltf2/blender/imp/gltf2_blender_primitive.py
M io_scene_gltf2/blender/imp/gltf2_blender_scene.py
M io_scene_gltf2/blender/imp/gltf2_blender_skin.py
A io_scene_gltf2/blender/imp/gltf2_blender_vnode.py
D io_scene_gltf2/io/com/gltf2_io_trs.py
M io_scene_gltf2/io/imp/gltf2_io_gltf.py
===================================================================
diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py
index 092bb830..f051b08e 100755
--- a/io_scene_gltf2/__init__.py
+++ b/io_scene_gltf2/__init__.py
@@ -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": (1, 2, 4),
+ "version": (1, 2, 5),
'blender': (2, 81, 6),
'location': 'File > Import-Export',
'description': 'Import-Export as glTF 2.0',
diff --git a/io_scene_gltf2/blender/com/gltf2_blender_conversion.py b/io_scene_gltf2/blender/com/gltf2_blender_conversion.py
index 6d9a901a..fccfce95 100755
--- a/io_scene_gltf2/blender/com/gltf2_blender_conversion.py
+++ b/io_scene_gltf2/blender/com/gltf2_blender_conversion.py
@@ -15,38 +15,6 @@
from mathutils import Matrix, Quaternion
from math import sqrt, sin, cos
-def matrix_gltf_to_blender(mat_input):
- """Matrix from glTF format to Blender format."""
- mat = Matrix([mat_input[0:4], mat_input[4:8], mat_input[8:12], mat_input[12:16]])
- mat.transpose()
- return mat
-
-def loc_gltf_to_blender(loc):
- """Location."""
- return loc
-
-def scale_gltf_to_blender(scale):
- """Scaling."""
- return scale
-
-def quaternion_gltf_to_blender(q):
- """Quaternion from glTF to Blender."""
- return Quaternion([q[3], q[0], q[1], q[2]])
-
-def scale_to_matrix(scale):
- """Scale to matrix."""
- mat = Matrix()
- for i in range(3):
- mat[i][i] = scale[i]
-
- return mat
-
-def correction_rotation():
- """Correction of Rotation."""
- # Correction is needed for lamps, because Yup2Zup is not written in vertices
- # and lamps has no vertices :)
- return Quaternion((sqrt(2)/2, -sqrt(2)/2, 0.0, 0.0)).to_matrix().to_4x4()
-
def texture_transform_blender_to_gltf(mapping_transform):
"""
Converts the offset/rotation/scale from a Mapping node applied in Blender's
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_animation.py b/io_scene_gltf2/blender/imp/gltf2_blender_animation.py
index 6394145d..ed1d938d 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_animation.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_animation.py
@@ -18,6 +18,7 @@ from .gltf2_blender_animation_bone import BlenderBoneAnim
from .gltf2_blender_animation_node import BlenderNodeAnim
from .gltf2_blender_animation_weight import BlenderWeightAnim
from .gltf2_blender_animation_utils import restore_animation_on_object
+from .gltf2_blender_vnode import VNode
class BlenderAnimation():
@@ -26,34 +27,36 @@ class BlenderAnimation():
raise RuntimeError("%s should not be instantiated" % cls)
@staticmethod
- def anim(gltf, anim_idx, node_idx):
+ def anim(gltf, anim_idx, vnode_id):
"""Dispatch Animation to bone or object."""
- if gltf.data.nodes[node_idx].is_joint:
- BlenderBoneAnim.anim(gltf, anim_idx, node_idx)
- else:
- BlenderNodeAnim.anim(gltf, anim_idx, node_idx)
- BlenderWeightAnim.anim(gltf, anim_idx, node_idx)
+ if isinstance(vnode_id, int):
+ if gltf.vnodes[vnode_id].type == VNode.Bone:
+ BlenderBoneAnim.anim(gltf, anim_idx, vnode_id)
+ elif gltf.vnodes[vnode_id].type == VNode.Object:
+ BlenderNodeAnim.anim(gltf, anim_idx, vnode_id)
- if gltf.data.nodes[node_idx].children:
- for child in gltf.data.nodes[node_idx].children:
- BlenderAnimation.anim(gltf, anim_idx, child)
+ BlenderWeightAnim.anim(gltf, anim_idx, vnode_id)
+
+ for child in gltf.vnodes[vnode_id].children:
+ BlenderAnimation.anim(gltf, anim_idx, child)
@staticmethod
- def restore_animation(gltf, node_idx, animation_name):
+ def restore_animation(gltf, vnode_id, animation_name):
"""Restores the actions for an animation by its track name on
the subtree starting at node_idx."""
- node = gltf.data.nodes[node_idx]
+ vnode = gltf.vnodes[vnode_id]
- if node.is_joint:
- obj = bpy.data.objects[gltf.data.skins[node.skin_id].blender_armature_name]
- else:
- obj = bpy.data.objects[node.blender_object]
+ obj = None
+ if vnode.type == VNode.Bone:
+ obj = gltf.vnodes[vnode.bone_arma].blender_object
+ elif vnode.type == VNode.Object:
+ obj = vnode.blender_object
- restore_animation_on_object(obj, animation_name)
- if obj.data and hasattr(obj.data, 'shape_keys'):
- restore_animation_on_object(obj.data.shape_keys, animation_name)
+ if obj is not None:
+ restore_animation_on_object(obj, animation_name)
+ if obj.data and hasattr(obj.data, 'shape_keys'):
+ restore_animation_on_object(obj.data.shape_keys, animation_name)
- if gltf.data.nodes[node_idx].children:
- for child in gltf.data.nodes[node_idx].children:
- BlenderAnimation.restore_animation(gltf, child, animation_name)
+ for child in gltf.vnodes[vnode_id].children:
+ BlenderAnimation.restore_animation(gltf, child, animation_name)
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_animation_bone.py b/io_scene_gltf2/blender/imp/gltf2_blender_animation_bone.py
index 465a801d..7e32487c 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_animation_bone.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_animation_bone.py
@@ -14,51 +14,56 @@
import json
import bpy
-from mathutils import Matrix
+from mathutils import Vector
-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
from .gltf2_blender_animation_utils import simulate_stash, make_fcurve
+# The glTF curves store the value of the final transform, but in Blender
+# curves animate a pose bone that is relative to the edit bone
+#
+# Final = EditBone * PoseBone
+# where
+# Final = Trans[ft] Rot[fr] Scale[fs]
+# EditBone = Trans[et] Rot[er] (edit bones have no scale)
+# PoseBone = Trans[pt] Rot[pr] Scale[ps]
+#
+# Solving for the PoseBone gives the change we need to apply to the curves
+#
+# pt = Rot[er^{-1}] (ft - et)
+# pr = er^{-1} fr
+# ps = fs
+
class BlenderBoneAnim():
"""Blender Bone Animation."""
def __new__(cls, *args, **kwargs):
raise RuntimeError("%s should not be instantiated" % cls)
@staticmethod
- def parse_translation_channel(gltf, node, obj, bone, channel, animation):
+ def parse_translation_channel(gltf, vnode, obj, bone, channel, animation):
"""Manage Location animation."""
blender_path = "pose.bones[" + json.dumps(bone.name) + "].location"
group_name = bone.name
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()
if animation.samplers[channel.sampler].interpolation == "CUBICSPLINE":
# TODO manage tangent?
translation_keyframes = (
- loc_gltf_to_blender(values[idx * 3 + 1])
+ gltf.loc_gltf_to_blender(values[idx * 3 + 1])
for idx in range(0, len(keys))
)
else:
- translation_keyframes = (loc_gltf_to_blender(vals) for vals in values)
- if node.parent is None:
- 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
+ translation_keyframes = (gltf.loc_gltf_to_blender(vals) for vals in values)
+
+ bind_trans, bind_rot, _ = vnode.trs
+ bind_rot_inv = bind_rot.conjugated()
+
final_translations = [
- inv_bind_matrix @ (parent_mat @ Matrix.Translation(translation_keyframe)).to_translation()
- for translation_keyframe in translation_keyframes
+ bind_rot_inv @ (trans - bind_trans)
+ for trans in translation_keyframes
]
BlenderBoneAnim.fill_fcurves(
@@ -71,48 +76,31 @@ class BlenderBoneAnim():
)
@staticmethod
- def parse_rotation_channel(gltf, node, obj, bone, channel, animation):
+ def parse_rotation_channel(gltf, vnode, obj, bone, channel, animation):
"""Manage rotation animation."""
blender_path = "pose.bones[" + json.dumps(bone.name) + "].rotation_quaternion"
group_name = bone.name
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()
if animation.samplers[channel.sampler].interpolation == "CUBICSPLINE":
# TODO manage tangent?
quat_keyframes = [
- quaternion_gltf_to_blender(values[idx * 3 + 1])
+ gltf.quaternion_gltf_to_blender(values[idx * 3 + 1])
for idx in range(0, len(keys))
]
else:
- quat_keyframes = [quaternion_gltf_to_blender(vals) for vals in values]
+ quat_keyframes = [gltf.quaternion_gltf_to_blender(vals) for vals in values]
+ _, bind_rot, _ = vnode.trs
+ bind_rot_inv = bind_rot.conjugated()
- if node.parent is None:
- final_rots = [
- bind_rotation.inverted() @ quat_keyframe
- for quat_keyframe in quat_keyframes
- ]
- else:
- if not gltf.data.nodes[node
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-extensions-cvs
mailing list