浏览代码

Merge pull request #42 from Jason0214/filter_ctrl_bones

add option to exclude control bones
sdfgeoff 7 年之前
父节点
当前提交
d8cabb02e4

+ 5 - 0
io_scene_godot/__init__.py

@@ -83,6 +83,11 @@ class ExportGodot(bpy.types.Operator, ExportHelper):
                     "layers if that applies).",
         default=False,
         )
+    use_exclude_ctrl_bone = BoolProperty(
+        name="Exclude Control Bones",
+        description="Do not export control bones (bone.use_deform = false)",
+        default=True,
+    )
     use_export_animation = BoolProperty(
         name="Export Animation",
         description="Export all the animation actions (include those "

+ 14 - 4
io_scene_godot/converters/animation.py

@@ -4,6 +4,7 @@ import re
 import copy
 import bpy
 import mathutils
+from . import armature
 from ..structures import (NodeTemplate, NodePath,
                           InternalResource, Array, fix_matrix)
 
@@ -214,6 +215,9 @@ def export_transform_action(godot_node, animation_player,
 
     first_frame, last_frame = get_frame_range(action)
 
+    # if no skeleton node exist, it will be None
+    skeleton_node = armature.find_skeletion_node(godot_node)
+
     transform_frames_map = collections.OrderedDict()
     for fcurve in action.fcurves:
         # fcurve data are seperated into different channels,
@@ -228,10 +232,16 @@ def export_transform_action(godot_node, animation_player,
                 default_frame = None
 
                 if object_path.startswith('pose'):
-                    bone_id = blender_object.pose.bones.find(
-                        blender_path_to_bone_name(object_path)
-                    )
-                    pose_bone = blender_object.pose.bones[bone_id]
+                    bone_name = blender_path_to_bone_name(object_path)
+
+                    # if the correspond bone of this track not exported, skip
+                    if (skeleton_node is None or
+                            skeleton_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

+ 59 - 45
io_scene_godot/converters/armature.py

@@ -34,63 +34,77 @@ def get_armature_data(node):
     return None
 
 
-class Bone:
-    """A Bone has almost same attributes as Godot bones"""
-
-    # must be ordered, Godot scene exporter force the first
-    # attribute of bone be 'name'
-    attributes = (
-        "name",
-        "parent",
-        "rest",
-        "pose",
-        "enabled",
-        "bound_children",
-    )
+def find_skeletion_node(node):
+    """Return the cloest Skeleton from node to root,
+    if not found, return None"""
+    node_ptr = node
+    while (node_ptr is not None and
+           node_ptr.get_type() != "Skeleton"):
+        node_ptr = node_ptr.parent
+    return node_ptr
 
-    def __init__(self, bone_id, name, parent_id):
-        # name needs wrapped by double quotes
-        self.id = bone_id
-        self.name = '"{}"'.format(name)
 
-        # attributes
-        self.parent = parent_id
+class Bone:
+    """A Bone has almost same attributes as Godot bones"""
+    def __init__(self, bone_name, parent_name):
+        # id assigned when add to skeleton
+        self.id = None
+        self.name = bone_name
+        self.parent_name = parent_name
         self.rest = mathutils.Matrix()
         self.pose = mathutils.Matrix()
-        self.enabled = True
-        self.bound_children = Array(prefix='[', suffix=']')
 
-    def attr_to_key(self, attr_name):
-        """Add bone id to bone attribute"""
-        assert attr_name in Bone.attributes
 
-        return "bones/{}/{}".format(self.id, attr_name).lower()
-
-
-def export_bone(pose_bone, self_id, parent_id):
+def export_bone(pose_bone, exclude_ctrl_bone):
     """Convert a Blender bone to a escn bone"""
     bone_name = pose_bone.name
+    parent_bone_name = ""
 
     rest_bone = pose_bone.bone
-    if pose_bone.parent is None:
+    if (pose_bone.parent is None or
+            (exclude_ctrl_bone and not pose_bone.bone.parent.use_deform)):
         rest_mat = rest_bone.matrix_local
     else:
+        parent_bone_name = pose_bone.parent.name
         rest_mat = (rest_bone.parent.matrix_local.inverted_safe() *
                     rest_bone.matrix_local)
     pose_mat = pose_bone.matrix_basis
 
-    bone = Bone(self_id, bone_name, parent_id)
+    bone = Bone(bone_name, parent_bone_name)
     bone.rest = rest_mat
     bone.pose = pose_mat
     return bone
 
 
-def attach_bones_to_skeleton(skeleton_node, bone_list):
-    """Convert Bone list to attributes of skeleton node"""
-    for bone in bone_list:
-        for attr in Bone.attributes:
-            if not getattr(bone, attr) is None:
-                skeleton_node[bone.attr_to_key(attr)] = getattr(bone, attr)
+class SkeletonNode(NodeTemplate):
+    """tscn node with type Skeleton"""
+    def __init__(self, name, parent):
+        super().__init__(name, "Skeleton", parent)
+        self.bone_name_to_id_map = dict()
+
+    def find_bone_id(self, bone_name):
+        """"Given bone name find the bone id in Skeleton node"""
+        return self.bone_name_to_id_map.get(bone_name, -1)
+
+    def add_bones(self, bone_list):
+        """Add a list of bone to skeleton node"""
+        # need first add all bones into name_to_id_map,
+        # otherwise the parent bone finding would be incorrect
+        for bone in bone_list:
+            bone.id = len(self.bone_name_to_id_map)
+            self.bone_name_to_id_map[bone.name] = bone.id
+
+        for bone in bone_list:
+            bone_prefix = 'bones/{}'.format(bone.id)
+
+            # bone name must be the first property
+            self[bone_prefix + '/name'] = '"{}"'.format(bone.name)
+            self[bone_prefix + '/parent'] = self.find_bone_id(bone.parent_name)
+            self[bone_prefix + '/rest'] = bone.rest
+            self[bone_prefix + '/pose'] = bone.pose
+            self[bone_prefix + '/enabled'] = True
+            self[bone_prefix + '/bound_children'] = Array(
+                prefix='[', suffix=']')
 
 
 def export_armature_node(escn_file, export_settings, node, parent_gd_node):
@@ -98,18 +112,18 @@ def export_armature_node(escn_file, export_settings, node, parent_gd_node):
     if "ARMATURE" not in export_settings['object_types']:
         return parent_gd_node
 
-    skeleton_node = NodeTemplate(node.name, "Skeleton", parent_gd_node)
+    skeleton_node = SkeletonNode(node.name, parent_gd_node)
     skeleton_node['transform'] = node.matrix_local
 
     bone_list = list()
-    for index, pose_bone in enumerate(node.pose.bones):
-        if pose_bone.parent is None:
-            parent_id = -1
-        else:
-            parent_id = node.pose.bones.find(pose_bone.parent.name)
-        bone_list.append(export_bone(pose_bone, index, parent_id))
-
-    attach_bones_to_skeleton(skeleton_node, bone_list)
+    for pose_bone in node.pose.bones:
+        if (export_settings["use_exclude_ctrl_bone"] and
+                pose_bone.bone.use_deform):
+            bone = export_bone(
+                pose_bone, export_settings["use_exclude_ctrl_bone"])
+            bone_list.append(bone)
+
+    skeleton_node.add_bones(bone_list)
 
     escn_file.add_node(skeleton_node)
 

+ 14 - 17
io_scene_godot/converters/mesh.py

@@ -37,18 +37,13 @@ def export_mesh_node(escn_file, export_settings, node, parent_gd_node):
         skeleton_node = None
         if ("ARMATURE" in export_settings['object_types'] and
                 armature_data is not None):
-            # trace up the godot scene tree to find the binded skeleton node
-            gd_node_ptr = parent_gd_node
-            while (gd_node_ptr is not None and
-                   gd_node_ptr.get_type() != "Skeleton"):
-                gd_node_ptr = gd_node_ptr.get_parent()
-            skeleton_node = gd_node_ptr
+            skeleton_node = armature.find_skeletion_node(parent_gd_node)
 
         mesh_id = export_mesh(
             escn_file,
             export_settings,
-            node,
-            armature_data
+            skeleton_node,
+            node
         )
 
         mesh_node = NodeTemplate(node.name, "MeshInstance", parent_gd_node)
@@ -66,7 +61,7 @@ def export_mesh_node(escn_file, export_settings, node, parent_gd_node):
         return mesh_node
 
 
-def export_mesh(escn_file, export_settings, node, armature_data):
+def export_mesh(escn_file, export_settings, skeleton_node, node):
     """Saves a mesh into the escn file """
     # Check if it exists so we don't bother to export it twice
     mesh = node.data
@@ -80,8 +75,8 @@ def export_mesh(escn_file, export_settings, node, armature_data):
     surfaces = make_arrays(
         escn_file,
         export_settings,
-        node,
-        armature_data)
+        skeleton_node,
+        node)
 
     if export_settings['export_shape_key'] and node.data.shape_keys:
         mesh_resource["blend_shape/names"] = Array(prefix="PoolStringArray(")
@@ -110,21 +105,22 @@ def triangulate_mesh(mesh):
     mesh.update(calc_tessface=True)
 
 
-def find_bone_vertex_groups(vertex_groups, armature_data):
+def find_bone_vertex_groups(vertex_groups, skeleton_node):
     """Find the id of vertex groups connected to bone weights,
     return a dict() mapping from vertex_group id to bone id"""
     ret = dict()
-    if armature_data is not None:
+    if skeleton_node is not None:
         # the bone's index in the bones list is exported as the id
-        for bone_id, bone in enumerate(armature_data.bones):
-            group = vertex_groups.get(bone.name)
+        for bone_name, bone_id in skeleton_node.bone_name_to_id_map.items():
+            group = vertex_groups.get(bone_name)
             if group is not None:
                 ret[group.index] = bone_id
     return ret
 
 
-def make_arrays(escn_file, export_settings, node, armature_data):
+def make_arrays(escn_file, export_settings, skeleton_node, node):
     """Generates arrays of positions, normals etc"""
+    armature_data = armature.get_armature_data(node)
     if armature_data is not None:
         original_pose_position = armature_data.pose_position
         armature_data.pose_position = 'REST'
@@ -149,7 +145,8 @@ def make_arrays(escn_file, export_settings, node, armature_data):
         has_tangents = False
 
     # find the vertex group id of bone weights
-    gid_to_bid_map = find_bone_vertex_groups(node.vertex_groups, armature_data)
+    gid_to_bid_map = find_bone_vertex_groups(
+        node.vertex_groups, skeleton_node)
 
     # Separate by materials into single-material surfaces
     surfaces = generate_surfaces(

文件差异内容过多而无法显示
+ 9 - 0
tests/reference_exports/armature_with_non_deform_bone.escn


二进制
tests/test_scenes/armature_with_non_deform_bone.blend


部分文件因为文件数量过多而无法显示