Browse Source

Squashed commit, multiple bug fixes + improvements.

----------------

Fixed pylint errors.

Removed commented code.
Formatted code.

Update test scene

Include Godot Resource Name in to MeshResourceKey

Same mesh may be used both as CollisionShape and ArrayMesh

Simplify MeshResourceKey construction

The previously implementation is kind of risk because of the recursive
calling, here introduce a simpler one with some fault allowance.

Minor changes and fixes to last commit.

- Added functionality to export all object types.
  - Replaced 'MESH' in object_types with more general 'GEOMETRY' option, allowing for all the following types to be converted: MESH, CURVE, SURFACE, META, FONT. These are all treated similarly using mesh.py. Beziers and Surfaces shapekeys tested and working.
- Moved + rejigged Mesh/Skeleton path resolving to end of export_scene() in export_godot.py for better functionality.
- Fixed bugs when attempting to export empty geometry.
- Added MeshResourceKey to mesh.py, this produces an identifier key for meshes with modifiers. Now, if more than one object use the exact same object data and modifiers then the exporter refers to a single resource.
- Reverted ArrayMeshResource resource_name values to object.data.name.
- Fixed some newline spacing inconsistencies that happened during serialization, so it now matches the same spacing that Godot uses.

Fully updated to work with latest Blender 2.8 build (March 31st 2019)
- Fixed use_mesh_modifiers when converting meshes.
- Added MeshConverter class to assist in all object to mesh conversions.
- Implemented new mesh naming convention to prevent clashing resource names.
- Commented out modifier disabling during ArrayMeshResourceExporter's export_morphs(), based on several tests that suggested it was not necessary.
- Fixed default_settings in __init__.py.
- Renamed MeshResourceExporter to ArrayMeshResourceExporter for consistency.

Implemented property "use_mesh_modifiers" prior to which was no longer working and had no effect on the resulting mesh.

Updated all props to use Python's type annotations, fixes start-up warning.

Updated all references of Object.to_mesh() to work with latest changes to Blender 2.8 API.
Tom Wilson 6 years ago
parent
commit
b534efb0aa
65 changed files with 485 additions and 369 deletions
  1. 19 26
      io_scene_godot/__init__.py
  2. 7 2
      io_scene_godot/converters/__init__.py
  3. 0 10
      io_scene_godot/converters/armature.py
  4. 223 140
      io_scene_godot/converters/mesh.py
  5. 31 64
      io_scene_godot/converters/physics.py
  6. 39 15
      io_scene_godot/export_godot.py
  7. 13 11
      io_scene_godot/structures.py
  8. 0 0
      tests/reference_exports/action_animation/animation_bone_transform.escn
  9. 0 0
      tests/reference_exports/action_animation/animation_loop.escn
  10. 0 0
      tests/reference_exports/action_animation/animation_object_transform.escn
  11. 0 0
      tests/reference_exports/action_animation/animation_rotation_euler.escn
  12. 1 1
      tests/reference_exports/action_animation/animation_shared_action.escn
  13. 5 5
      tests/reference_exports/action_animation/physics_animation.escn
  14. 1 1
      tests/reference_exports/action_with_constraint/bone_attachment_ik.escn
  15. 0 0
      tests/reference_exports/action_with_constraint/constraint_external_IK.escn
  16. 2 2
      tests/reference_exports/action_with_constraint/constraint_internal_IK.escn
  17. 0 0
      tests/reference_exports/action_with_constraint/constraint_with_undeform_bone.escn
  18. 2 2
      tests/reference_exports/action_with_constraint/stashed_constraint.escn
  19. 1 1
      tests/reference_exports/armature/armature_bone_attachment.escn
  20. 3 3
      tests/reference_exports/armature/armature_illegal_bone_name.escn
  21. 8 0
      tests/reference_exports/armature/armature_not_ancester_of_mesh.escn
  22. 0 0
      tests/reference_exports/armature/armature_with_mesh.escn
  23. 0 0
      tests/reference_exports/armature/armature_with_non_deform_bone.escn
  24. 0 0
      tests/reference_exports/armature/armature_with_other_vertex_groups.escn
  25. 0 0
      tests/reference_exports/armature/armature_with_physics.escn
  26. 0 0
      tests/reference_exports/armature/armature_with_pose.escn
  27. 0 2
      tests/reference_exports/armature/just_armature.escn
  28. 0 0
      tests/reference_exports/armature/non-inherit-bone.escn
  29. 1 1
      tests/reference_exports/camera/animation_camera.escn
  30. 0 2
      tests/reference_exports/camera/just_cameras.escn
  31. 1 1
      tests/reference_exports/light/animation_sun.escn
  32. 1 1
      tests/reference_exports/light/animation_various_lights.escn
  33. 1 1
      tests/reference_exports/light/cycles_lights.escn
  34. 1 1
      tests/reference_exports/light/just_point_lights.escn
  35. 1 1
      tests/reference_exports/light/just_spot_lights.escn
  36. 2 1
      tests/reference_exports/material/material_search.escn
  37. 2 2
      tests/reference_exports/material/object_link_material.escn
  38. 1 1
      tests/reference_exports/material/simple_materials.escn
  39. 1 1
      tests/reference_exports/material_cycle/material_anistropy.escn
  40. 82 34
      tests/reference_exports/material_cycle/material_cycle.escn
  41. 5 5
      tests/reference_exports/material_cycle/material_normal.escn
  42. 2 1
      tests/reference_exports/material_cycle/material_unpack_texture.escn
  43. 1 1
      tests/reference_exports/mesh/just_mesh.escn
  44. 1 1
      tests/reference_exports/mesh/parented_meshes.escn
  45. 8 8
      tests/reference_exports/mesh/physics.escn
  46. 0 12
      tests/reference_exports/mesh/single_edge_and_vertex.escn
  47. 1 1
      tests/reference_exports/mesh/tangent_test.escn
  48. 1 1
      tests/reference_exports/mesh/uv_testing.escn
  49. 1 1
      tests/reference_exports/mesh/vertex_color.escn
  50. 1 1
      tests/reference_exports/misc/duplicate_name.escn
  51. 0 0
      tests/reference_exports/misc/invisible_objects.escn
  52. 0 0
      tests/reference_exports/nla_animation/animation_multi_strip.escn
  53. 0 0
      tests/reference_exports/nla_animation/animation_with_empty_strip.escn
  54. 0 0
      tests/reference_exports/nla_animation/nla_with_active_action.escn
  55. 1 1
      tests/reference_exports/nla_animation/nla_with_no_active_action.escn
  56. 0 0
      tests/reference_exports/nla_animation/nla_with_stashed_action.escn
  57. 1 1
      tests/reference_exports/scene_animation/animation_parented_objects.escn
  58. 1 1
      tests/reference_exports/shape_key/animation_shapekey.escn
  59. 0 0
      tests/reference_exports/shape_key/animation_shapekey_with_transform.escn
  60. 1 1
      tests/reference_exports/shape_key/just_shapekey.escn
  61. 1 1
      tests/reference_exports/shape_key/shapekey_with_multi_surface.escn
  62. 0 0
      tests/reference_exports/shape_key/shapekey_with_pose.escn
  63. 10 0
      tests/reference_exports/shape_key/shapekey_with_subdivision.escn
  64. BIN
      tests/test_scenes/armature/armature_not_ancester_of_mesh.blend
  65. BIN
      tests/test_scenes/shape_key/shapekey_with_subdivision.blend

+ 19 - 26
io_scene_godot/__init__.py

@@ -46,11 +46,11 @@ class ExportGodot(bpy.types.Operator, ExportHelper):
     bl_options = {"PRESET"}
 
     filename_ext = ".escn"
-    filter_glob = StringProperty(default="*.escn", options={"HIDDEN"})
+    filter_glob: StringProperty(default="*.escn", options={"HIDDEN"})
 
     # List of operator properties, the attributes will be assigned
     # to the class instance from the operator settings before calling
-    object_types = EnumProperty(
+    object_types: EnumProperty(
         name="Object Types",
         options={"ENUM_FLAG"},
         items=(
@@ -58,41 +58,39 @@ class ExportGodot(bpy.types.Operator, ExportHelper):
             ("CAMERA", "Camera", ""),
             ("LIGHT", "Light", ""),
             ("ARMATURE", "Armature", ""),
-            ("MESH", "Mesh", ""),
-            # ("CURVE", "Curve", ""),
+            ("GEOMETRY", "Geometry", "")
         ),
         default={
             "EMPTY",
             "CAMERA",
             "LIGHT",
             "ARMATURE",
-            "MESH",
-            # "CURVE"
+            "GEOMETRY"
         },
     )
 
-    use_visible_objects = BoolProperty(
+    use_visible_objects: BoolProperty(
         name="Only Visible Object",
         description="Export only objects which are in the current view layer "
                     "and are visible.",
         default=True,
     )
-    use_export_selected = BoolProperty(
+    use_export_selected: BoolProperty(
         name="Only Selected Objects",
         description="Export only selected objects",
         default=False,
     )
-    use_mesh_modifiers = BoolProperty(
+    use_mesh_modifiers: BoolProperty(
         name="Apply Modifiers",
         description="Apply modifiers to mesh objects (on a copy!).",
         default=True,
     )
-    use_exclude_ctrl_bone = BoolProperty(
+    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(
+    use_export_animation: BoolProperty(
         name="Export Animation",
         description="Export all the animation actions (include actions "
                     "in nla_tracks), note that by default blender animation "
@@ -100,40 +98,35 @@ class ExportGodot(bpy.types.Operator, ExportHelper):
                     "own AnimationPlayer hold their actions",
         default=True,
     )
-    use_export_material = BoolProperty(
-        name="Export Materinal",
+    use_export_material: BoolProperty(
+        name="Export Material",
         description="Export all the material associated with mesh surfaces",
         default=True,
     )
-    use_export_shape_key = BoolProperty(
+    use_export_shape_key: BoolProperty(
         name="Export Shape Key",
         description="Export all the shape keys in mesh objects",
         default=True,
     )
-    use_stashed_action = BoolProperty(
+    use_stashed_action: BoolProperty(
         name="Export Stashed Actions",
         description="Export stashed actions and muted nla_strip as separate "
                     "animation and place into AnimationPlayer",
         default=True,
     )
-    use_export_material = BoolProperty(
-        name="Export Materinal",
-        description="Export all the material associated with mesh surfaces",
-        default=True,
-    )
-    use_beta_features = BoolProperty(
+    use_beta_features: BoolProperty(
         name="Use Beta Features",
         description="Export using new features coming in Godot beta versions",
         default=True,
     )
-    generate_external_material = BoolProperty(
+    generate_external_material: BoolProperty(
         name="Generate External Material",
         description="If turned on, materials in the exported scene would "
                     "generate external .material files when imported to "
                     "godot,  thus make it easy for material reusing",
         default=False,
     )
-    animation_modes = EnumProperty(
+    animation_modes: EnumProperty(
         name="Animation Modes",
         description="Configuration of how blender animation data being "
                     "exported to godot AnimationPlayer as well as the "
@@ -158,7 +151,7 @@ class ExportGodot(bpy.types.Operator, ExportHelper):
             )
         )
     )
-    material_search_paths = EnumProperty(
+    material_search_paths: EnumProperty(
         name="Material Search Paths",
         description="Search for existing godot materials with names that match"
                     "the blender material names (ie the file <matname>.tres"
@@ -243,8 +236,8 @@ def export(filename, overrides=None):
     """
 
     default_settings = dict()
-    for attr_name in ExportGodot.__dict__:
-        attr = ExportGodot.__dict__[attr_name]
+    for attr_name in ExportGodot.__annotations__:
+        attr = ExportGodot.__annotations__[attr_name]
         # This introspection is not very robust and may break in future blende
         # versions. This is becase for some reason you can't compare against
         # bpy.types.Property because. well, they end up not being subclasses

+ 7 - 2
io_scene_godot/converters/__init__.py

@@ -21,13 +21,18 @@ from .physics import export_physics_properties
 from .armature import export_armature_node, export_bone_attachment
 from .animation import export_animation_data
 
-
+# TODO: What about Empties which refer to group instances? (in 2.8 Collection
+# Instances)
 BLENDER_TYPE_TO_EXPORTER = {
     "MESH": export_mesh_node,
     "ARMATURE": export_armature_node,
     "CAMERA": export_camera_node,
     "LIGHT": export_light_node,
-    "EMPTY": export_empty_node
+    "EMPTY": export_empty_node,
+    "CURVE": export_mesh_node,
+    "SURFACE": export_mesh_node,
+    "META": export_mesh_node,
+    "FONT": export_mesh_node
 }
 
 BONE_ATTACHMENT_EXPORTER = export_bone_attachment

+ 0 - 10
io_scene_godot/converters/armature.py

@@ -27,16 +27,6 @@ def export_bone_attachment(escn_file, node, parent_gd_node):
     return bone_attachment
 
 
-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
-
-
 class Bone:
     """A Bone has almost same attributes as Godot bones"""
 

+ 223 - 140
io_scene_godot/converters/mesh.py

@@ -5,8 +5,8 @@ import bmesh
 import mathutils
 
 from .material import export_material
-from ..structures import (Array, NodeTemplate, InternalResource, NodePath,
-                          Map, gamma_correct)
+from ..structures import (
+    Array, NodeTemplate, InternalResource, Map, gamma_correct)
 from . import physics
 from . import armature
 from . import animation
@@ -25,44 +25,50 @@ def export_mesh_node(escn_file, export_settings, obj, parent_gd_node):
         parent_gd_node = physics.export_physics_properties(
             escn_file, export_settings, obj, parent_gd_node
         )
-
-    if physics.has_physics(obj) and obj.display_type == "WIRE":
         # skip wire mesh which is used as collision mesh
-        return parent_gd_node
+        if obj.display_type == "WIRE":
+            return parent_gd_node
 
     mesh_node = NodeTemplate(obj.name, "MeshInstance", parent_gd_node)
-    mesh_exporter = MeshResourceExporter(obj)
-
-    armature_data = get_modifier_armature_data(obj)
-    if ("ARMATURE" in export_settings['object_types'] and
-            armature_data is not None):
-        skeleton_node = armature.find_skeletion_node(parent_gd_node)
-        if skeleton_node is not None:
-            mesh_exporter.init_mesh_bones_data(skeleton_node)
-            mesh_node['skeleton'] = NodePath(
-                mesh_node.get_path(), skeleton_node.get_path())
+    mesh_exporter = ArrayMeshResourceExporter(obj)
+
+    armature_obj = None
+    if "ARMATURE" in export_settings['object_types']:
+        armature_obj = get_modifier_armature(obj)
+        if armature_obj:
+            mesh_exporter.init_mesh_bones_data(armature_obj, export_settings)
+            # set armature to REST so current pose does not affect converted
+            # meshes.
+            armature_pose_position = armature_obj.data.pose_position
+            armature_obj.data.pose_position = "REST"
 
     mesh_id = mesh_exporter.export_mesh(escn_file, export_settings)
 
-    mesh_node['mesh'] = "SubResource({})".format(mesh_id)
-    mesh_node['visible'] = obj.visible_get()
+    if armature_obj:
+        # set armature back to previous pose_position
+        armature_obj.data.pose_position = armature_pose_position
+
+    if mesh_id is not None:
+        mesh_node['mesh'] = "SubResource({})".format(mesh_id)
+        mesh_node['visible'] = obj.visible_get()
+
+        mesh_resource = escn_file.internal_resources[mesh_id - 1]
+        export_object_link_material(
+            escn_file, export_settings, obj, mesh_resource, mesh_node
+        )
 
     # Transform of rigid mesh is moved up to its collision
     # shapes.
-    if not physics.has_physics(obj):
-        mesh_node['transform'] = obj.matrix_local
-    else:
+    if physics.has_physics(obj):
         mesh_node['transform'] = mathutils.Matrix.Identity(4)
+    else:
+        mesh_node['transform'] = obj.matrix_local
 
     escn_file.add_node(mesh_node)
 
-    export_object_link_material(
-        escn_file, export_settings, obj, mesh_node
-    )
-
     # export shape key animation
     if (export_settings['use_export_shape_key'] and
-            obj.data.shape_keys is not None):
+            has_shape_keys(obj.data)):
         animation.export_animation_data(
             escn_file, export_settings, mesh_node,
             obj.data.shape_keys, 'shapekey')
@@ -87,22 +93,19 @@ def fix_vertex(vtx):
     return mathutils.Vector((vtx.x, vtx.z, -vtx.y))
 
 
-def get_modifier_armature_data(mesh_object):
-    """Get the armature modifier of a blender object
+def get_modifier_armature(mesh_object):
+    """Get the armature modifier target object of a blender object
     if does not have one, return None"""
     for modifier in mesh_object.modifiers:
-        if (isinstance(modifier, bpy.types.ArmatureModifier) and
-                modifier.object is not None):
-            return modifier.object.data
+        if isinstance(modifier, bpy.types.ArmatureModifier):
+            return modifier.object
     return None
 
 
 def export_object_link_material(escn_file, export_settings, mesh_object,
-                                gd_node):
+                                mesh_resource, gd_node):
     """Export object linked material, if multiple object link material,
     only export the first one in the material slots"""
-    mesh_resource_id = escn_file.get_internal_resource(mesh_object.data)
-    mesh_resource = escn_file.internal_resources[mesh_resource_id - 1]
     for index, slot in enumerate(mesh_object.material_slots):
         if slot.link == 'OBJECT' and slot.material is not None:
             surface_id = mesh_resource.get_surface_id(index)
@@ -115,8 +118,88 @@ def export_object_link_material(escn_file, export_settings, mesh_object,
                 )
 
 
+def get_applicable_modifiers(obj, export_settings):
+    """Returns a list of all the modifiers that'll be applied to the final
+    godot mesh"""
+    ignore_modifiers = []
+    if not export_settings['use_mesh_modifiers']:
+        return []
+    if "ARMATURE" in export_settings['object_types']:
+        ignore_modifiers.append(bpy.types.ArmatureModifier)
+    ignore_modifiers = tuple(ignore_modifiers)
+    return [m for m in obj.modifiers if not isinstance(m, ignore_modifiers)
+            and m.show_viewport]
+
+
+def record_modifier_config(obj):
+    """Returns modifiers viewport visibility config"""
+    modifier_config_cache = []
+    for mod in obj.modifiers:
+        modifier_config_cache.append(mod.show_viewport)
+    return modifier_config_cache
+
+
+def restore_modifier_config(obj, modifier_config_cache):
+    """Applies modifiers viewport visibility config"""
+    for i, mod in enumerate(obj.modifiers):
+        mod.show_viewport = modifier_config_cache[i]
+
+
+def has_shape_keys(object_data):
+    """Determine if object data has shape keys"""
+    return (hasattr(object_data, "shape_keys") and
+            object_data.shape_keys is not None)
+
+
+class MeshResourceKey:
+    """Produces a key based on an mesh object's data, every different
+    Mesh Resource would have a unique key"""
+
+    def __init__(self, rsc_type, obj, export_settings):
+        mesh_data = obj.data
+
+        # Resource type included because same blender mesh may be used as
+        # MeshResource or CollisionShape, but they are different resource
+        gd_rsc_type = rsc_type
+
+        # Here collect info of all the modifiers applied on the mesh.
+        # Modifiers along with the original mesh data would determine
+        # the evaluated mesh.
+        mod_info_list = list()
+        for modifier in get_applicable_modifiers(obj, export_settings):
+            # Modifier name indicates its type, its an identifier
+            mod_info_list.append(modifier.name)
+
+            # First property is always 'rna_type', skip it
+            for prop in modifier.bl_rna.properties.keys()[1:]:
+                # Note that Property may be `BoolProperty`,
+                # `CollectionProperty`, `EnumProperty`, `FloatProperty`,
+                # `IntProperty`, `PointerProperty`, `StringProperty`"
+                # Most of them are primary type when accessed with `getattr`,
+                # so they are fine to be hashed.
+                # For `PointerProperty`, it is mostly an bpy.types.ID, hash it
+                # would get its python object identifier, which is also good.
+                # For `CollectionProperty`, it would make more sense to
+                # traversal it, however, we cut down it here to allow
+                # some of mesh resource not be shared because of simplicity
+                mod_info_list.append(getattr(modifier, prop))
+
+        self._data = tuple([mesh_data, gd_rsc_type] + mod_info_list)
+        # Precalculate the hash now for better efficiency later
+        self._hash = hash(self._data)
+
+    def __hash__(self):
+        return self._hash
+
+    def __eq__(self, other):
+        # pylint: disable=protected-access
+        return (self.__class__ == other.__class__ and
+                self._data == other._data)
+
+
 class ArrayMeshResource(InternalResource):
     """Godot ArrayMesh resource, containing surfaces"""
+
     def __init__(self, name):
         super().__init__('ArrayMesh', name)
         self._mat_to_surf_mapping = dict()
@@ -131,8 +214,9 @@ class ArrayMeshResource(InternalResource):
         self._mat_to_surf_mapping[material_index] = surface_id
 
 
-class MeshResourceExporter:
+class ArrayMeshResourceExporter:
     """Export a mesh resource from a blender mesh object"""
+
     def __init__(self, mesh_object):
         # blender mesh object
         self.object = mesh_object
@@ -141,82 +225,50 @@ class MeshResourceExporter:
         self.has_tangents = False
         self.vgroup_to_bone_mapping = dict()
 
-    def init_mesh_bones_data(self, skeleton_node):
+    def init_mesh_bones_data(self, armature_obj, export_settings):
         """Find the mapping relation between vertex groups
         and bone id"""
-        for bone_name, bone_info in skeleton_node.bones.items():
-            group = self.object.vertex_groups.get(bone_name)
-            if group is not None:
-                self.vgroup_to_bone_mapping[group.index] = bone_info.id
+        if armature_obj is None:
+            return
+        export_bones = []
+        for bone in armature_obj.data.bones:
+            if armature.should_export(export_settings, armature_obj, bone):
+                export_bones.append(bone)
+        for bone_id, bone in enumerate(export_bones):
+            if armature.should_export(export_settings, armature_obj, bone):
+                group = self.object.vertex_groups.get(bone.name)
+                if group is not None:
+                    self.vgroup_to_bone_mapping[group.index] = bone_id
 
     def export_mesh(self, escn_file, export_settings):
-        """Saves a mesh into the escn file """
-        # Check if it exists so we don't bother to export it twice
-        mesh = self.object.data
-        mesh_id = escn_file.get_internal_resource(mesh)
-
+        """Saves a mesh into the escn file"""
+        mesh_converter = MeshConverter(self.object, export_settings)
+        key = MeshResourceKey('ArrayMesh', self.object, export_settings)
+        # Check if mesh resource exists so we don't bother to export it twice,
+        mesh_id = escn_file.get_internal_resource(key)
         if mesh_id is not None:
             return mesh_id
 
-        self.mesh_resource = ArrayMeshResource(mesh.name)
-
-        self.make_arrays(
-            escn_file,
-            export_settings,
-        )
-
-        mesh_id = escn_file.add_internal_resource(self.mesh_resource, mesh)
-        assert mesh_id is not None
+        mesh = mesh_converter.to_mesh()
+        self.has_tangents = mesh_converter.has_tangents
 
-        return mesh_id
-
-    def make_arrays(self, escn_file, export_settings):
-        """Generates arrays of positions, normals etc"""
-        apply_modifiers = export_settings['use_mesh_modifiers']
+        if mesh is not None and mesh.polygons:
+            self.mesh_resource = ArrayMeshResource(mesh.name)
 
-        # set shape key to basis key which would have index 0
-        self.object.show_only_shape_key = True
-        self.object.active_shape_key_index = 0
+            # Separate by materials into single-material surfaces
+            self.generate_surfaces(
+                escn_file,
+                export_settings,
+                mesh
+            )
 
-        mesh = self.object.to_mesh()
+            mesh_id = escn_file.add_internal_resource(self.mesh_resource, key)
+            assert mesh_id is not None
 
-        self.object.show_only_shape_key = False
+        # free mesh from memory
+        mesh_converter.to_mesh_clear()
 
-        # if the original mesh has an object link material,
-        # the new created mesh would use it as data link material,
-        # seems a bug of Blender,
-        # here is a simple fix, not sure if it is robust enough..
-        for idx in range(len(mesh.materials)):
-            mesh.materials[idx] = self.object.data.materials[idx]
-
-        # Prepare the mesh for export
-        triangulate_mesh(mesh)
-
-        # godot engine supports two uv channels
-        uv_layer_count = min(len(mesh.uv_layers), 2)
-
-        if mesh.uv_layers:
-            self.has_tangents = True
-            try:
-                mesh.calc_tangents()
-            except RuntimeError:
-                # This fails if the mesh is a single vertex (and presumably an
-                # edge). Since this won't be rendered by visualserver (the only
-                # user of the tangents), we'll just disable tangents and hope
-                # for the best....
-                self.has_tangents = False
-        else:
-            mesh.calc_normals_split()
-            self.has_tangents = False
-
-        # Separate by materials into single-material surfaces
-        self.generate_surfaces(
-            escn_file,
-            export_settings,
-            mesh
-        )
-
-        bpy.data.meshes.remove(mesh)
+        return mesh_id
 
     @staticmethod
     def validate_morph_mesh_modifiers(mesh_object):
@@ -249,44 +301,17 @@ class MeshResourceExporter:
         )
         self.mesh_resource["blend_shape/mode"] = 0
 
-        # toggle shapekey uncompatible modifiers to false
-        modifier_config_cache = list()
-        if export_settings['use_mesh_modifiers']:
-            if not self.validate_morph_mesh_modifiers(
-                    self.object):
-                logging.warning(
-                    "Mesh object '%s' has modifiers "
-                    "incompatible with shape key",
-                    self.object.name
-                )
-
-            for modifier in self.object.modifiers:
-                modifier_config_cache.append(modifier.show_viewport)
-                modifier.show_viewport = False
-
-        # turn on shape key mode
-        self.object.show_only_shape_key = True
-        blender_shape_keys = self.object.data.shape_keys
-        for index, shape_key in enumerate(blender_shape_keys.key_blocks):
-            if shape_key == blender_shape_keys.reference_key:
+        shape_keys = self.object.data.shape_keys
+        for index, shape_key in enumerate(shape_keys.key_blocks):
+            if shape_key == shape_keys.reference_key:
                 continue
 
             self.mesh_resource["blend_shape/names"].append(
                 '"{}"'.format(shape_key.name)
             )
 
-            self.object.active_shape_key_index = index
-
-            bpy.context.view_layer.depsgraph.update()
-            shape_key_mesh = self.object.evaluated_get(
-                bpy.context.view_layer.depsgraph).to_mesh()
-
-            triangulate_mesh(shape_key_mesh)
-
-            if self.has_tangents:
-                shape_key_mesh.calc_tangents()
-            else:
-                shape_key_mesh.calc_normals_split()
+            mesh_converter = MeshConverter(self.object, export_settings)
+            shape_key_mesh = mesh_converter.to_mesh(index)
 
             surfaces_morph_data = self.intialize_surfaces_morph_data(surfaces)
 
@@ -314,14 +339,7 @@ class MeshResourceExporter:
             for surf_index, surf in enumerate(surfaces):
                 surf.morph_arrays.append(surfaces_morph_data[surf_index])
 
-            bpy.data.meshes.remove(shape_key_mesh)
-
-        # turn off shape key mode
-        self.object.show_only_shape_key = False
-        # revert modifiers
-        if export_settings['use_mesh_modifiers']:
-            for index, modifier in enumerate(self.object.modifiers):
-                modifier.show_render = modifier_config_cache[index]
+            mesh_converter.to_mesh_clear()
 
     def generate_surfaces(self, escn_file, export_settings, mesh):
         """Splits up the mesh into surfaces with a single material each.
@@ -384,7 +402,7 @@ class MeshResourceExporter:
                 surface.vertex_data.indices.append(vertex_indices)
 
         if (export_settings['use_export_shape_key'] and
-                self.object.data.shape_keys):
+                has_shape_keys(self.object.data)):
             self.export_morphs(export_settings, surfaces)
 
         has_bone = bool(self.vgroup_to_bone_mapping)
@@ -448,7 +466,7 @@ class VerticesArrays:
         uv_layer_count = len(self.vertices[0].uv)
         if uv_index >= uv_layer_count:
             # If lacking 2 UV layers, mark them as null
-            return Array("null, ; No UV"+str(uv_index+1), "", "")
+            return Array("null, ; No UV%d" % (uv_index+1), "", "")
 
         uv_vals = Array("Vector2Array(")
         for vert in self.vertices:
@@ -656,3 +674,68 @@ class Vertex:
         self.uv = []
         self.bones = []
         self.weights = []
+
+
+class MeshConverter:
+    """MeshConverter evaulates and converts objects to meshes, triangulates
+    and calculates tangents"""
+
+    def __init__(self, obj, export_settings):
+        self.object = obj
+        self.eval_object = None
+        self.use_mesh_modifiers = export_settings["use_mesh_modifiers"]
+        self.use_export_shape_key = export_settings['use_export_shape_key']
+        self.has_tangents = False
+
+    def to_mesh(self, shape_key_index=0, calculate_tangents=True):
+        """Evaluates object & converts to final mesh, ready for export.
+        The mesh is only temporary, call to_mesh_clear() afterwards."""
+        # set shape key to basis key which would have index 0
+        orig_shape_key_index = self.object.active_shape_key_index
+        self.object.show_only_shape_key = True
+        self.object.active_shape_key_index = shape_key_index
+
+        self.eval_object = self.object
+
+        modifier_config_cache = None
+        if not self.use_mesh_modifiers:
+            modifier_config_cache = record_modifier_config(self.object)
+            for mod in self.object.modifiers:
+                mod.show_viewport = False
+
+        depsgraph = bpy.context.view_layer.depsgraph
+        depsgraph.update()
+        self.eval_object = self.object.evaluated_get(depsgraph)
+
+        # These parameters are required for preserving vertex groups.
+        mesh = self.eval_object.to_mesh(
+            preserve_all_data_layers=True, depsgraph=depsgraph)
+
+        if not self.use_mesh_modifiers:
+            restore_modifier_config(self.object, modifier_config_cache)
+
+        self.has_tangents = False
+
+        # mesh result can be none if the source geometry has no faces, so we
+        # need to consider this if we want a robust exporter.
+        if mesh is not None:
+            triangulate_mesh(mesh)
+
+            self.has_tangents = mesh.uv_layers and mesh.polygons
+            if calculate_tangents:
+                if self.has_tangents:
+                    mesh.calc_tangents()
+                else:
+                    mesh.calc_normals_split()
+
+        self.object.show_only_shape_key = False
+        self.object.active_shape_key_index = orig_shape_key_index
+
+        return mesh
+
+    def to_mesh_clear(self):
+        """Clears the temporary generated mesh from memory"""
+        if self.object is None:
+            return
+        self.eval_object.to_mesh_clear()
+        self.object = self.eval_object = None

+ 31 - 64
io_scene_godot/converters/physics.py

@@ -5,9 +5,7 @@ physics owns the object.
 """
 
 import logging
-import bpy
 import mathutils
-import bmesh
 from ..structures import NodeTemplate, InternalResource, Array, _AXIS_CORRECT
 
 PHYSICS_TYPES = {'KinematicBody', 'RigidBody', 'StaticBody'}
@@ -89,20 +87,19 @@ def export_collision_shape(escn_file, export_settings, node, parent_gd_node,
         col_shape['height'] = bounds.z - col_shape['radius'] * 2
         shape_id = escn_file.add_internal_resource(col_shape, rbd)
     elif rbd.collision_shape == "CONVEX_HULL":
-        col_shape, shape_id = generate_convex_mesh_array(
+        col_shape, shape_id = generate_mesh_array(
             escn_file, export_settings,
-            node
+            node, convex=True
         )
     elif rbd.collision_shape == "MESH":
-        col_shape, shape_id = generate_triangle_mesh_array(
+        col_shape, shape_id = generate_mesh_array(
             escn_file, export_settings,
-            node
+            node, convex=False
         )
     else:
         logging.warning("Unable to export physics shape for %s", node.name)
 
-    if shape_id is not None:
-
+    if shape_id is not None and col_shape is not None:
         if rbd.use_margin or rbd.collision_shape == "MESH":
             col_shape['margin'] = rbd.collision_margin
         col_node['shape'] = "SubResource({})".format(shape_id)
@@ -111,68 +108,38 @@ def export_collision_shape(escn_file, export_settings, node, parent_gd_node,
     return col_node
 
 
-def generate_convex_mesh_array(escn_file, export_settings, node):
-    """Generates godots ConvexPolygonShape from an object"""
-    mesh = node.data
-    key = (mesh, "ConvexCollisionMesh")
-    resource_id = escn_file.get_internal_resource(key)
-    if resource_id is not None:
-        return resource_id
-
-    col_shape = InternalResource("ConvexPolygonShape", mesh.name)
-
-    mesh = node.to_mesh()
-
-    # Triangulate
-    triangulated_mesh = bmesh.new()
-    triangulated_mesh.from_mesh(mesh)
-    # For some reason, generateing the convex hull here causes Godot to crash
-    # bmesh.ops.convex_hull(triangulated_mesh, input=triangulated_mesh.verts)
-    bmesh.ops.triangulate(triangulated_mesh, faces=triangulated_mesh.faces)
-    triangulated_mesh.to_mesh(mesh)
-    triangulated_mesh.free()
-
-    vert_array = list()
-    for poly in mesh.polygons:
-        for vert_id in poly.vertices:
-            vert_array.append(list(mesh.vertices[vert_id].co))
-
-    bpy.data.meshes.remove(mesh)
-
-    col_shape['points'] = Array("PoolVector3Array(", values=vert_array)
-
-    return col_shape, escn_file.add_internal_resource(col_shape, key)
-
-
-def generate_triangle_mesh_array(escn_file, export_settings, node):
-    """Generates godots ConcavePolygonShape from an object"""
-    mesh = node.data
-    key = (mesh, "TriangleCollisionMesh")
+def generate_mesh_array(escn_file, export_settings, node, convex=False):
+    """Generates godots PolygonShape from an object"""
+    from .mesh import (MeshConverter, MeshResourceKey)
+    mesh_converter = MeshConverter(node, export_settings)
+    if convex:
+        key = MeshResourceKey("ConvexPolygonShape", node, export_settings)
+    else:
+        key = MeshResourceKey("ConcavePolygonShape", node, export_settings)
     resource_id = escn_file.get_internal_resource(key)
     if resource_id is not None:
         return resource_id
 
-    col_shape = InternalResource("ConcavePolygonShape", mesh.name)
-
-    mesh = node.to_mesh()
-
-    # Triangulate
-    triangulated_mesh = bmesh.new()
-    triangulated_mesh.from_mesh(mesh)
-    bmesh.ops.triangulate(triangulated_mesh, faces=triangulated_mesh.faces)
-    triangulated_mesh.to_mesh(mesh)
-    triangulated_mesh.free()
-
-    vert_array = list()
-    for poly in mesh.polygons:
-        for vert_id in poly.vertices:
-            vert_array.append(list(mesh.vertices[vert_id].co))
-
-    bpy.data.meshes.remove(mesh)
+    col_shape = None
+    shape_id = None
+    mesh = mesh_converter.to_mesh(calculate_tangents=False)
+    if mesh is not None and mesh.polygons:
+        vert_array = list()
+        for poly in mesh.polygons:
+            for vert_id in poly.vertices:
+                vert_array.append(list(mesh.vertices[vert_id].co))
+
+        if convex:
+            col_shape = InternalResource("ConvexPolygonShape", mesh.name)
+            col_shape['points'] = Array("PoolVector3Array(", values=vert_array)
+        else:
+            col_shape = InternalResource("ConcavePolygonShape", mesh.name)
+            col_shape['data'] = Array("PoolVector3Array(", values=vert_array)
+        shape_id = escn_file.add_internal_resource(col_shape, key)
 
-    col_shape['data'] = Array("PoolVector3Array(", values=vert_array)
+    mesh_converter.to_mesh_clear()
 
-    return col_shape, escn_file.add_internal_resource(col_shape, key)
+    return col_shape, shape_id
 
 
 def export_physics_controller(escn_file, export_settings, node,

+ 39 - 15
io_scene_godot/export_godot.py

@@ -28,12 +28,11 @@ import os
 import collections
 import functools
 import logging
-import math
 import bpy
-import mathutils
 
 from . import structures
 from . import converters
+from .structures import (_AXIS_CORRECT, NodePath)
 
 logging.basicConfig(level=logging.INFO, format="[%(levelname)s]: %(message)s")
 
@@ -93,19 +92,17 @@ class GodotExporter:
         bpy.context.view_layer.objects.active = obj
 
         # Figure out what function will perform the export of this object
-        if (obj.type in converters.BLENDER_TYPE_TO_EXPORTER and
-                obj in self.exporting_objects):
+        if obj.type not in converters.BLENDER_TYPE_TO_EXPORTER:
+            logging.warning(
+                "Unknown object type. Treating as empty: %s", obj.name
+            )
+        elif obj in self.exporting_objects:
             exporter = converters.BLENDER_TYPE_TO_EXPORTER[obj.type]
         else:
-            if obj not in self.exporting_objects:
-                logging.warning(
-                    "Object is parent of exported objects. "
-                    "Treating as empty: %s", obj.name
-                )
-            else:
-                logging.warning(
-                    "Unknown object type. Treating as empty: %s", obj.name
-                )
+            logging.warning(
+                "Object is parent of exported objects. "
+                "Treating as empty: %s", obj.name
+            )
             exporter = converters.BLENDER_TYPE_TO_EXPORTER["EMPTY"]
 
         is_bone_attachment = False
@@ -124,6 +121,8 @@ class GodotExporter:
         exported_node = exporter(self.escn_file, self.config, obj,
                                  parent_gd_node)
 
+        self.bl_object_gd_node_map[obj] = exported_node
+
         if is_bone_attachment:
             for child in parent_gd_node.children:
                 child['transform'] = structures.fix_bone_attachment_transform(
@@ -136,7 +135,7 @@ class GodotExporter:
         if (exported_node.parent is not None and
                 exported_node.parent.get_type() == 'CollisionShape'):
             exported_node['transform'] = (
-                mathutils.Matrix.Rotation(math.radians(90), 4, 'X') @
+                _AXIS_CORRECT.inverted() @
                 exported_node['transform'])
 
         # if the blender node is exported and it has animation data
@@ -169,18 +168,23 @@ class GodotExporter:
         if self.config["use_export_selected"] and not obj.select_get():
             return False
 
-        self.exporting_objects.add(obj)
         return True
 
     def export_scene(self):
         """Decide what objects to export, and export them!"""
         logging.info("Exporting scene: %s", self.scene.name)
 
+        in_edit_mode = False
+        if bpy.context.object and bpy.context.object.mode == "EDIT":
+            in_edit_mode = True
+            bpy.ops.object.editmode_toggle()
+
         # Decide what objects to export
         for obj in self.scene.objects:
             if obj in self.exporting_objects:
                 continue
             if self.should_export_object(obj):
+                self.exporting_objects.add(obj)
                 # Ensure parents of current valid object is
                 # going to the exporting recursion
                 tmp = obj
@@ -204,6 +208,18 @@ class GodotExporter:
                 # recursive exporting on root object
                 self.export_object(obj, root_gd_node)
 
+        if "ARMATURE" in self.config['object_types']:
+            for bl_obj in self.bl_object_gd_node_map:
+                for mod in bl_obj.modifiers:
+                    if mod.type == "ARMATURE":
+                        mesh_node = self.bl_object_gd_node_map[bl_obj]
+                        skeleton_node = self.bl_object_gd_node_map[mod.object]
+                        mesh_node['skeleton'] = NodePath(
+                            mesh_node.get_path(), skeleton_node.get_path())
+
+        if in_edit_mode:
+            bpy.ops.object.editmode_toggle()
+
     def load_supported_features(self):
         """According to `project.godot`, determine all new feature supported
         by that godot version"""
@@ -275,6 +291,7 @@ class GodotExporter:
             self.load_supported_features()
 
         self.escn_file = None
+        self.bl_object_gd_node_map = {}
 
     def __enter__(self):
         return self
@@ -288,6 +305,13 @@ def save(operator, context, filepath="", **kwargs):
     exporter_log_handler = ExporterLogHandler(operator)
     logging.getLogger().addHandler(exporter_log_handler)
 
+    object_types = kwargs["object_types"]
+    # GEOMETRY isn't an object type so replace it with all valid geometry based
+    # object types
+    if "GEOMETRY" in object_types:
+        object_types.remove("GEOMETRY")
+        object_types |= {"MESH", "CURVE", "SURFACE", "META", "FONT"}
+
     with GodotExporter(filepath, kwargs, operator) as exp:
         exp.export()
 

+ 13 - 11
io_scene_godot/structures.py

@@ -93,13 +93,13 @@ class ESCNFile:
 
     def to_string(self):
         """Serializes the file ready to dump out to disk"""
-
-        return "{}{}\n{}\n{}\n".format(
+        sections = (
             self.heading.to_string(),
             '\n\n'.join(i.to_string() for i in self.external_resources),
             '\n\n'.join(e.to_string() for e in self.internal_resources),
             '\n\n'.join(n.to_string() for n in self.nodes)
         )
+        return "\n\n".join([s for s in sections if s]) + "\n"
 
 
 class FileEntry(collections.OrderedDict):
@@ -111,6 +111,7 @@ class FileEntry(collections.OrderedDict):
         self.entry_type = entry_type
         self.heading = collections.OrderedDict(heading_dict)
 
+        # NOTE: contents is unused. Remove?
         # This string is copied verbaitum, so can be used for custom writing
         self.contents = ''
 
@@ -132,21 +133,22 @@ class FileEntry(collections.OrderedDict):
     def generate_body_string(self):
         """Convert the contents of the super/internal dict into newline
         separated key=val pairs"""
-        out_str = ''
+        lines = []
         for var in self:
             val = self[var]
             val = to_string(val)
-
-            out_str += '\n{} = {}'.format(var, val)
-        return out_str
+            lines.append('{} = {}'.format(var, val))
+        return "\n".join(lines)
 
     def to_string(self):
         """Serialize this entire entry"""
-        return "{}\n{}{}".format(
-            self.generate_heading_string(),
-            self.generate_body_string(),
-            self.contents
-        )
+        heading = self.generate_heading_string()
+        body = self.generate_body_string()
+        if body and self.contents:
+            return "{}\n\n{}\n{}".format(heading, body, self.contents)
+        if body:
+            return "{}\n\n{}".format(heading, body)
+        return heading
 
 
 class NodeTemplate(FileEntry):

File diff suppressed because it is too large
+ 0 - 0
tests/reference_exports/action_animation/animation_bone_transform.escn


File diff suppressed because it is too large
+ 0 - 0
tests/reference_exports/action_animation/animation_loop.escn


File diff suppressed because it is too large
+ 0 - 0
tests/reference_exports/action_animation/animation_object_transform.escn


File diff suppressed because it is too large
+ 0 - 0
tests/reference_exports/action_animation/animation_rotation_euler.escn


+ 1 - 1
tests/reference_exports/action_animation/animation_shared_action.escn

@@ -59,8 +59,8 @@ tracks/0/type = "transform"
 tracks/0/path = NodePath(".:")
 tracks/0/interp = 1
 tracks/0/keys = [0.0, 1.0, -0.0829935, -0.0705722, -0.022377, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0416667, 1.0, -0.0829935, -0.0705722, 0.0411448, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0833333, 1.0, -0.0829935, -0.0705722, 0.237523, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.125, 1.0, -0.0829935, -0.0705722, 0.566712, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.166667, 1.0, -0.0829935, -0.0705722, 1.01117, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.208333, 1.0, -0.0829935, -0.0705722, 1.53291, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.25, 1.0, -0.0829935, -0.0705722, 2.08052, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.291667, 1.0, -0.0829935, -0.0705722, 2.60399, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.333333, 1.0, -0.0829935, -0.0705722, 3.06703, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.375, 1.0, -0.0829935, -0.0705722, 3.45022, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.416667, 1.0, -0.0852899, -0.0355096, 3.80904, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.458333, 1.0, -0.092201, 0.0700142, 4.2004, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.5, 1.0, -0.103438, 0.24159, 4.61307, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.541667, 1.0, -0.118136, 0.466003, 5.02871, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.583333, 1.0, -0.134786, 0.720226, 5.42298, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.625, 1.0, -0.151436, 0.974449, 5.76998, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.666667, 1.0, -0.166133, 1.19886, 6.0486, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.708333, 1.0, -0.17737, 1.37044, 6.24686, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.75, 1.0, -0.184282, 1.47596, 6.36227, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.791667, 1.0, -0.186578, 1.51102, 6.39911, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0]
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Empty" type="Spatial" parent="."]
 

+ 5 - 5
tests/reference_exports/action_animation/physics_animation.escn

@@ -3,7 +3,7 @@
 [sub_resource id=1 type="ConvexPolygonShape"]
 
 resource_name = "Cube"
-points = PoolVector3Array(1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 4.09789, 0.999999, -1.0, 4.09789, 1.0, 0.999999, 4.09789, 1.0, 0.999999, 4.09789, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 0.999999, -1.0, 4.09789, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 4.09789, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 4.09789, 1.0, 0.999999, 4.09789, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 4.09789, -1.0, -1.0, 4.09789, 0.999999, -1.0, 4.09789, 1.0, 0.999999, 4.09789, 0.999999, -1.0, 4.09789, 1.0, -1.0, -1.0, 0.999999, -1.0, 4.09789, -1.0, -1.0, 4.09789, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 4.09789, -1.0, 1.0, 4.09789, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 4.09789)
+points = PoolVector3Array(1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 4.09789, 0.999999, -1.0, 4.09789, 1.0, 0.999999, 4.09789, 1.0, 0.999999, 4.09789, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 0.999999, -1.0, 4.09789, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 4.09789, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 4.09789, 1.0, 0.999999, 4.09789, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 4.09789, -1.0, -1.0, 4.09789, 0.999999, -1.0, 4.09789, 1.0, 0.999999, 4.09789, 0.999999, -1.0, 4.09789, 1.0, -1.0, -1.0, 0.999999, -1.0, 4.09789, -1.0, -1.0, 4.09789, -1.0, -1.0, -1.0, -1.0, -1.0, 4.09789, -1.0, 1.0, 4.09789, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 4.09789)
 
 [sub_resource id=2 type="ArrayMesh"]
 
@@ -27,7 +27,7 @@ surfaces/0 = {
 [sub_resource id=3 type="ConvexPolygonShape"]
 
 resource_name = "Cube003"
-points = PoolVector3Array(1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 4.09789, 0.999999, -1.0, 4.09789, 1.0, 0.999999, 4.09789, 1.0, 0.999999, 4.09789, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 0.999999, -1.0, 4.09789, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 4.09789, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 4.09789, 1.0, 0.999999, 4.09789, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 4.09789, -1.0, -1.0, 4.09789, 0.999999, -1.0, 4.09789, 1.0, 0.999999, 4.09789, 0.999999, -1.0, 4.09789, 1.0, -1.0, -1.0, 0.999999, -1.0, 4.09789, -1.0, -1.0, 4.09789, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 4.09789, -1.0, 1.0, 4.09789, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 4.09789)
+points = PoolVector3Array(1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 4.09789, 0.999999, -1.0, 4.09789, 1.0, 0.999999, 4.09789, 1.0, 0.999999, 4.09789, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 0.999999, -1.0, 4.09789, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 4.09789, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 4.09789, 1.0, 0.999999, 4.09789, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 4.09789, -1.0, -1.0, 4.09789, 0.999999, -1.0, 4.09789, 1.0, 0.999999, 4.09789, 0.999999, -1.0, 4.09789, 1.0, -1.0, -1.0, 0.999999, -1.0, 4.09789, -1.0, -1.0, 4.09789, -1.0, -1.0, -1.0, -1.0, -1.0, 4.09789, -1.0, 1.0, 4.09789, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 4.09789)
 
 [sub_resource id=4 type="Animation"]
 
@@ -58,8 +58,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="CubePhysics" type="RigidBody" parent="."]
 
@@ -82,7 +82,7 @@ shape = SubResource(1)
 
 mesh = SubResource(2)
 visible = true
-transform = Transform(1.0, 0.0, 0.0, 0.0, 7.54979e-08, -1.0, 0.0, 1.0, 7.54979e-08, 0.0, 0.0, 0.0)
+transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, -1.0, 0.0, 1.0, -4.37114e-08, 0.0, 0.0, 0.0)
 
 [node name="Cube003Collision" type="CollisionShape" parent="CubePhysics"]
 
@@ -98,4 +98,4 @@ anims/Cube.003Action.001 = SubResource(4)
 
 mesh = SubResource(5)
 visible = true
-transform = Transform(1.65289, 0.0, 0.0, 0.0, 1.2479e-07, -1.65289, 0.0, 1.65289, 1.2479e-07, 0.0, 0.0, 0.0)
+transform = Transform(1.65289, 0.0, 0.0, 0.0, -7.22502e-08, -1.65289, 0.0, 1.65289, -7.22502e-08, 0.0, 0.0, 0.0)

+ 1 - 1
tests/reference_exports/action_with_constraint/bone_attachment_ik.escn

@@ -52,8 +52,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Armature" type="Skeleton" parent="."]
 

File diff suppressed because it is too large
+ 0 - 0
tests/reference_exports/action_with_constraint/constraint_external_IK.escn


+ 2 - 2
tests/reference_exports/action_with_constraint/constraint_internal_IK.escn

@@ -37,8 +37,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Armature" type="Skeleton" parent="."]
 
@@ -70,7 +70,7 @@ anims/ArmatureAction = SubResource(1)
 
 [node name="Cylinder" type="MeshInstance" parent="Armature"]
 
-skeleton = NodePath("..:")
 mesh = SubResource(2)
 visible = true
 transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.936288, 0.0)
+skeleton = NodePath("..:")

File diff suppressed because it is too large
+ 0 - 0
tests/reference_exports/action_with_constraint/constraint_with_undeform_bone.escn


+ 2 - 2
tests/reference_exports/action_with_constraint/stashed_constraint.escn

@@ -37,8 +37,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Armature" type="Skeleton" parent="."]
 
@@ -70,7 +70,7 @@ anims/ArmatureAction = SubResource(1)
 
 [node name="Cylinder" type="MeshInstance" parent="Armature"]
 
-skeleton = NodePath("..:")
 mesh = SubResource(2)
 visible = true
 transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.936288, 0.0)
+skeleton = NodePath("..:")

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

@@ -18,8 +18,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Armature" type="Skeleton" parent="."]
 

+ 3 - 3
tests/reference_exports/armature/armature_illegal_bone_name.escn

@@ -48,8 +48,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Armature" type="Skeleton" parent="."]
 
@@ -64,7 +64,7 @@ bones/0/bound_children = []
 bones/1/name = "bone"
 bones/1/parent = 0
 bones/1/rest = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, -1.0)
-bones/1/pose = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0)
+bones/1/pose = Transform(0.99961, -0.00771234, -0.0268356, 0.00777253, 0.999968, 0.00213956, 0.0268182, -0.0023473, 0.999638, 0.0, 0.0, 0.0)
 bones/1/enabled = true
 bones/1/bound_children = []
 bones/2/name = "bone001"
@@ -91,7 +91,7 @@ transform = Transform(0.430514, 2.1646e-10, 0.0, -1.27329e-11, 0.430514, 0.0, 0.
 
 [node name="Cube" type="MeshInstance" parent="Armature"]
 
-skeleton = NodePath("..:")
 mesh = SubResource(3)
 visible = true
 transform = Transform(0.706204, 0.0, 0.0, 0.0, 2.62841, 0.0, 0.0, 0.0, 0.48724, -0.0546017, 2.43518, 0.0)
+skeleton = NodePath("..:")

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


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


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


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


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


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


+ 0 - 2
tests/reference_exports/armature/just_armature.escn

@@ -1,9 +1,7 @@
 [gd_scene load_steps=1 format=2]
 
-
 [node type="Spatial" name="Scene"]
 
-
 [node name="Armature" type="Skeleton" parent="."]
 
 bones_in_world_transform = true

File diff suppressed because it is too large
+ 0 - 0
tests/reference_exports/armature/non-inherit-bone.escn


+ 1 - 1
tests/reference_exports/camera/animation_camera.escn

@@ -70,8 +70,8 @@ tracks/4/keys = {
 	"update":0,
 	"values":[49.1343, 49.1343, 49.1343, 48.7259, 47.5342, 45.7099, 43.5151, 41.2598, 39.2179, 37.5705, 36.3983, 35.7119, 35.4893, 35.5137, 35.5876, 35.7119, 35.8878, 36.1163, 36.3983, 36.7346, 37.1254, 37.5705, 38.0689, 38.619, 39.2179, 39.8614, 40.5443, 41.2598, 41.9998, 42.7551, 43.5151, 44.2688, 45.0043, 45.7099, 46.3738, 46.9853, 47.5342, 48.0119, 48.411, 48.7259, 48.9526, 49.089, 49.1343]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Cube" type="MeshInstance" parent="."]
 

+ 0 - 2
tests/reference_exports/camera/just_cameras.escn

@@ -1,9 +1,7 @@
 [gd_scene load_steps=1 format=2]
 
-
 [node type="Spatial" name="Scene"]
 
-
 [node name="Camera004" type="Camera" parent="."]
 
 far = 10.0

+ 1 - 1
tests/reference_exports/light/animation_sun.escn

@@ -34,8 +34,8 @@ tracks/0/keys = {
 	"update":0,
 	"values":[1.0, 0.992688, 0.970082, 0.931923, 0.879461, 0.815882, 0.746055, 0.675477, 0.608971, 0.549881, 0.5, 0.452336, 0.399918, 0.34426, 0.287871, 0.234118, 0.186613, 0.14834, 0.121029, 0.105095, 0.1, 0.115518, 0.162222, 0.23816, 0.337483, 0.45, 0.562517, 0.66184, 0.737778, 0.784482, 0.8, 0.8, 0.8]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Plane" type="MeshInstance" parent="."]
 

+ 1 - 1
tests/reference_exports/light/animation_various_lights.escn

@@ -140,8 +140,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="SpotDistanceChange" type="SpotLight" parent="."]
 

+ 1 - 1
tests/reference_exports/light/cycles_lights.escn

@@ -18,8 +18,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="MissingEmission" type="OmniLight" parent="."]
 

+ 1 - 1
tests/reference_exports/light/just_point_lights.escn

@@ -56,8 +56,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Lamp003" type="OmniLight" parent="."]
 

+ 1 - 1
tests/reference_exports/light/just_spot_lights.escn

@@ -56,8 +56,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Lamp007" type="SpotLight" parent="."]
 

+ 2 - 1
tests/reference_exports/material/material_search.escn

@@ -1,4 +1,5 @@
 [gd_scene load_steps=1 format=2]
+
 [ext_resource id=1 path="../../uv_tester_material.tres" type="SpatialMaterial"]
 
 [sub_resource id=1 type="ArrayMesh"]
@@ -20,8 +21,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Cube" type="MeshInstance" parent="."]
 

+ 2 - 2
tests/reference_exports/material/object_link_material.escn

@@ -55,8 +55,8 @@ albedo_color = Color(0.903545, 0.169508, 0.242857, 1.0)
 
 resource_name = ""
 albedo_color = Color(0.121702, 0.903545, 0.139942, 1.0)
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Cube000" type="MeshInstance" parent="."]
 
@@ -68,6 +68,6 @@ transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.804941, -0.
 
 mesh = SubResource(3)
 visible = true
-transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, -0.0116182, 0.681681, -0.190806)
 material/1 = SubResource(4)
 material/0 = SubResource(5)
+transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, -0.0116182, 0.681681, -0.190806)

+ 1 - 1
tests/reference_exports/material/simple_materials.escn

@@ -45,8 +45,8 @@ surfaces/1 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Cube" type="MeshInstance" parent="."]
 

+ 1 - 1
tests/reference_exports/material_cycle/material_anistropy.escn

@@ -313,8 +313,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Point" type="DirectionalLight" parent="."]
 

File diff suppressed because it is too large
+ 82 - 34
tests/reference_exports/material_cycle/material_cycle.escn


+ 5 - 5
tests/reference_exports/material_cycle/material_normal.escn

@@ -1,6 +1,6 @@
 [gd_scene load_steps=1 format=2]
-[ext_resource id=1 path="Normal_OGL.png" type="Texture"]
 
+[ext_resource id=1 path="Normal_OGL.png" type="Texture"]
 
 [ext_resource id=2 path="bump.png" type="Texture"]
 
@@ -1105,8 +1105,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="test_normal_tangent" type="MeshInstance" parent="."]
 
@@ -1118,8 +1118,8 @@ transform = Transform(2.17434, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 2.089, 5.56855
 
 mesh = SubResource(6)
 visible = true
-transform = Transform(1.87169, 0.0, 0.0, 0.0, 1.87169, 0.0, 0.0, 0.0, 1.87169, -7.39736, 6.91874, -7.43003)
 material/0 = SubResource(8)
+transform = Transform(1.87169, 0.0, 0.0, 0.0, 1.87169, 0.0, 0.0, 0.0, 1.87169, -7.39736, 6.91874, -7.43003)
 
 [node name="test_mapping_texture" type="MeshInstance" parent="."]
 
@@ -1131,15 +1131,15 @@ transform = Transform(1.87169, 0.0, 0.0, 0.0, 1.87169, 0.0, 0.0, 0.0, 1.87169, -
 
 mesh = SubResource(6)
 visible = true
-transform = Transform(1.87169, 0.0, 0.0, 0.0, 1.87169, 0.0, 0.0, 0.0, 1.87169, -4.71194, 9.35422, 0.926832)
 material/0 = SubResource(10)
+transform = Transform(1.87169, 0.0, 0.0, 0.0, 1.87169, 0.0, 0.0, 0.0, 1.87169, -4.71194, 9.35422, 0.926832)
 
 [node name="test_mapping_point" type="MeshInstance" parent="."]
 
 mesh = SubResource(6)
 visible = true
-transform = Transform(1.87169, 0.0, 0.0, 0.0, 1.87169, 0.0, 0.0, 0.0, 1.87169, -2.75008, 9.34185, 5.68027)
 material/0 = SubResource(12)
+transform = Transform(1.87169, 0.0, 0.0, 0.0, 1.87169, 0.0, 0.0, 0.0, 1.87169, -2.75008, 9.34185, 5.68027)
 
 [node name="Lamp" type="DirectionalLight" parent="."]
 

+ 2 - 1
tests/reference_exports/material_cycle/material_unpack_texture.escn

@@ -1,4 +1,5 @@
 [gd_scene load_steps=1 format=2]
+
 [ext_resource id=1 path="brick_4_diff_1k.jpg" type="Texture"]
 
 [sub_resource id=1 type="Shader"]
@@ -96,8 +97,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Cube" type="MeshInstance" parent="."]
 

+ 1 - 1
tests/reference_exports/mesh/just_mesh.escn

@@ -56,8 +56,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Cylinder002" type="MeshInstance" parent="."]
 

+ 1 - 1
tests/reference_exports/mesh/parented_meshes.escn

@@ -18,8 +18,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Suzanne006" type="MeshInstance" parent="."]
 

+ 8 - 8
tests/reference_exports/mesh/physics.escn

@@ -155,8 +155,8 @@ surfaces/0 = {
 
 resource_name = "Sphere003Collision"
 radius = 0.5
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Cube002Physics" type="StaticBody" parent="."]
 
@@ -175,7 +175,7 @@ shape = SubResource(1)
 
 mesh = SubResource(2)
 visible = true
-transform = Transform(1.0, 0.0, 0.0, 0.0, 7.54979e-08, -1.0, 0.0, 1.0, 7.54979e-08, 0.0, 0.0, 0.0)
+transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, -1.0, 0.0, 1.0, -4.37114e-08, 0.0, 0.0, 0.0)
 
 [node name="Cube001Physics" type="StaticBody" parent="."]
 
@@ -211,7 +211,7 @@ shape = SubResource(4)
 
 mesh = SubResource(5)
 visible = true
-transform = Transform(1.0, 0.0, 0.0, 0.0, 7.54979e-08, -1.0, 0.0, 1.0, 7.54979e-08, 0.0, 0.0, 0.0)
+transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, -1.0, 0.0, 1.0, -4.37114e-08, 0.0, 0.0, 0.0)
 
 [node name="PlanePhysics" type="StaticBody" parent="."]
 
@@ -230,7 +230,7 @@ shape = SubResource(6)
 
 mesh = SubResource(7)
 visible = true
-transform = Transform(1.0, 0.0, 0.0, 0.0, 7.54979e-08, -1.0, 0.0, 1.0, 7.54979e-08, 0.0, 0.0, 0.0)
+transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, -1.0, 0.0, 1.0, -4.37114e-08, 0.0, 0.0, 0.0)
 
 [node name="CubePhysics" type="RigidBody" parent="."]
 
@@ -253,7 +253,7 @@ shape = SubResource(8)
 
 mesh = SubResource(9)
 visible = true
-transform = Transform(1.0, 0.0, 0.0, 0.0, 7.54979e-08, -1.0, 0.0, 1.0, 7.54979e-08, 0.0, 0.0, 0.0)
+transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, -1.0, 0.0, 1.0, -4.37114e-08, 0.0, 0.0, 0.0)
 
 [node name="SpherePhysics" type="RigidBody" parent="."]
 
@@ -276,7 +276,7 @@ shape = SubResource(10)
 
 mesh = SubResource(11)
 visible = true
-transform = Transform(1.0, 0.0, 0.0, 0.0, 7.54979e-08, -1.0, 0.0, 1.0, 7.54979e-08, 0.0, 0.0, 0.0)
+transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, -1.0, 0.0, 1.0, -4.37114e-08, 0.0, 0.0, 0.0)
 
 [node name="Sphere002Collision" type="CollisionShape" parent="SpherePhysics"]
 
@@ -287,7 +287,7 @@ shape = SubResource(12)
 
 mesh = SubResource(13)
 visible = true
-transform = Transform(1.0, 0.0, 0.0, 0.0, 7.54979e-08, -1.0, 0.0, 1.0, 7.54979e-08, 0.0, 0.0, 0.0)
+transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, -1.0, 0.0, 1.0, -4.37114e-08, 0.0, 0.0, 0.0)
 
 [node name="Sphere003Physics" type="RigidBody" parent="."]
 
@@ -310,7 +310,7 @@ shape = SubResource(14)
 
 mesh = SubResource(13)
 visible = true
-transform = Transform(1.0, 0.0, 0.0, 0.0, 7.54979e-08, -1.0, 0.0, 1.0, 7.54979e-08, 0.0, 0.0, 0.0)
+transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, -1.0, 0.0, 1.0, -4.37114e-08, 0.0, 0.0, 0.0)
 
 [node name="Lamp" type="OmniLight" parent="."]
 

+ 0 - 12
tests/reference_exports/mesh/single_edge_and_vertex.escn

@@ -1,23 +1,11 @@
 [gd_scene load_steps=1 format=2]
 
-[sub_resource id=1 type="ArrayMesh"]
-
-resource_name = "Cube001"
-
-[sub_resource id=2 type="ArrayMesh"]
-
-resource_name = "Cube"
 [node type="Spatial" name="Scene"]
 
-
 [node name="SingleEdge" type="MeshInstance" parent="."]
 
-mesh = SubResource(1)
-visible = true
 transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, -1.0, 0.0, 0.0)
 
 [node name="SingleVert" type="MeshInstance" parent="."]
 
-mesh = SubResource(2)
-visible = true
 transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0)

+ 1 - 1
tests/reference_exports/mesh/tangent_test.escn

@@ -18,8 +18,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Plane" type="MeshInstance" parent="."]
 

+ 1 - 1
tests/reference_exports/mesh/uv_testing.escn

@@ -75,8 +75,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Cube001" type="MeshInstance" parent="."]
 

+ 1 - 1
tests/reference_exports/mesh/vertex_color.escn

@@ -18,8 +18,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Plane" type="MeshInstance" parent="."]
 

+ 1 - 1
tests/reference_exports/misc/duplicate_name.escn

@@ -56,8 +56,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Cube" type="MeshInstance" parent="."]
 

File diff suppressed because it is too large
+ 0 - 0
tests/reference_exports/misc/invisible_objects.escn


File diff suppressed because it is too large
+ 0 - 0
tests/reference_exports/nla_animation/animation_multi_strip.escn


File diff suppressed because it is too large
+ 0 - 0
tests/reference_exports/nla_animation/animation_with_empty_strip.escn


File diff suppressed because it is too large
+ 0 - 0
tests/reference_exports/nla_animation/nla_with_active_action.escn


+ 1 - 1
tests/reference_exports/nla_animation/nla_with_no_active_action.escn

@@ -66,8 +66,8 @@ tracks/1/keys = {
 	"update":0,
 	"values":[0.0, 0.119438, 0.353741, 0.611825, 0.891865, 1.0, 0.935595, 0.756443, 0.564626, 0.341587, 0.095626, 0.0]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Suzanne" type="MeshInstance" parent="."]
 

File diff suppressed because it is too large
+ 0 - 0
tests/reference_exports/nla_animation/nla_with_stashed_action.escn


+ 1 - 1
tests/reference_exports/scene_animation/animation_parented_objects.escn

@@ -52,8 +52,8 @@ surfaces/0 = {
 	],
 	"morph_arrays":[]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Cube" type="MeshInstance" parent="."]
 

+ 1 - 1
tests/reference_exports/shape_key/animation_shapekey.escn

@@ -46,8 +46,8 @@ tracks/0/keys = {
 	"update":0,
 	"values":[0.0, 0.00261531, 0.0105104, 0.0237313, 0.0422801, 0.0661052, 0.095091, 0.129048, 0.167705, 0.2107, 0.257579, 0.307793, 0.360707, 0.415611, 0.471734, 0.528267, 0.584389, 0.639293, 0.692207, 0.742421, 0.7893, 0.832295, 0.870952, 0.904909, 0.933895, 0.95772, 0.976269, 0.98949, 0.997385, 1.0, 0.997557, 0.990181, 0.977831, 0.9605, 0.938228, 0.911111, 0.879307, 0.843044, 0.802629, 0.758447, 0.710968, 0.660738, 0.608376, 0.554558, 0.5, 0.445442, 0.391624, 0.339262, 0.289032, 0.241553, 0.197372, 0.156956, 0.120693, 0.088889, 0.0617719, 0.0395004, 0.0221692, 0.00981867, 0.00244331, 0.0]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Suzanne002" type="MeshInstance" parent="."]
 

File diff suppressed because it is too large
+ 0 - 0
tests/reference_exports/shape_key/animation_shapekey_with_transform.escn


+ 1 - 1
tests/reference_exports/shape_key/just_shapekey.escn

@@ -30,8 +30,8 @@ surfaces/0 = {
 		null, ; Morph Object
 	]]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Suzanne002" type="MeshInstance" parent="."]
 

+ 1 - 1
tests/reference_exports/shape_key/shapekey_with_multi_surface.escn

@@ -67,8 +67,8 @@ surfaces/1 = {
 		null, ; Morph Object
 	]]
 }
-[node type="Spatial" name="Scene"]
 
+[node type="Spatial" name="Scene"]
 
 [node name="Suzanne" type="MeshInstance" parent="."]
 

File diff suppressed because it is too large
+ 0 - 0
tests/reference_exports/shape_key/shapekey_with_pose.escn


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


BIN
tests/test_scenes/armature/armature_not_ancester_of_mesh.blend


BIN
tests/test_scenes/shape_key/shapekey_with_subdivision.blend


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