Browse Source

Fix bone attachment transform (which has a difference of bone length
between godot and blender )

Jason0214 6 years ago
parent
commit
b2f6d903f3

+ 64 - 45
io_scene_godot/converters/animation.py

@@ -9,7 +9,8 @@ import bpy
 import bpy_extras.anim_utils
 import bpy_extras.anim_utils
 import mathutils
 import mathutils
 from ..structures import (NodeTemplate, NodePath, fix_directional_transform,
 from ..structures import (NodeTemplate, NodePath, fix_directional_transform,
-                          InternalResource, Array, Map, fix_matrix)
+                          InternalResource, Array, Map, fix_matrix,
+                          fix_bone_attachment_transform)
 
 
 NEAREST_INTERPOLATION = 0
 NEAREST_INTERPOLATION = 0
 LINEAR_INTERPOLATION = 1
 LINEAR_INTERPOLATION = 1
@@ -465,6 +466,45 @@ def export_transform_action(godot_node, animation_player,
             )).to_4x4()
             )).to_4x4()
             return loc_mat * rot_mat * sca_mat
             return loc_mat * rot_mat * sca_mat
 
 
+    def init_transform_frame_values(object_path, blender_object, godot_node,
+                                    first_frame, last_frame):
+        """Initialize a list of TransformFrame for every animated object"""
+        if object_path.startswith('pose'):
+            bone_name = blender_path_to_bone_name(object_path)
+
+            # bone fcurve in a non armature object
+            if godot_node.get_type() != 'Skeleton':
+                logging.warning(
+                    "Skip a bone fcurve in a non-armature "
+                    "object '%s'",
+                    blender_object.name
+                )
+                return None
+
+            # if the correspond bone of this track not exported, skip
+            if godot_node.find_bone_id(bone_name) == -1:
+                return None
+
+            pose_bone = blender_object.pose.bones[
+                blender_object.pose.bones.find(bone_name)
+            ]
+
+            default_frame = TransformFrame(
+                pose_bone.matrix_basis,
+                pose_bone.rotation_mode
+            )
+        else:
+            # the fcurve location is matrix_basis.to_translation()
+            default_frame = TransformFrame(
+                blender_object.matrix_basis,
+                blender_object.rotation_mode
+            )
+
+        return [
+            copy.deepcopy(default_frame)
+            for _ in range(last_frame - first_frame)
+        ]
+
     first_frame, last_frame = get_action_frame_range(action)
     first_frame, last_frame = get_action_frame_range(action)
 
 
     transform_frame_values_map = collections.OrderedDict()
     transform_frame_values_map = collections.OrderedDict()
@@ -478,45 +518,18 @@ def export_transform_action(godot_node, animation_player,
         if (object_path not in transform_frame_values_map and
         if (object_path not in transform_frame_values_map and
                 attribute in TransformFrame.ATTRIBUTES):
                 attribute in TransformFrame.ATTRIBUTES):
 
 
-            default_frame = None
-
-            # the fcurve location is matrix_basis.to_translation()
-            default_frame = TransformFrame(
-                blender_object.matrix_basis,
-                blender_object.rotation_mode
+            frame_values = init_transform_frame_values(
+                object_path, blender_object,
+                godot_node, first_frame, last_frame
             )
             )
 
 
-            if object_path.startswith('pose'):
-                bone_name = blender_path_to_bone_name(object_path)
-
-                # bone fcurve in a non armature object
-                if godot_node.get_type() != 'Skeleton':
-                    logging.warning(
-                        "Skip a bone fcurve in a non-armature "
-                        "object '%s'",
-                        blender_object.name
-                    )
-                    continue
+            # unsuccessfully initialize frames, then skip this fcurve
+            if not frame_values:
+                continue
 
 
-                    # if the correspond bone of this track not exported, skip
-                if godot_node.find_bone_id(bone_name) == -1:
-                    continue
-
-                pose_bone = blender_object.pose.bones[
-                    blender_object.pose.bones.find(bone_name)
-                ]
-                default_frame = TransformFrame(
-                    pose_bone.matrix_basis,
-                    pose_bone.rotation_mode
-                )
-
-            transform_frame_values_map[object_path] = [
-                copy.deepcopy(default_frame)
-                for _ in range(last_frame - first_frame)
-            ]
+            transform_frame_values_map[object_path] = frame_values
 
 
         if attribute in TransformFrame.ATTRIBUTES:
         if attribute in TransformFrame.ATTRIBUTES:
-
             for frame in range(first_frame, last_frame):
             for frame in range(first_frame, last_frame):
                 transform_frame_values_map[
                 transform_frame_values_map[
                     object_path][frame - first_frame].update(
                     object_path][frame - first_frame].update(
@@ -528,21 +541,27 @@ def export_transform_action(godot_node, animation_player,
     for object_path, frame_value_list in transform_frame_values_map.items():
     for object_path, frame_value_list in transform_frame_values_map.items():
         if object_path == '':
         if object_path == '':
             # object_path equals '' represents node itself
             # object_path equals '' represents node itself
-
-            # convert matrix_basis to matrix_local(parent space transform)
-            if (godot_node.get_type()
-                    in ("SpotLight", "DirectionalLight", "Camera")):
-                normalized_value_list = [
-                    fix_directional_transform(
-                        blender_object.matrix_parent_inverse * x.to_matrix()
+            if godot_node.parent.get_type() == 'BoneAttachment':
+                transform_mtx_list = [
+                    fix_bone_attachment_transform(
+                        blender_object,
+                        blender_object.matrix_parent_inverse * x.to_matrix
                     ) for x in frame_value_list
                     ) for x in frame_value_list
                 ]
                 ]
             else:
             else:
-                normalized_value_list = [
+                transform_mtx_list = [
                     blender_object.matrix_parent_inverse *
                     blender_object.matrix_parent_inverse *
                     x.to_matrix() for x in frame_value_list
                     x.to_matrix() for x in frame_value_list
                 ]
                 ]
 
 
+            # convert matrix_basis to matrix_local(parent space transform)
+            if (godot_node.get_type()
+                    in ("SpotLight", "DirectionalLight", "Camera")):
+                transform_mtx_list = [
+                    fix_directional_transform(mtx)
+                    for mtx in transform_mtx_list
+                ]
+
             track_path = NodePath(
             track_path = NodePath(
                 animation_player.parent.get_path(),
                 animation_player.parent.get_path(),
                 godot_node.get_path()
                 godot_node.get_path()
@@ -555,14 +574,14 @@ def export_transform_action(godot_node, animation_player,
                 blender_path_to_bone_name(object_path)
                 blender_path_to_bone_name(object_path)
             )
             )
 
 
-            normalized_value_list = [x.to_matrix() for x in frame_value_list]
+            transform_mtx_list = [x.to_matrix() for x in frame_value_list]
 
 
         animation_resource.add_track(
         animation_resource.add_track(
             Track(
             Track(
                 'transform',
                 'transform',
                 track_path,
                 track_path,
                 range(first_frame, last_frame),
                 range(first_frame, last_frame),
-                normalized_value_list
+                transform_mtx_list
             )
             )
         )
         )
 
 

+ 8 - 0
io_scene_godot/export_godot.py

@@ -97,8 +97,10 @@ class GodotExporter:
             )
             )
             exporter = converters.BLENDER_TYPE_TO_EXPORTER["EMPTY"]
             exporter = converters.BLENDER_TYPE_TO_EXPORTER["EMPTY"]
 
 
+        is_bone_attachment = False
         if ("ARMATURE" in self.config['object_types'] and
         if ("ARMATURE" in self.config['object_types'] and
                 node.parent_bone != ''):
                 node.parent_bone != ''):
+            is_bone_attachment = True
             parent_gd_node = converters.BONE_ATTACHMENT_EXPORTER(
             parent_gd_node = converters.BONE_ATTACHMENT_EXPORTER(
                 self.escn_file,
                 self.escn_file,
                 node,
                 node,
@@ -109,6 +111,12 @@ class GodotExporter:
         exported_node = exporter(self.escn_file, self.config, node,
         exported_node = exporter(self.escn_file, self.config, node,
                                  parent_gd_node)
                                  parent_gd_node)
 
 
+        if is_bone_attachment:
+            for child in parent_gd_node.children:
+                child['transform'] = structures.fix_bone_attachment_transform(
+                    node, child['transform']
+                )
+
         # if the blender node is exported and it has animation data
         # if the blender node is exported and it has animation data
         if exported_node != parent_gd_node:
         if exported_node != parent_gd_node:
             converters.ANIMATION_DATA_EXPORTER(
             converters.ANIMATION_DATA_EXPORTER(

+ 12 - 0
io_scene_godot/structures.py

@@ -358,6 +358,18 @@ def fix_directional_transform(mtx):
     return mtx * _AXIS_CORRECT
     return mtx * _AXIS_CORRECT
 
 
 
 
+def fix_bone_attachment_transform(attachment_obj, blender_transform):
+    """Godot and blender bone children nodes' transform relative to
+    different bone joints, so there is a difference of bone_length
+    along bone direction axis"""
+    armature_obj = attachment_obj.parent
+    bone_length = armature_obj.data.bones[attachment_obj.parent_bone].length
+    # sometimes this transform could be read-only, so copy is required
+    mtx = copy.copy(blender_transform)
+    mtx[1][3] += bone_length
+    return mtx
+
+
 # ------------------ Implicit Conversions of Blender Types --------------------
 # ------------------ Implicit Conversions of Blender Types --------------------
 def mat4_to_string(mtx):
 def mat4_to_string(mtx):
     """Converts a matrix to a "Transform" string that can be parsed by Godot"""
     """Converts a matrix to a "Transform" string that can be parsed by Godot"""

+ 1 - 1
tests/reference_exports/armature/armature_bone_attachment.escn

@@ -57,4 +57,4 @@ bone_name = "Bone.003"
 
 
 mesh = SubResource(1)
 mesh = SubResource(1)
 visible = true
 visible = true
-transform = Transform(1.0, 0.0, 0.0, 0.0, 1.86265e-09, 1.0, 0.0, -1.0, 0.0, 0.0219986, -0.0343127, 2.25595)
+transform = Transform(1.0, 0.0, 0.0, 0.0, 1.86265e-09, 1.0, 0.0, -1.0, 0.0, 0.0219986, -0.0343127, 1.44506)

File diff suppressed because it is too large
+ 10 - 0
tests/reference_exports/armature/armature_bone_attachment_ik.escn


BIN
tests/test_scenes/armature/armature_bone_attachment_ik.blend


Some files were not shown because too many files changed in this diff