فهرست منبع

small refactor, use NodeTemplate instead of path to keep track of the
tree relation of Godot scene during export

Jason0214 7 سال پیش
والد
کامیت
c3e78389a8

+ 3 - 3
io_scene_godot/converters/__init__.py

@@ -3,10 +3,10 @@ This file provides the conversion for a single blend object into one or
 more godot nodes. All the converters should take as input arguments:
  - The ESCN file (so you can use add_internal_resource() method etc.)
  - The exporter config (so you can see what options the user selected)
- - The node to export
- - The path to the parent node
+ - The blender node to export
+ - The parent Godot scene node of the node being processed
 
-All converters that convert nodes should return the path to the node. All
+All converters that convert nodes should return the node itself. All
 converters that convert resources should return the resource ID. Additional,
 converters for resources should have internal protection against importing
 twice

+ 7 - 7
io_scene_godot/converters/mesh.py

@@ -9,23 +9,23 @@ from . import physics
 
 
 # ------------------------------- The Mesh -----------------------------------
-def export_mesh_node(escn_file, export_settings, node, parent_path):
+def export_mesh_node(escn_file, export_settings, node, parent_gd_node):
     """Exports a MeshInstance. If the mesh is not already exported, it will
     trigger the export of that mesh"""
     if (node.data is None or
             "MESH" not in export_settings['object_types']):
-        return parent_path
+        return parent_gd_node
 
     # If this mesh object has physics properties, we need to export them first
     # because they need to be higher in the scene-tree
     if physics.has_physics(node):
-        parent_path = physics.export_physics_properties(
-            escn_file, export_settings, node, parent_path
+        parent_gd_node = physics.export_physics_properties(
+            escn_file, export_settings, node, parent_gd_node
         )
 
     if (node.hide_render or
             (physics.has_physics(node) and node.draw_type == "WIRE")):
-        return parent_path
+        return parent_gd_node
 
     else:
         armature = None
@@ -34,7 +34,7 @@ def export_mesh_node(escn_file, export_settings, node, parent_path):
 
         mesh_id = export_mesh(escn_file, export_settings, node, armature)
 
-        mesh_node = NodeTemplate(node.name, "MeshInstance", parent_path)
+        mesh_node = NodeTemplate(node.name, "MeshInstance", parent_gd_node)
         mesh_node['mesh'] = "SubResource({})".format(mesh_id)
         mesh_node['visible'] = not node.hide
         if not physics.has_physics(node) or not physics.is_physics_root(node):
@@ -43,7 +43,7 @@ def export_mesh_node(escn_file, export_settings, node, parent_path):
             mesh_node['transform'] = mathutils.Matrix.Identity(4)
         escn_file.add_node(mesh_node)
 
-        return parent_path + '/' + node.name
+        return mesh_node
 
 
 def export_mesh(escn_file, export_settings, node, armature):

+ 18 - 17
io_scene_godot/converters/physics.py

@@ -14,6 +14,7 @@ from ..structures import NodeTemplate, InternalResource, Array
 
 
 AXIS_CORRECT = mathutils.Matrix.Rotation(math.radians(-90), 4, 'X')
+PHYSICS_TYPES = {'KinematicBody', 'RigidBody', 'StaticBody'}
 
 
 def has_physics(node):
@@ -58,11 +59,11 @@ def get_extents(node):
     return maxs - mins
 
 
-def export_collision_shape(escn_file, export_settings, node, parent_path,
+def export_collision_shape(escn_file, export_settings, node, parent_gd_node,
                            parent_override=None):
     """Exports the collision primitives/geometry"""
     col_name = node.name + 'Collision'
-    col_node = NodeTemplate(col_name, "CollisionShape", parent_path)
+    col_node = NodeTemplate(col_name, "CollisionShape", parent_gd_node)
 
     if parent_override is None:
         col_node['transform'] = mathutils.Matrix.Identity(4) * AXIS_CORRECT
@@ -107,7 +108,7 @@ def export_collision_shape(escn_file, export_settings, node, parent_path,
         col_node['shape'] = "SubResource({})".format(shape_id)
     escn_file.add_node(col_node)
 
-    return parent_path + "/" + col_name
+    return col_node
 
 
 def generate_convex_mesh_array(escn_file, export_settings, node):
@@ -177,7 +178,7 @@ def generate_triangle_mesh_array(escn_file, export_settings, node):
     return escn_file.add_internal_resource(col_shape, key)
 
 
-def export_physics_controller(escn_file, export_settings, node, parent_path):
+def export_physics_controller(escn_file, export_settings, node, parent_gd_node):
     """Exports the physics body "type" as a separate node. In blender, the
     physics body type and the collision shape are one object, in godot they
     are two. This is the physics body type"""
@@ -192,7 +193,7 @@ def export_physics_controller(escn_file, export_settings, node, parent_path):
     else:
         phys_controller = 'StaticBody'
 
-    phys_obj = NodeTemplate(phys_name, phys_controller, parent_path)
+    phys_obj = NodeTemplate(phys_name, phys_controller, parent_gd_node)
 
     #  OPTIONS FOR ALL PHYSICS TYPES
     phys_obj['friction'] = rbd.friction
@@ -214,27 +215,27 @@ def export_physics_controller(escn_file, export_settings, node, parent_path):
 
     escn_file.add_node(phys_obj)
 
-    return parent_path + '/' + phys_name
+    return phys_obj
 
 
-def export_physics_properties(escn_file, export_settings, node, parent_path):
+def export_physics_properties(escn_file, export_settings, node, parent_gd_node):
     """Creates the necessary nodes for the physics"""
     parent_rbd = get_physics_root(node)
 
     if parent_rbd is None:
-        parent_path = export_physics_controller(
-            escn_file, export_settings, node, parent_path
+        parent_gd_node = export_physics_controller(
+            escn_file, export_settings, node, parent_gd_node
         )
-    if parent_rbd is None:
-        tmp_parent_path = parent_path
-    else:
-        search_str = "/{}Physics".format(parent_rbd.name)
-        start_path = parent_path.rsplit(search_str, 1)[0]
-        tmp_parent_path = start_path + search_str
+
+    # trace the path towards root, find the cloest physics node
+    gd_node_ptr = parent_gd_node
+    while gd_node_ptr.get_type() not in PHYSICS_TYPES:
+        gd_node_ptr = gd_node_ptr.parent
+    physics_gd_node = gd_node_ptr
 
     export_collision_shape(
-        escn_file, export_settings, node, tmp_parent_path,
+        escn_file, export_settings, node, physics_gd_node,
         parent_override=parent_rbd
     )
 
-    return parent_path
+    return parent_gd_node

+ 14 - 14
io_scene_godot/converters/simple_nodes.py

@@ -13,24 +13,24 @@ from ..structures import NodeTemplate
 AXIS_CORRECT = mathutils.Matrix.Rotation(math.radians(-90), 4, 'X')
 
 
-def export_empty_node(escn_file, export_settings, node, parent_path):
+def export_empty_node(escn_file, export_settings, node, parent_gd_node):
     """Converts an empty (or any unknown node) into a spatial"""
     if "EMPTY" not in export_settings['object_types']:
-        return parent_path
-    empty_node = NodeTemplate(node.name, "Spatial", parent_path)
+        return parent_gd_node
+    empty_node = NodeTemplate(node.name, "Spatial", parent_gd_node)
     empty_node['transform'] = node.matrix_local
     escn_file.add_node(empty_node)
 
-    return parent_path + '/' + node.name
+    return empty_node
 
 
-def export_camera_node(escn_file, export_settings, node, parent_path):
+def export_camera_node(escn_file, export_settings, node, parent_gd_node):
     """Exports a camera"""
     if (node.data is None or node.hide_render or
             "CAMERA" not in export_settings['object_types']):
-        return parent_path
+        return parent_gd_node
 
-    cam_node = NodeTemplate(node.name, "Camera", parent_path)
+    cam_node = NodeTemplate(node.name, "Camera", parent_gd_node)
     camera = node.data
 
     cam_node['far'] = camera.clip_end
@@ -46,20 +46,20 @@ def export_camera_node(escn_file, export_settings, node, parent_path):
     cam_node['transform'] = node.matrix_local * AXIS_CORRECT
     escn_file.add_node(cam_node)
 
-    return parent_path + '/' + node.name
+    return cam_node
 
 
-def export_lamp_node(escn_file, export_settings, node, parent_path):
+def export_lamp_node(escn_file, export_settings, node, parent_gd_node):
     """Exports lights - well, the ones it knows about. Other light types
     just throw a warning"""
     if (node.data is None or node.hide_render or
             "LAMP" not in export_settings['object_types']):
-        return parent_path
+        return parent_gd_node
 
     light = node.data
 
     if light.type == "POINT":
-        light_node = NodeTemplate(node.name, "OmniLight", parent_path)
+        light_node = NodeTemplate(node.name, "OmniLight", parent_gd_node)
         light_node['omni_range'] = light.distance
         light_node['shadow_enabled'] = light.shadow_method != "NOSHADOW"
 
@@ -69,7 +69,7 @@ def export_lamp_node(escn_file, export_settings, node, parent_path):
             )
 
     elif light.type == "SPOT":
-        light_node = NodeTemplate(node.name, "SpotLight", parent_path)
+        light_node = NodeTemplate(node.name, "SpotLight", parent_gd_node)
         light_node['spot_range'] = light.distance
         light_node['spot_angle'] = math.degrees(light.spot_size/2)
         light_node['spot_angle_attenuation'] = 0.2/(light.spot_blend + 0.01)
@@ -81,7 +81,7 @@ def export_lamp_node(escn_file, export_settings, node, parent_path):
             )
 
     elif light.type == "SUN":
-        light_node = NodeTemplate(node.name, "DirectionalLight", parent_path)
+        light_node = NodeTemplate(node.name, "DirectionalLight", parent_gd_node)
         light_node['shadow_enabled'] = light.shadow_method != "NOSHADOW"
     else:
         light_node = None
@@ -99,4 +99,4 @@ def export_lamp_node(escn_file, export_settings, node, parent_path):
 
         escn_file.add_node(light_node)
 
-    return parent_path + '/' + node.name
+    return light_node

+ 6 - 10
io_scene_godot/export_godot.py

@@ -52,7 +52,7 @@ def find_godot_project_dir(export_path):
 class GodotExporter:
     """Handles picking what nodes to export and kicks off the export process"""
 
-    def export_node(self, node, parent_path):
+    def export_node(self, node, parent_gd_node):
         """Recursively export a node. It calls the export_node function on
         all of the nodes children. If you have heirarchies more than 1000 nodes
         deep, this will fail with a recursion error"""
@@ -73,10 +73,10 @@ class GodotExporter:
             exporter = converters.BLENDER_TYPE_TO_EXPORTER["EMPTY"]
 
         # Perform the export
-        parent_path = exporter(self.escn_file, self.config, node, parent_path)
+        parent_gd_node = exporter(self.escn_file, self.config, node, parent_gd_node)
 
         for child in node.children:
-            self.export_node(child, parent_path)
+            self.export_node(child, parent_gd_node)
 
         bpy.context.scene.objects.active = prev_node
 
@@ -102,12 +102,8 @@ class GodotExporter:
     def export_scene(self):
         """Decide what objects to export, and export them!"""
         # Scene root
-        self.escn_file.add_node(structures.FileEntry(
-            "node", collections.OrderedDict((
-                ("type", "Spatial"),
-                ("name", self.scene.name)
-            ))
-        ))
+        root_gd_node = structures.NodeTemplate(self.scene.name, "Spatial", None)
+        self.escn_file.add_node(root_gd_node)
         logging.info("Exporting scene: %s", self.scene.name)
 
         # Decide what objects to export
@@ -126,7 +122,7 @@ class GodotExporter:
 
         for obj in self.scene.objects:
             if obj in self.valid_nodes and obj.parent is None:
-                self.export_node(obj, ".")
+                self.export_node(obj, root_gd_node)
 
     def export(self):
         """Begin the export"""

+ 48 - 13
io_scene_godot/structures.py

@@ -147,19 +147,54 @@ class NodeTemplate(FileEntry):
     This is a template node that can be used to contruct nodes of any type.
     It is not intended that other classes in the exporter inherit from this,
     but rather that all the exported nodes use this template directly."""
-    def __init__(self, name, node_type, parent_path):
-        if parent_path.startswith("./"):
-            parent_path = parent_path[2:]
-
-        super().__init__(
-            "node",
-            collections.OrderedDict((
-                ("name", name),
-                ("type", node_type),
-                ("parent", parent_path)
-            ))
-        )
-
+    def __init__(self, name, node_type, parent_node):
+        # set child, parent relation
+        self.children = []
+        self.parent = parent_node
+
+        node_name = name.replace('.', '').replace('/', '')
+
+        if parent_node is not None:
+            parent_node.children.append(self)
+
+            super().__init__(
+                "node",
+                collections.OrderedDict((
+                    ("name", node_name),
+                    ("type", node_type),
+                    ("parent", parent_node.get_path())
+                ))
+            )
+        else:
+            # root node
+            super().__init__(
+                "node",
+                collections.OrderedDict((
+                    ("type", node_type),
+                    ("name", node_name)
+                ))
+            )
+
+
+    def get_name(self):
+        """Get the name of the node in Godot scene"""
+        return self.heading['name']
+
+    def get_path(self):
+        """Get the node path in the Godot scene"""
+        # root node
+        if 'parent' not in self.heading:
+            return '.'
+
+        # children of root node
+        if self.heading['parent'] == '.':
+            return self.heading['name']
+
+        return self.heading['parent'] + '/' + self.heading['name']
+
+    def get_type(self):
+        """Get the node type in Godot scene"""
+        return self.heading["type"]
 
 class ExternalResource(FileEntry):
     """External Resouces are references to external files. In the case of