[Bf-extensions-cvs] [6e7dfdd8] master: glTF importer: prettify bones: manage more readable bone rotation

Julien Duroure noreply at git.blender.org
Mon Mar 9 16:23:17 CET 2020


Commit: 6e7dfdd8a91fdccae321f6192dd22accb5bc2426
Author: Julien Duroure
Date:   Mon Mar 9 16:20:54 2020 +0100
Branches: master
https://developer.blender.org/rBA6e7dfdd8a91fdccae321f6192dd22accb5bc2426

glTF importer: prettify bones: manage more readable bone rotation

Thanks to scurest!

===================================================================

M	io_scene_gltf2/__init__.py
M	io_scene_gltf2/blender/com/gltf2_blender_math.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_node.py
M	io_scene_gltf2/blender/imp/gltf2_blender_vnode.py

===================================================================

diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py
index 95e24933..f9ad9b8d 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, 36),
+    "version": (1, 2, 37),
     'blender': (2, 82, 7),
     'location': 'File > Import-Export',
     'description': 'Import-Export as glTF 2.0',
@@ -856,11 +856,26 @@ class ImportGLTF2(Operator, ImportHelper):
         description="How normals are computed during import",
         default="NORMALS")
 
+    bone_heuristic: EnumProperty(
+        name="Bone Dir",
+        items=(
+            ("BLENDER", "Blender (+Y)",
+                "Round-trips bone directions in glTFs exported from Blender.\n"
+                "Bone tips are placed on their local +Y axis (in glTF space)"),
+            ("TEMPERANCE", "Temperance",
+                "Okay for many different models.\n"
+                "Bone tips are placed at a child's root")
+        ),
+        description="Heuristic for placing bones. Tries to make bones pretty",
+        default="TEMPERANCE",
+    )
+
     def draw(self, context):
         layout = self.layout
 
         layout.prop(self, 'import_pack_images')
         layout.prop(self, 'import_shading')
+        layout.prop(self, 'bone_heuristic')
 
     def execute(self, context):
         return self.import_gltf2(context)
diff --git a/io_scene_gltf2/blender/com/gltf2_blender_math.py b/io_scene_gltf2/blender/com/gltf2_blender_math.py
index fb342bc4..b3f13f65 100755
--- a/io_scene_gltf2/blender/com/gltf2_blender_math.py
+++ b/io_scene_gltf2/blender/com/gltf2_blender_math.py
@@ -170,3 +170,45 @@ def transform_value(value: Vector, _: Matrix = Matrix.Identity(4)) -> Vector:
 def round_if_near(value: float, target: float) -> float:
     """If value is very close to target, round to target."""
     return value if abs(value - target) > 2.0e-6 else target
+
+def scale_rot_swap_matrix(rot):
+    """Returns a matrix m st. Scale[s] Rot[rot] = Rot[rot] Scale[m s].
+    If rot.to_matrix() is a signed permutation matrix, works for any s.
+    Otherwise works only if s is a uniform scaling.
+    """
+    m = nearby_signed_perm_matrix(rot)  # snap to signed perm matrix
+    m.transpose()  # invert permutation
+    for i in range(3):
+        for j in range(3):
+            m[i][j] = abs(m[i][j])  # discard sign
+    return m
+
+def nearby_signed_perm_matrix(rot):
+    """Returns a signed permutation matrix close to rot.to_matrix().
+    (A signed permutation matrix is like a permutation matrix, except
+    the non-zero entries can be ±1.)
+    """
+    m = rot.to_matrix()
+    x, y, z = m[0], m[1], m[2]
+
+    # Set the largest entry in the first row to ±1
+    a, b, c = abs(x[0]), abs(x[1]), abs(x[2])
+    i = 0 if a >= b and a >= c else 1 if b >= c else 2
+    x[i] = 1 if x[i] > 0 else -1
+    x[(i+1) % 3] = 0
+    x[(i+2) % 3] = 0
+
+    # Same for second row: only two columns to consider now.
+    a, b = abs(y[(i+1) % 3]), abs(y[(i+2) % 3])
+    j = (i+1) % 3 if a >= b else (i+2) % 3
+    y[j] = 1 if y[j] > 0 else -1
+    y[(j+1) % 3] = 0
+    y[(j+2) % 3] = 0
+
+    # Same for third row: only one column left
+    k = (0 + 1 + 2) - i - j
+    z[k] = 1 if z[k] > 0 else -1
+    z[(k+1) % 3] = 0
+    z[(k+2) % 3] = 0
+
+    return m
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 62c179c1..6f65de43 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_animation_bone.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_animation_bone.py
@@ -58,12 +58,14 @@ class BlenderBoneAnim():
         else:
             translation_keyframes = (gltf.loc_gltf_to_blender(vals) for vals in values)
 
+        final_translations = vnode.base_locs_to_final_locs(translation_keyframes)
+
         # Calculate pose bone trans from final bone trans
         edit_trans, edit_rot = vnode.editbone_trans, vnode.editbone_rot
         edit_rot_inv = edit_rot.conjugated()
         pose_translations = [
             edit_rot_inv @ (trans - edit_trans)
-            for trans in translation_keyframes
+            for trans in final_translations
         ]
 
         BlenderBoneAnim.fill_fcurves(
@@ -93,12 +95,14 @@ class BlenderBoneAnim():
         else:
             quat_keyframes = [gltf.quaternion_gltf_to_blender(vals) for vals in values]
 
+        final_rots = vnode.base_rots_to_final_rots(quat_keyframes)
+
         # Calculate pose bone rotation from final bone rotation
         edit_rot = vnode.editbone_rot
         edit_rot_inv = edit_rot.conjugated()
         pose_rots = [
             edit_rot_inv @ rot
-            for rot in quat_keyframes
+            for rot in final_rots
         ]
 
         # Manage antipodal quaternions
@@ -133,10 +137,13 @@ class BlenderBoneAnim():
         else:
             scale_keyframes = [gltf.scale_gltf_to_blender(vals) for vals in values]
 
+        final_scales = vnode.base_scales_to_final_scales(scale_keyframes)
+        pose_scales = final_scales  # no change needed
+
         BlenderBoneAnim.fill_fcurves(
             obj.animation_data.action,
             keys,
-            scale_keyframes,
+            pose_scales,
             group_name,
             blender_path,
             animation.samplers[channel.sampler].interpolation
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_animation_node.py b/io_scene_gltf2/blender/imp/gltf2_blender_animation_node.py
index a0205483..b6369b8b 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_animation_node.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_animation_node.py
@@ -74,20 +74,22 @@ class BlenderNodeAnim():
                 blender_path = "location"
                 group_name = "Location"
                 num_components = 3
+                values = [gltf.loc_gltf_to_blender(vals) for vals in values]
+                values = vnode.base_locs_to_final_locs(values)
 
                 if vnode.parent is not None and gltf.vnodes[vnode.parent].type == VNode.Bone:
                     # Nodes with a bone parent need to be translated
-                    # backwards by their bone length (always 1 currently)
-                    off = Vector((0, -1, 0))
-                    values = [gltf.loc_gltf_to_blender(vals) + off for vals in values]
-                else:
-                    values = [gltf.loc_gltf_to_blender(vals) for vals in values]
+                    # backwards from the tip to the root
+                    bone_length = gltf.vnodes[vnode.parent].bone_length
+                    off = Vector((0, -bone_length, 0))
+                    values = [vals + off for vals in values]
 
             elif channel.target.path == "rotation":
                 blender_path = "rotation_quaternion"
                 group_name = "Rotation"
                 num_components = 4
                 values = [gltf.quaternion_gltf_to_blender(vals) for vals in values]
+                values = vnode.base_rots_to_final_rots(values)
 
                 # Manage antipodal quaternions
                 for i in range(1, len(values)):
@@ -99,6 +101,7 @@ class BlenderNodeAnim():
                 group_name = "Scale"
                 num_components = 3
                 values = [gltf.scale_gltf_to_blender(vals) for vals in values]
+                values = vnode.base_scales_to_final_scales(values)
 
             coords = [0] * (2 * len(keys))
             coords[::2] = (key[0] * fps for key in keys)
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_node.py b/io_scene_gltf2/blender/imp/gltf2_blender_node.py
index d1ffdbe9..6ce91ea3 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_node.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_node.py
@@ -78,7 +78,7 @@ class BlenderNode():
             set_extras(obj, pynode.extras)
 
         # Set transform
-        trans, rot, scale = vnode.trs
+        trans, rot, scale = vnode.trs()
         obj.location = trans
         obj.rotation_mode = 'QUATERNION'
         obj.rotation_quaternion = rot
@@ -96,8 +96,8 @@ class BlenderNode():
                 obj.parent_bone = parent_vnode.blender_bone_name
 
                 # Nodes with a bone parent need to be translated
-                # backwards by their bone length (always 1 currently)
-                obj.location += Vector((0, -1, 0))
+                # backwards from the tip to the root
+                obj.location += Vector((0, -parent_vnode.bone_length, 0))
 
         bpy.data.scenes[gltf.blender_scene].collection.objects.link(obj)
 
@@ -138,6 +138,7 @@ class BlenderNode():
             editbone.head = arma_mat @ Vector((0, 0, 0))
             editbone.tail = arma_mat @ Vector((0, 1, 0))
             editbone.align_roll(arma_mat @ Vector((0, 0, 1)) - editbone.head)
+            editbone.length = vnode.bone_length
 
             if isinstance(id, int):
                 pynode = gltf.data.nodes[id]
@@ -161,7 +162,7 @@ class BlenderNode():
 
             # BoneTRS = EditBone * PoseBone
             # Set PoseBone to make BoneTRS = vnode.trs.
-            t, r, s = vnode.trs
+            t, r, s = vnode.trs()
             et, er = vnode.editbone_trans, vnode.editbone_rot
             pose_bone.location = er.conjugated() @ (t - et)
             pose_bone.rotation_mode = 'QUATERNION'
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_vnode.py b/io_scene_gltf2/blender/imp/gltf2_blender_vnode.py
index bd5edcd1..554c09e9 100644
--- a/io_scene_gltf2/blender/imp/gltf2_blender_vnode.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_vnode.py
@@ -15,6 +15,8 @@
 import bpy
 from mathutils import Vector, Quaternion, Matrix
 
+from ..com.gltf2_blender_math import scale_rot_swap_matrix, nearby_signed_perm_matrix
+
 def compute_vnodes(gltf):
     """Computes the tree of virtual nodes.
     Copies the glTF nodes into a tree of VNodes, then performs a series of
@@ -26,6 +28,7 @@ def compute_vnodes(gltf):
     fixup_multitype_nodes(gltf)
     correct_cameras_and_lights(gltf)
     pick_bind_pose(gltf)
+    prettify_bones(gltf)
     calc_

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list