瀏覽代碼

refactor mesh exporting

Jason0214 7 年之前
父節點
當前提交
a640876ba8
共有 27 個文件被更改,包括 317 次插入337 次删除
  1. 1 1
      io_scene_godot/__init__.py
  2. 0 9
      io_scene_godot/converters/armature.py
  3. 292 282
      io_scene_godot/converters/mesh.py
  4. 17 0
      io_scene_godot/structures.py
  5. 1 2
      tests/reference_exports/animation_bone_transform.escn
  6. 0 1
      tests/reference_exports/animation_object_transform.escn
  7. 0 2
      tests/reference_exports/animation_parented_objects.escn
  8. 0 1
      tests/reference_exports/animation_rotation_euler.escn
  9. 0 1
      tests/reference_exports/armature_bone_attachment.escn
  10. 1 2
      tests/reference_exports/armature_with_mesh.escn
  11. 2 4
      tests/reference_exports/armature_with_non_deform_bone.escn
  12. 1 2
      tests/reference_exports/armature_with_other_vertex_groups.escn
  13. 1 2
      tests/reference_exports/armature_with_physics.escn
  14. 1 2
      tests/reference_exports/armature_with_pose.escn
  15. 0 3
      tests/reference_exports/duplicate_name.escn
  16. 0 3
      tests/reference_exports/just_mesh.escn
  17. 0 3
      tests/reference_exports/just_point_lights.escn
  18. 0 1
      tests/reference_exports/just_shapekey.escn
  19. 0 3
      tests/reference_exports/just_spot_lights.escn
  20. 0 1
      tests/reference_exports/material_search.escn
  21. 0 1
      tests/reference_exports/parented_meshes.escn
  22. 0 1
      tests/reference_exports/physics.escn
  23. 0 2
      tests/reference_exports/shapekey_with_multi_surface.escn
  24. 0 2
      tests/reference_exports/simple_materials.escn
  25. 0 1
      tests/reference_exports/tangent_test.escn
  26. 0 4
      tests/reference_exports/uv_testing.escn
  27. 0 1
      tests/reference_exports/vertex_color.escn

+ 1 - 1
io_scene_godot/__init__.py

@@ -72,7 +72,7 @@ class ExportGodot(bpy.types.Operator, ExportHelper):
         },
         },
     )
     )
 
 
-    export_shape_key = BoolProperty(
+    use_export_shape_key = BoolProperty(
         name="Export Shape Key",
         name="Export Shape Key",
         description="Export all the shape keys in mesh objects",
         description="Export all the shape keys in mesh objects",
         default=True,
         default=True,

+ 0 - 9
io_scene_godot/converters/armature.py

@@ -25,15 +25,6 @@ def export_bone_attachment(escn_file, node, parent_gd_node):
     return bone_attachment
     return bone_attachment
 
 
 
 
-def get_armature_data(node):
-    """Get the armature modifier of a blender object
-    if does not have one, return None"""
-    for modifier in node.modifiers:
-        if modifier.type.lower() == 'armature':
-            return modifier.object.data
-    return None
-
-
 def find_skeletion_node(node):
 def find_skeletion_node(node):
     """Return the cloest Skeleton from node to root,
     """Return the cloest Skeleton from node to root,
     if not found, return None"""
     if not found, return None"""

+ 292 - 282
io_scene_godot/converters/mesh.py

@@ -5,7 +5,7 @@ import mathutils
 
 
 from .material import export_material
 from .material import export_material
 from ..structures import (Array, NodeTemplate, InternalResource, NodePath,
 from ..structures import (Array, NodeTemplate, InternalResource, NodePath,
-                          ValidationError)
+                          ValidationError, Map)
 from . import physics
 from . import physics
 from . import armature
 from . import armature
 
 
@@ -32,26 +32,21 @@ def export_mesh_node(escn_file, export_settings, node, parent_gd_node):
         return parent_gd_node
         return parent_gd_node
 
 
     else:
     else:
-        armature_data = armature.get_armature_data(node)
+        mesh_node = NodeTemplate(node.name, "MeshInstance", parent_gd_node)
+        mesh_exporter = MeshResourceExporter(node)
 
 
-        skeleton_node = None
+        armature_data = get_modifier_armature_data(node)
         if ("ARMATURE" in export_settings['object_types'] and
         if ("ARMATURE" in export_settings['object_types'] and
                 armature_data is not None):
                 armature_data is not None):
             skeleton_node = armature.find_skeletion_node(parent_gd_node)
             skeleton_node = armature.find_skeletion_node(parent_gd_node)
+            mesh_exporter.init_mesh_bones_data(skeleton_node)
+            mesh_node['skeleton'] = NodePath(
+                mesh_node.get_path(), skeleton_node.get_path())
 
 
-        mesh_id = export_mesh(
-            escn_file,
-            export_settings,
-            skeleton_node,
-            node
-        )
+        mesh_id = mesh_exporter.export_mesh(escn_file, export_settings)
 
 
-        mesh_node = NodeTemplate(node.name, "MeshInstance", parent_gd_node)
         mesh_node['mesh'] = "SubResource({})".format(mesh_id)
         mesh_node['mesh'] = "SubResource({})".format(mesh_id)
         mesh_node['visible'] = not node.hide
         mesh_node['visible'] = not node.hide
-        if skeleton_node is not None:
-            mesh_node['skeleton'] = NodePath(
-                mesh_node.get_path(), skeleton_node.get_path())
         if not physics.has_physics(node) or not physics.is_physics_root(node):
         if not physics.has_physics(node) or not physics.is_physics_root(node):
             mesh_node['transform'] = node.matrix_local
             mesh_node['transform'] = node.matrix_local
         else:
         else:
@@ -61,39 +56,6 @@ def export_mesh_node(escn_file, export_settings, node, parent_gd_node):
         return mesh_node
         return mesh_node
 
 
 
 
-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
-    mesh_id = escn_file.get_internal_resource(mesh)
-
-    if mesh_id is not None:
-        return mesh_id
-
-    mesh_resource = InternalResource('ArrayMesh')
-
-    surfaces = make_arrays(
-        escn_file,
-        export_settings,
-        skeleton_node,
-        node)
-
-    if export_settings['export_shape_key'] and node.data.shape_keys:
-        mesh_resource["blend_shape/names"] = Array(prefix="PoolStringArray(")
-        mesh_resource["blend_shape/mode"] = 0
-        for _, shape_key in extract_shape_keys(node.data.shape_keys):
-            mesh_resource["blend_shape/names"].append(
-                '"{}"'.format(shape_key.name))
-
-    for surface in surfaces:
-        mesh_resource[surface.name_str] = surface
-
-    mesh_id = escn_file.add_internal_resource(mesh_resource, mesh)
-    assert mesh_id is not None
-
-    return mesh_id
-
-
 def triangulate_mesh(mesh):
 def triangulate_mesh(mesh):
     """Triangulate a mesh"""
     """Triangulate a mesh"""
     tri_mesh = bmesh.new()
     tri_mesh = bmesh.new()
@@ -105,270 +67,284 @@ def triangulate_mesh(mesh):
     mesh.update(calc_tessface=True)
     mesh.update(calc_tessface=True)
 
 
 
 
-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 skeleton_node is not None:
-        # the bone's index in the bones list is exported as the id
+def fix_vertex(vtx):
+    """Changes a single position vector from y-up to z-up"""
+    return mathutils.Vector((vtx.x, vtx.z, -vtx.y))
+
+
+def get_modifier_armature_data(mesh_object):
+    """Get the armature modifier of a blender object
+    if does not have one, return None"""
+    for modifier in mesh_object.modifiers:
+        if isinstance(modifier, bpy.types.ArmatureModifier):
+            return modifier.object.data
+    return None
+
+
+class MeshResourceExporter:
+    """Export a mesh resource from a blender mesh object"""
+    def __init__(self, mesh_object):
+        # blender mesh object
+        self.object = mesh_object
+
+        self.mesh_resource = None
+        self.has_tangents = False
+        self.vgroup_to_bone_mapping = dict()
+        self.mat_to_surf_mapping = dict()
+
+    def init_mesh_bones_data(self, skeleton_node):
+        """Find the mapping relation between vertex groups
+        and bone id"""
         for bone_name, bone_id in skeleton_node.bone_name_to_id_map.items():
         for bone_name, bone_id in skeleton_node.bone_name_to_id_map.items():
-            group = vertex_groups.get(bone_name)
+            group = self.object.vertex_groups.get(bone_name)
             if group is not None:
             if group is not None:
-                ret[group.index] = bone_id
-    return ret
+                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)
 
 
-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'
-        bpy.context.scene.update()
+        if mesh_id is not None:
+            return mesh_id
 
 
-    if not export_settings['use_mesh_modifiers']:
-        for modifier in node.modifiers:
-            if not isinstance(modifier, bpy.types.ArmatureModifier):
-                modifier.show_render = False
+        self.mesh_resource = InternalResource('ArrayMesh')
 
 
-    mesh = node.to_mesh(bpy.context.scene,
-                        True,
-                        "RENDER")
+        try:
+            self.make_arrays(
+                escn_file,
+                export_settings,
+            )
+        except ValidationError as exception:
+            exception.args += (mesh.name, )
+            raise
 
 
-    # Prepare the mesh for export
-    triangulate_mesh(mesh)
+        mesh_id = escn_file.add_internal_resource(self.mesh_resource, mesh)
+        assert mesh_id is not None
 
 
-    uv_layer_count = len(mesh.uv_textures)
-    if uv_layer_count > 2:
-        uv_layer_count = 2
+        return mesh_id
 
 
-    if mesh.uv_textures:
-        has_tangents = True
-        mesh.calc_tangents()
-    else:
-        mesh.calc_normals_split()
-        has_tangents = False
+    def make_arrays(self, escn_file, export_settings):
+        """Generates arrays of positions, normals etc"""
+        armature_data = get_modifier_armature_data(self.object)
+        if armature_data is not None:
+            original_pose_position = armature_data.pose_position
+            armature_data.pose_position = 'REST'
+            bpy.context.scene.update()
 
 
-    # find the vertex group id of bone weights
-    gid_to_bid_map = find_bone_vertex_groups(
-        node.vertex_groups, skeleton_node)
+        if not export_settings['use_mesh_modifiers']:
+            for modifier in self.object.modifiers:
+                if not isinstance(modifier, bpy.types.ArmatureModifier):
+                    modifier.show_render = False
 
 
-    # Separate by materials into single-material surfaces
-    surfaces = generate_surfaces(
-        escn_file,
-        export_settings,
-        mesh,
-        has_tangents,
-        gid_to_bid_map
-    )
+        mesh = self.object.to_mesh(bpy.context.scene,
+                                   True,
+                                   "RENDER")
 
 
-    if (export_settings['export_shape_key'] and
-            node.data.shape_keys is not None):
-        export_morphs(
-            export_settings, node, surfaces, has_tangents, gid_to_bid_map)
+        # Prepare the mesh for export
+        triangulate_mesh(mesh)
 
 
-    has_bone = True if armature_data is not None else False
+        # godot engine supports two uv channels
+        uv_layer_count = min(len(mesh.uv_textures), 2)
 
 
-    for surface_id, surface in enumerate(surfaces):
-        surface.id = surface_id
-        surface.vertex_data.has_bone = has_bone
-        for vert_data in surface.morph_arrays:
-            vert_data.has_bone = has_bone
+        if mesh.uv_textures:
+            self.has_tangents = True
+            mesh.calc_tangents()
+        else:
+            mesh.calc_normals_split()
+            self.has_tangents = False
 
 
-    bpy.data.meshes.remove(mesh)
+        # Separate by materials into single-material surfaces
+        self.generate_surfaces(
+            escn_file,
+            export_settings,
+            mesh
+        )
 
 
-    if armature_data is not None:
-        armature_data.pose_position = original_pose_position
-        bpy.context.scene.update()
+        bpy.data.meshes.remove(mesh)
 
 
-    return surfaces
+        if armature_data is not None:
+            armature_data.pose_position = original_pose_position
+            bpy.context.scene.update()
 
 
+    @staticmethod
+    def extract_shape_keys(blender_shape_keys):
+        """Return a list of (shape_key_index, shape_key_object) each of them
+        is a shape key needs exported"""
+        # base shape key needn't be exported
+        ret = list()
+        base_key = blender_shape_keys.reference_key
+        for index, shape_key in enumerate(blender_shape_keys.key_blocks):
+            if shape_key != base_key:
+                ret.append((index, shape_key))
+        return ret
 
 
-def extract_shape_keys(blender_shape_keys):
-    """Return a list of (shape_key_index, shape_key_object) each of them
-    is a shape key needs exported"""
-    # base shape key needn't be exported
-    ret = list()
-    base_key = blender_shape_keys.reference_key
-    for index, shape_key in enumerate(blender_shape_keys.key_blocks):
-        if shape_key != base_key:
-            ret.append((index, shape_key))
-    return ret
+    @staticmethod
+    def validate_morph_mesh_modifiers(mesh_object):
+        """Check whether a mesh has modifiers not
+        compatible with shape key"""
+        # this black list is not complete
+        modifiers_not_supported = (
+            bpy.types.SubsurfModifier,
+        )
+        for modifier in mesh_object.modifiers:
+            if isinstance(modifier, modifiers_not_supported):
+                return False
+        return True
 
 
+    @staticmethod
+    def intialize_surfaces_morph_data(surfaces):
+        """Initialize a list of empty morph for surfaces"""
+        surfaces_morph_list = [VerticesArrays() for _ in range(len(surfaces))]
 
 
-def intialize_surfaces_morph_data(surfaces):
-    """Initialize a list of empty morph for surfaces"""
-    surfaces_morph_list = [VerticesArrays() for _ in range(len(surfaces))]
+        for index, morph in enumerate(surfaces_morph_list):
+            morph.vertices = [None] * len(surfaces[index].vertex_data.vertices)
 
 
-    for index, morph in enumerate(surfaces_morph_list):
-        morph.vertices = [None] * len(surfaces[index].vertex_data.vertices)
+        return surfaces_morph_list
 
 
-    return surfaces_morph_list
+    def export_morphs(self, export_settings, surfaces):
+        """Export shape keys in mesh node and append them to surfaces"""
+        if export_settings['use_mesh_modifiers']:
+            if not self.validate_morph_mesh_modifiers(
+                    self.object):
+                raise ValidationError(
+                    "Mesh object '{}' has modifiers "
+                    "incompatible with shape key".format(self.object.name)
+                )
 
 
+        self.mesh_resource["blend_shape/names"] = Array(
+            prefix="PoolStringArray(", suffix=')'
+        )
+        self.mesh_resource["blend_shape/mode"] = 0
 
 
-def validate_morph_mesh_modifiers(mesh_object):
-    """Check whether a mesh has modifiers not
-    compatible with shape key"""
-    # this black list is not complete
-    modifiers_not_supported = (
-        bpy.types.SubsurfModifier,
-    )
-    for modifier in mesh_object.modifiers:
-        if isinstance(modifier, modifiers_not_supported):
-            return False
-    return True
+        shape_keys_to_export = self.extract_shape_keys(
+            self.object.data.shape_keys
+        )
+        for index, shape_key in shape_keys_to_export:
+            self.mesh_resource["blend_shape/names"].append(
+                '"{}"'.format(shape_key.name)
+            )
 
 
+            self.object.show_only_shape_key = True
+            self.object.active_shape_key_index = index
+            shape_key.value = 1.0
 
 
-def export_morphs(export_settings, node, surfaces, has_tangents,
-                  gid_to_bid_map):
-    """Export shape keys in mesh node and append them to surfaces"""
-    if export_settings['use_mesh_modifiers']:
-        if not validate_morph_mesh_modifiers(node):
-            raise ValidationError(
-                "Mesh object '{}' has modifiers "
-                "incompatible with shape key".format(node.name)
+            shape_key_mesh = self.object.to_mesh(
+                bpy.context.scene,
+                True,
+                "RENDER"
             )
             )
-    for index, shape_key in extract_shape_keys(node.data.shape_keys):
-
-        node.show_only_shape_key = True
-        node.active_shape_key_index = index
-        shape_key.value = 1.0
 
 
-        mesh = node.to_mesh(bpy.context.scene,
-                            True,
-                            "RENDER")
-        triangulate_mesh(mesh)
+            triangulate_mesh(shape_key_mesh)
 
 
-        if has_tangents:
-            mesh.calc_tangents()
-        else:
-            mesh.calc_normals_split()
+            if self.has_tangents:
+                shape_key_mesh.calc_tangents()
+            else:
+                shape_key_mesh.calc_normals_split()
 
 
-        surfaces_morph_data = intialize_surfaces_morph_data(surfaces)
+            surfaces_morph_data = self.intialize_surfaces_morph_data(surfaces)
 
 
-        for face in mesh.polygons:
-            surface_index = -1
-            for surf_index, surf in enumerate(surfaces):
-                # todo:
-                # use the `material_to_surface` map from `generate_surfaces`
-                if surf.face_material_index == face.material_index:
-                    surface_index = surf_index
-                    break
+            for face in shape_key_mesh.polygons:
+                surface_index = self.mat_to_surf_mapping[face.material_index]
 
 
-            if surface_index != -1:
                 surface = surfaces[surface_index]
                 surface = surfaces[surface_index]
                 morph = surfaces_morph_data[surface_index]
                 morph = surfaces_morph_data[surface_index]
 
 
                 for loop_id in range(face.loop_total):
                 for loop_id in range(face.loop_total):
                     loop_index = face.loop_start + loop_id
                     loop_index = face.loop_start + loop_id
-                    new_vert = VerticesArrays.create_vertex_from_loop(
-                        mesh, loop_index, has_tangents, gid_to_bid_map
+                    new_vert = Vertex.create_from_mesh_loop(
+                        shape_key_mesh,
+                        loop_index,
+                        self.has_tangents,
+                        self.vgroup_to_bone_mapping
                     )
                     )
 
 
                     vertex_index = surface.vertex_index_map[loop_index]
                     vertex_index = surface.vertex_index_map[loop_index]
 
 
                     morph.vertices[vertex_index] = new_vert
                     morph.vertices[vertex_index] = new_vert
 
 
-        for surf_index, surf in enumerate(surfaces):
-            surf.morph_arrays.append(surfaces_morph_data[surf_index])
-
-        bpy.data.meshes.remove(mesh)
-
-
-def generate_surfaces(escn_file, export_settings, mesh, has_tangents,
-                      gid_to_bid_map):
-    """Splits up the mesh into surfaces with a single material each.
-    Within this, it creates the Vertex structure to contain all data about
-    a single vertex
-    """
-    material_to_surface = {}
-    surfaces = []
-
-    for face_index in range(len(mesh.polygons)):
-        face = mesh.polygons[face_index]
-
-        # Find a surface that matches the material, otherwise create a new
-        # surface for it
-        if face.material_index not in material_to_surface:
-            material_to_surface[face.material_index] = len(surfaces)
-            surface = Surface()
-            surface.face_material_index = face.material_index
-            surfaces.append(surface)
-            if mesh.materials:
-                mat = mesh.materials[face.material_index]
-                if mat is not None:
-                    surface.material = export_material(
-                        escn_file,
-                        export_settings,
-                        mat
-                    )
-
-        surface = surfaces[material_to_surface[face.material_index]]
-        vertex_indices = []
+            for surf_index, surf in enumerate(surfaces):
+                surf.morph_arrays.append(surfaces_morph_data[surf_index])
+
+            bpy.data.meshes.remove(shape_key_mesh)
+
+    def generate_surfaces(self, escn_file, export_settings, mesh):
+        """Splits up the mesh into surfaces with a single material each.
+        Within this, it creates the Vertex structure to contain all data about
+        a single vertex
+        """
+        surfaces = []
+
+        for face_index in range(len(mesh.polygons)):
+            face = mesh.polygons[face_index]
+
+            # Find a surface that matches the material, otherwise create a new
+            # surface for it
+            if face.material_index not in self.mat_to_surf_mapping:
+                self.mat_to_surf_mapping[face.material_index] = len(surfaces)
+                surface = Surface()
+                surfaces.append(surface)
+                if mesh.materials:
+                    mat = mesh.materials[face.material_index]
+                    if mat is not None:
+                        surface.material = export_material(
+                            escn_file,
+                            export_settings,
+                            mat
+                        )
+
+            surface = surfaces[self.mat_to_surf_mapping[face.material_index]]
+            vertex_indices = []
+
+            for loop_id in range(face.loop_total):
+                loop_index = face.loop_start + loop_id
+
+                new_vert = Vertex.create_from_mesh_loop(
+                    mesh,
+                    loop_index,
+                    self.has_tangents,
+                    self.vgroup_to_bone_mapping
+                )
 
 
-        for loop_id in range(face.loop_total):
-            loop_index = face.loop_start + loop_id
+                # Merge similar vertices
+                tup = new_vert.get_tup()
+                if tup not in surface.vertex_map:
+                    surface.vertex_map[tup] = len(surface.vertex_data.vertices)
+                    surface.vertex_data.vertices.append(new_vert)
 
 
-            new_vert = VerticesArrays.create_vertex_from_loop(
-                mesh, loop_index, has_tangents, gid_to_bid_map)
+                vertex_index = surface.vertex_map[tup]
+                surface.vertex_index_map[loop_index] = vertex_index
 
 
-            # Merge similar vertices
-            tup = new_vert.get_tup()
-            if tup not in surface.vertex_map:
-                surface.vertex_map[tup] = len(surface.vertex_data.vertices)
-                surface.vertex_data.vertices.append(new_vert)
+                vertex_indices.append(vertex_index)
 
 
-            vertex_index = surface.vertex_map[tup]
-            surface.vertex_index_map[loop_index] = vertex_index
+            if len(vertex_indices) > 2:  # Only triangles and above
+                surface.vertex_data.indices.append(vertex_indices)
 
 
-            vertex_indices.append(vertex_index)
+        if (export_settings['use_export_shape_key'] and
+                self.object.data.shape_keys):
+            self.export_morphs(export_settings, surfaces)
 
 
-        if len(vertex_indices) > 2:  # Only triangles and above
-            surface.vertex_data.indices.append(vertex_indices)
+        has_bone = True if self.vgroup_to_bone_mapping else False
+        for surface_id, surface in enumerate(surfaces):
+            surface.id = surface_id
+            surface.vertex_data.has_bone = has_bone
+            for vert_array in surface.morph_arrays:
+                vert_array.has_bone = has_bone
 
 
-    return surfaces
+            self.mesh_resource[surface.name_str] = surface
 
 
 
 
 class VerticesArrays:
 class VerticesArrays:
     """Godot use several arrays to store the data of a surface(e.g. vertices,
     """Godot use several arrays to store the data of a surface(e.g. vertices,
     indices, bone weights). A surface object has a single VerticesArrays as its
     indices, bone weights). A surface object has a single VerticesArrays as its
     default and also may have a morph array with a list of VerticesArrays"""
     default and also may have a morph array with a list of VerticesArrays"""
+
     def __init__(self):
     def __init__(self):
         self.vertices = []
         self.vertices = []
         self.indices = []
         self.indices = []
         self.has_bone = False
         self.has_bone = False
 
 
-    @staticmethod
-    def create_vertex_from_loop(mesh, loop_index, has_tangents,
-                                gid_to_bid_map):
-        """Create a vertex from a blender mesh loop"""
-        new_vert = Vertex()
-
-        loop = mesh.loops[loop_index]
-        new_vert.vertex = fix_vertex(mesh.vertices[loop.vertex_index].co)
-
-        for uv_layer in mesh.uv_layers:
-            new_vert.uv.append(mathutils.Vector(
-                uv_layer.data[loop_index].uv
-            ))
-
-        if mesh.vertex_colors:
-            new_vert.color = mathutils.Vector(
-                mesh.vertex_colors[0].data[loop_index].color)
-
-        new_vert.normal = fix_vertex(loop.normal)
-
-        if has_tangents:
-            new_vert.tangent = fix_vertex(loop.tangent)
-            new_vert.bitangent = fix_vertex(loop.bitangent)
-
-        for vertex_group in mesh.vertices[loop.vertex_index].groups:
-            if vertex_group.group in gid_to_bid_map:
-                new_vert.bones.append(gid_to_bid_map[vertex_group.group])
-                new_vert.weights.append(vertex_group.weight)
-
-        return new_vert
-
     def calc_tangent_dp(self, vert):
     def calc_tangent_dp(self, vert):
         """Calculates the dot product of the tangent. I think this has
         """Calculates the dot product of the tangent. I think this has
         something to do with normal mapping"""
         something to do with normal mapping"""
@@ -425,7 +401,9 @@ class VerticesArrays:
     def generate_lines(self):
     def generate_lines(self):
         """Generates the various arrays that are part of the surface (eg
         """Generates the various arrays that are part of the surface (eg
         normals, position etc.)"""
         normals, position etc.)"""
-        surface_lines = []
+        surface_lines = Array(
+            prefix='[\n\t\t', seperator=',\n\t\t', suffix='\n\t]'
+        )
 
 
         position_vals = Array("Vector3Array(",
         position_vals = Array("Vector3Array(",
                               values=[v.vertex for v in self.vertices])
                               values=[v.vertex for v in self.vertices])
@@ -482,37 +460,33 @@ class VerticesArrays:
                 weights.append((vert.bones[i], vert.weights[i]))
                 weights.append((vert.bones[i], vert.weights[i]))
 
 
             weights = sorted(weights, key=lambda x: x[1], reverse=True)
             weights = sorted(weights, key=lambda x: x[1], reverse=True)
+
+            # totalw guaranteed to be not zero
             totalw = 0.0
             totalw = 0.0
             for index, weight in enumerate(weights):
             for index, weight in enumerate(weights):
                 if index >= MAX_BONE_PER_VERTEX:
                 if index >= MAX_BONE_PER_VERTEX:
                     break
                     break
                 totalw += weight[1]
                 totalw += weight[1]
 
 
-            if totalw > 0.0:
-                for i in range(MAX_BONE_PER_VERTEX):
-                    if i < len(weights):
-                        bone_idx_array.append(weights[i][0])
-                        bone_ws_array.append(weights[i][1]/totalw)
-                    else:
-                        bone_idx_array.append(0)
-                        bone_ws_array.append(0.0)
-            else:
-                # vertex not assign to any bones
-                raise ValidationError(
-                    "There are vertices with no bone weight in rigged mesh, "
-                    "please fix them in Blender"
-                )
+            for i in range(MAX_BONE_PER_VERTEX):
+                if i < len(weights):
+                    bone_idx_array.append(weights[i][0])
+                    bone_ws_array.append(weights[i][1]/totalw)
+                else:
+                    bone_idx_array.append(0)
+                    bone_ws_array.append(0.0)
 
 
         return bone_idx_array, bone_ws_array
         return bone_idx_array, bone_ws_array
 
 
     def to_string(self):
     def to_string(self):
         """Serialize"""
         """Serialize"""
-        return "[\n\t\t{}\n\t]".format(",\n\t\t".join(self.generate_lines()))
+        return self.generate_lines().to_string()
 
 
 
 
 class Surface:
 class Surface:
     """A surface is a single part of a mesh (eg in blender, one mesh can have
     """A surface is a single part of a mesh (eg in blender, one mesh can have
     multiple materials. Godot calls these separate parts separate surfaces"""
     multiple materials. Godot calls these separate parts separate surfaces"""
+
     def __init__(self):
     def __init__(self):
         # map from a Vertex.tup() to surface.vertex_data.indices
         # map from a Vertex.tup() to surface.vertex_data.indices
         self.vertex_map = dict()
         self.vertex_map = dict()
@@ -522,7 +496,6 @@ class Surface:
         self.vertex_index_map = dict()
         self.vertex_index_map = dict()
         self.id = None
         self.id = None
         self.material = None
         self.material = None
-        self.face_material_index = -1
 
 
     @property
     @property
     def name_str(self):
     def name_str(self):
@@ -530,29 +503,25 @@ class Surface:
         id"""
         id"""
         return "surfaces/" + str(self.id)
         return "surfaces/" + str(self.id)
 
 
-    def to_string(self):
-        """Serialize"""
-        out_str = "{\n"
+    def generate_object(self):
+        """Generate object with mapping structure which fully
+        describe the surface"""
+        surface_object = Map()
         if self.material is not None:
         if self.material is not None:
+            surface_object['material'] = self.material
+        surface_object['primitive'] = 4
+        surface_object['arrays'] = self.vertex_data
+        surface_object['morph_arrays'] = self.morph_arrays
+        return surface_object
 
 
-            out_str += "\t\"material\":" + self.material + ",\n"
-        out_str += "\t\"primitive\":4,\n"
-        out_str += "\t\"arrays\":" + self.vertex_data.to_string() + ",\n"
-        out_str += "\t" + "\"morph_arrays\":"
-        out_str += self.morph_arrays.to_string()
-        out_str += "\n"
-        out_str += "}\n"
-
-        return out_str
-
-
-def fix_vertex(vtx):
-    """Changes a single position vector from y-up to z-up"""
-    return mathutils.Vector((vtx.x, vtx.z, -vtx.y))
+    def to_string(self):
+        """Serialize"""
+        return self.generate_object().to_string()
 
 
 
 
 class Vertex:
 class Vertex:
     """Stores all the attributes for a single vertex"""
     """Stores all the attributes for a single vertex"""
+
     def get_tup(self):
     def get_tup(self):
         """Returns a tuple form of this vertex so that it can be hashed"""
         """Returns a tuple form of this vertex so that it can be hashed"""
         tup = (self.vertex.x, self.vertex.y, self.vertex.z, self.normal.x,
         tup = (self.vertex.x, self.vertex.y, self.vertex.z, self.normal.x,
@@ -573,6 +542,47 @@ class Vertex:
 
 
         return tup
         return tup
 
 
+    @classmethod
+    def create_from_mesh_loop(cls, mesh, loop_index, has_tangents,
+                              gid_to_bid_map):
+        """Create a vertex from a blender mesh loop"""
+        new_vert = cls()
+
+        loop = mesh.loops[loop_index]
+        new_vert.vertex = fix_vertex(mesh.vertices[loop.vertex_index].co)
+
+        for uv_layer in mesh.uv_layers:
+            new_vert.uv.append(mathutils.Vector(
+                uv_layer.data[loop_index].uv
+            ))
+
+        if mesh.vertex_colors:
+            new_vert.color = mathutils.Vector(
+                mesh.vertex_colors[0].data[loop_index].color)
+
+        new_vert.normal = fix_vertex(loop.normal)
+
+        if has_tangents:
+            new_vert.tangent = fix_vertex(loop.tangent)
+            new_vert.bitangent = fix_vertex(loop.bitangent)
+
+        for vertex_group in mesh.vertices[loop.vertex_index].groups:
+            if (vertex_group.group in gid_to_bid_map and
+                    vertex_group.weight != 0.0):
+                new_vert.bones.append(gid_to_bid_map[vertex_group.group])
+                new_vert.weights.append(vertex_group.weight)
+
+        if gid_to_bid_map and not new_vert.weights:
+            # vertex not assign to any bones
+            raise ValidationError(
+                "No bone assigned vertex detected, "
+                "vertex with local position {} ".format(
+                    mesh.vertices[loop.vertex_index].co
+                )
+            )
+
+        return new_vert
+
     __slots__ = ("vertex", "normal", "tangent", "bitangent", "color", "uv",
     __slots__ = ("vertex", "normal", "tangent", "bitangent", "color", "uv",
                  "bones", "weights")
                  "bones", "weights")
 
 

+ 17 - 0
io_scene_godot/structures.py

@@ -272,6 +272,23 @@ class Array(list):
         )
         )
 
 
 
 
+class Map(collections.OrderedDict):
+    """An ordered dict, used to serialize to a dict to escn file. Note
+    that the key should be string, but for the value will be applied
+    with to_string() method"""
+    def __init__(self):
+        super().__init__()
+
+        self.__str__ = self.to_string
+
+    def to_string(self):
+        """Convert the map to serialized form"""
+        return ("{\n\t" +
+                ',\n\t'.join(['"{}":{}'.format(k, to_string(v))
+                              for k, v in self.items()]) +
+                "\n}")
+
+
 class NodePath:
 class NodePath:
     """Node in scene points to other node or node's attribute,
     """Node in scene points to other node or node's attribute,
     for example, a MeshInstane points to a Skeleton. """
     for example, a MeshInstane points to a Skeleton. """

+ 1 - 2
tests/reference_exports/animation_bone_transform.escn

@@ -30,7 +30,6 @@ surfaces/0 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 
@@ -63,7 +62,7 @@ anims/ArmatureAction.001 = SubResource(1)
 
 
 [node name="Cylinder" type="MeshInstance" parent="Armature"]
 [node name="Cylinder" type="MeshInstance" parent="Armature"]
 
 
+skeleton = NodePath("..:")
 mesh = SubResource(2)
 mesh = SubResource(2)
 visible = true
 visible = true
-skeleton = NodePath("..:")
 transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.49012e-08, 1.1074, -1.49012e-08)
 transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.49012e-08, 1.1074, -1.49012e-08)

+ 0 - 1
tests/reference_exports/animation_object_transform.escn

@@ -28,7 +28,6 @@ surfaces/0 = {
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
 
 
-
 [sub_resource id=3 type="Animation"]
 [sub_resource id=3 type="Animation"]
 
 
 step = 0.1
 step = 0.1

+ 0 - 2
tests/reference_exports/animation_parented_objects.escn

@@ -28,7 +28,6 @@ surfaces/0 = {
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
 
 
-
 [sub_resource id=3 type="Animation"]
 [sub_resource id=3 type="Animation"]
 
 
 step = 0.1
 step = 0.1
@@ -59,7 +58,6 @@ surfaces/0 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 

+ 0 - 1
tests/reference_exports/animation_rotation_euler.escn

@@ -28,7 +28,6 @@ surfaces/0 = {
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
 
 
-
 [sub_resource id=3 type="Animation"]
 [sub_resource id=3 type="Animation"]
 
 
 step = 0.1
 step = 0.1

+ 0 - 1
tests/reference_exports/armature_bone_attachment.escn

@@ -17,7 +17,6 @@ surfaces/0 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 

+ 1 - 2
tests/reference_exports/armature_with_mesh.escn

@@ -17,7 +17,6 @@ surfaces/0 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 
@@ -39,7 +38,7 @@ bones/1/bound_children = []
 
 
 [node name="Suzanne" type="MeshInstance" parent="Armature001"]
 [node name="Suzanne" type="MeshInstance" parent="Armature001"]
 
 
+skeleton = NodePath("..:")
 mesh = SubResource(1)
 mesh = SubResource(1)
 visible = true
 visible = true
-skeleton = NodePath("..:")
 transform = Transform(1.22688, 0.0, 0.0, 0.0, 1.22688, 0.0, 0.0, 0.0, 1.22688, 0.0440081, 1.01822, -0.544986)
 transform = Transform(1.22688, 0.0, 0.0, 0.0, 1.22688, 0.0, 0.0, 0.0, 1.22688, 0.0440081, 1.01822, -0.544986)

+ 2 - 4
tests/reference_exports/armature_with_non_deform_bone.escn

@@ -43,7 +43,6 @@ surfaces/0 = {
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
 
 
-
 [sub_resource id=3 type="ArrayMesh"]
 [sub_resource id=3 type="ArrayMesh"]
 
 
 surfaces/0 = {
 surfaces/0 = {
@@ -61,7 +60,6 @@ surfaces/0 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 
@@ -94,9 +92,9 @@ anims/Armature.001Action = SubResource(1)
 
 
 [node name="Cube" type="MeshInstance" parent="Armature001"]
 [node name="Cube" type="MeshInstance" parent="Armature001"]
 
 
+skeleton = NodePath("..:")
 mesh = SubResource(2)
 mesh = SubResource(2)
 visible = true
 visible = true
-skeleton = NodePath("..:")
 transform = Transform(1.0, 0.0, 0.0, 2.98023e-08, 1.0, 0.0, -5.96046e-08, 0.0, 1.0, -0.06635, 0.614036, -0.079489)
 transform = Transform(1.0, 0.0, 0.0, 2.98023e-08, 1.0, 0.0, -5.96046e-08, 0.0, 1.0, -0.06635, 0.614036, -0.079489)
 
 
 [node name="Armature" type="Skeleton" parent="."]
 [node name="Armature" type="Skeleton" parent="."]
@@ -123,7 +121,7 @@ bones/2/bound_children = []
 
 
 [node name="Cylinder" type="MeshInstance" parent="Armature"]
 [node name="Cylinder" type="MeshInstance" parent="Armature"]
 
 
+skeleton = NodePath("..:")
 mesh = SubResource(3)
 mesh = SubResource(3)
 visible = true
 visible = true
-skeleton = NodePath("..:")
 transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.826031, 0.0)
 transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.826031, 0.0)

+ 1 - 2
tests/reference_exports/armature_with_other_vertex_groups.escn

@@ -17,7 +17,6 @@ surfaces/0 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 
@@ -39,7 +38,7 @@ bones/1/bound_children = []
 
 
 [node name="Cylinder" type="MeshInstance" parent="Armature"]
 [node name="Cylinder" type="MeshInstance" parent="Armature"]
 
 
+skeleton = NodePath("..:")
 mesh = SubResource(1)
 mesh = SubResource(1)
 visible = true
 visible = true
-skeleton = NodePath("..:")
 transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, -0.0250223, 0.657771, 0.060578)
 transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, -0.0250223, 0.657771, 0.060578)

+ 1 - 2
tests/reference_exports/armature_with_physics.escn

@@ -21,7 +21,6 @@ surfaces/0 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 
@@ -84,7 +83,7 @@ bones/5/bound_children = []
 
 
 [node name="Mesh" type="MeshInstance" parent="PhysicsPhysics/Rig"]
 [node name="Mesh" type="MeshInstance" parent="PhysicsPhysics/Rig"]
 
 
+skeleton = NodePath("..:")
 mesh = SubResource(2)
 mesh = SubResource(2)
 visible = true
 visible = true
-skeleton = NodePath("..:")
 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)
 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 - 2
tests/reference_exports/armature_with_pose.escn

@@ -17,7 +17,6 @@ surfaces/0 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 
@@ -39,7 +38,7 @@ bones/1/bound_children = []
 
 
 [node name="Suzanne" type="MeshInstance" parent="Armature001"]
 [node name="Suzanne" type="MeshInstance" parent="Armature001"]
 
 
+skeleton = NodePath("..:")
 mesh = SubResource(1)
 mesh = SubResource(1)
 visible = true
 visible = true
-skeleton = NodePath("..:")
 transform = Transform(1.22688, 0.0, 0.0, 0.0, 1.22688, 0.0, 0.0, 0.0, 1.22688, 0.0440081, 1.01822, -0.544986)
 transform = Transform(1.22688, 0.0, 0.0, 0.0, 1.22688, 0.0, 0.0, 0.0, 1.22688, 0.0440081, 1.01822, -0.544986)

+ 0 - 3
tests/reference_exports/duplicate_name.escn

@@ -18,7 +18,6 @@ surfaces/0 = {
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
 
 
-
 [sub_resource id=2 type="ArrayMesh"]
 [sub_resource id=2 type="ArrayMesh"]
 
 
 surfaces/0 = {
 surfaces/0 = {
@@ -37,7 +36,6 @@ surfaces/0 = {
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
 
 
-
 [sub_resource id=3 type="SpatialMaterial"]
 [sub_resource id=3 type="SpatialMaterial"]
 
 
 flags_unshaded = false
 flags_unshaded = false
@@ -65,7 +63,6 @@ surfaces/0 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 

+ 0 - 3
tests/reference_exports/just_mesh.escn

@@ -18,7 +18,6 @@ surfaces/0 = {
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
 
 
-
 [sub_resource id=2 type="ArrayMesh"]
 [sub_resource id=2 type="ArrayMesh"]
 
 
 surfaces/0 = {
 surfaces/0 = {
@@ -37,7 +36,6 @@ surfaces/0 = {
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
 
 
-
 [sub_resource id=3 type="ArrayMesh"]
 [sub_resource id=3 type="ArrayMesh"]
 
 
 surfaces/0 = {
 surfaces/0 = {
@@ -55,7 +53,6 @@ surfaces/0 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 

+ 0 - 3
tests/reference_exports/just_point_lights.escn

@@ -18,7 +18,6 @@ surfaces/0 = {
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
 
 
-
 [sub_resource id=2 type="ArrayMesh"]
 [sub_resource id=2 type="ArrayMesh"]
 
 
 surfaces/0 = {
 surfaces/0 = {
@@ -37,7 +36,6 @@ surfaces/0 = {
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
 
 
-
 [sub_resource id=3 type="ArrayMesh"]
 [sub_resource id=3 type="ArrayMesh"]
 
 
 surfaces/0 = {
 surfaces/0 = {
@@ -55,7 +53,6 @@ surfaces/0 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 

+ 0 - 1
tests/reference_exports/just_shapekey.escn

@@ -29,7 +29,6 @@ surfaces/0 = {
 		null, ; Morph Object
 		null, ; Morph Object
 	]]
 	]]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 

+ 0 - 3
tests/reference_exports/just_spot_lights.escn

@@ -18,7 +18,6 @@ surfaces/0 = {
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
 
 
-
 [sub_resource id=2 type="ArrayMesh"]
 [sub_resource id=2 type="ArrayMesh"]
 
 
 surfaces/0 = {
 surfaces/0 = {
@@ -37,7 +36,6 @@ surfaces/0 = {
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
 
 
-
 [sub_resource id=3 type="ArrayMesh"]
 [sub_resource id=3 type="ArrayMesh"]
 
 
 surfaces/0 = {
 surfaces/0 = {
@@ -55,7 +53,6 @@ surfaces/0 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 

+ 0 - 1
tests/reference_exports/material_search.escn

@@ -19,7 +19,6 @@ surfaces/0 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 

+ 0 - 1
tests/reference_exports/parented_meshes.escn

@@ -17,7 +17,6 @@ surfaces/0 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 

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


+ 0 - 2
tests/reference_exports/shapekey_with_multi_surface.escn

@@ -48,7 +48,6 @@ surfaces/0 = {
 		null, ; Morph Object
 		null, ; Morph Object
 	]]
 	]]
 }
 }
-
 surfaces/1 = {
 surfaces/1 = {
 	"material":SubResource(2),
 	"material":SubResource(2),
 	"primitive":4,
 	"primitive":4,
@@ -75,7 +74,6 @@ surfaces/1 = {
 		null, ; Morph Object
 		null, ; Morph Object
 	]]
 	]]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 

+ 0 - 2
tests/reference_exports/simple_materials.escn

@@ -36,7 +36,6 @@ surfaces/0 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 surfaces/1 = {
 surfaces/1 = {
 	"material":SubResource(2),
 	"material":SubResource(2),
 	"primitive":4,
 	"primitive":4,
@@ -53,7 +52,6 @@ surfaces/1 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 

+ 0 - 1
tests/reference_exports/tangent_test.escn

@@ -17,7 +17,6 @@ surfaces/0 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 

+ 0 - 4
tests/reference_exports/uv_testing.escn

@@ -18,7 +18,6 @@ surfaces/0 = {
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
 
 
-
 [sub_resource id=2 type="ArrayMesh"]
 [sub_resource id=2 type="ArrayMesh"]
 
 
 surfaces/0 = {
 surfaces/0 = {
@@ -37,7 +36,6 @@ surfaces/0 = {
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
 
 
-
 [sub_resource id=3 type="ArrayMesh"]
 [sub_resource id=3 type="ArrayMesh"]
 
 
 surfaces/0 = {
 surfaces/0 = {
@@ -56,7 +54,6 @@ surfaces/0 = {
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
 
 
-
 [sub_resource id=4 type="ArrayMesh"]
 [sub_resource id=4 type="ArrayMesh"]
 
 
 surfaces/0 = {
 surfaces/0 = {
@@ -74,7 +71,6 @@ surfaces/0 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 

+ 0 - 1
tests/reference_exports/vertex_color.escn

@@ -17,7 +17,6 @@ surfaces/0 = {
 	],
 	],
 	"morph_arrays":[]
 	"morph_arrays":[]
 }
 }
-
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]
 
 
 
 

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