瀏覽代碼

support shapekey, light, camera value animation

Jason0214 7 年之前
父節點
當前提交
bb56ec8e7a
共有 30 個文件被更改,包括 1003 次插入144 次删除
  1. 348 37
      io_scene_godot/converters/animation.py
  2. 8 0
      io_scene_godot/converters/mesh.py
  3. 87 19
      io_scene_godot/converters/simple_nodes.py
  4. 1 3
      io_scene_godot/export_godot.py
  5. 9 0
      io_scene_godot/structures.py
  6. 1 1
      tests/reference_exports/animation_bone_transform.escn
  7. 101 0
      tests/reference_exports/animation_camera.escn
  8. 66 0
      tests/reference_exports/animation_light_type_change.escn
  9. 1 1
      tests/reference_exports/animation_object_transform.escn
  10. 1 1
      tests/reference_exports/animation_parented_objects.escn
  11. 91 0
      tests/reference_exports/animation_point_light_shadow.escn
  12. 1 1
      tests/reference_exports/animation_rotation_euler.escn
  13. 9 0
      tests/reference_exports/animation_shapekey.escn
  14. 87 0
      tests/reference_exports/animation_spot_light.escn
  15. 7 6
      tests/reference_exports/animation_spot_light_transform.escn
  16. 83 0
      tests/reference_exports/animation_sun.escn
  17. 1 1
      tests/reference_exports/armature_with_non_deform_bone.escn
  18. 6 4
      tests/reference_exports/just_armature.escn
  19. 7 2
      tests/reference_exports/just_cameras.escn
  20. 21 16
      tests/reference_exports/just_point_lights.escn
  21. 49 40
      tests/reference_exports/just_spot_lights.escn
  22. 6 4
      tests/reference_exports/material_search.escn
  23. 6 4
      tests/reference_exports/physics.escn
  24. 6 4
      tests/reference_exports/simple_materials.escn
  25. 二進制
      tests/test_scenes/animation_camera.blend
  26. 二進制
      tests/test_scenes/animation_light_type_change.blend
  27. 二進制
      tests/test_scenes/animation_point_light_shadow.blend
  28. 二進制
      tests/test_scenes/animation_shapekey.blend
  29. 二進制
      tests/test_scenes/animation_spot_light.blend
  30. 二進制
      tests/test_scenes/animation_sun.blend

+ 348 - 37
io_scene_godot/converters/animation.py

@@ -1,29 +1,70 @@
 """Export animation into Godot scene tree"""
 """Export animation into Godot scene tree"""
 import collections
 import collections
 import re
 import re
+import math
 import copy
 import copy
+from functools import partial
 import bpy
 import bpy
 import mathutils
 import mathutils
 from . import armature
 from . import armature
 from ..structures import (NodeTemplate, NodePath, fix_directional_transform,
 from ..structures import (NodeTemplate, NodePath, fix_directional_transform,
-                          InternalResource, Array, fix_matrix)
+                          InternalResource, Array, Map, fix_matrix)
 
 
+NEAREST_INTERPOLATION = 0
 LINEAR_INTERPOLATION = 1
 LINEAR_INTERPOLATION = 1
 
 
 
 
+# attribute converted as a bool, no interpolation
+CONVERT_AS_BOOL = 0
+# attribute converted as a float
+CONVERT_AS_FLOAT = 1
+# attribute is a vec or mat, mapping to several fcurves in animation
+CONVERT_AS_MULTI_VALUE = 2
+# a quad tuple contains information to convert an attribute
+# or a fcurve of blender to godot things
+AttributeConvertInfo = collections.namedtuple(
+    'AttributeConvertInfo',
+    ['bl_name', 'gd_name', 'converter_function', 'attribute_type']
+)
+
+
 class Track:
 class Track:
-    """Animation track, with a type track and a frame list
-    the element in frame list is not strictly typed, for example,
-    a transform track would have frame with type mathutils.Matrix()"""
-    def __init__(self, track_type, track_path, frame_begin, frame_list):
+    """Animation track, has track type, track path, interpolation
+    method, a list of frames and a list of frame values.
+
+    Note that element in value_list is not strictly typed, for example,
+    a transform track would have value with type mathutils.Matrix(),
+    while some track would just have a float value"""
+    def __init__(self, track_type, track_path,
+                 frames=(), values=()):
         self.type = track_type
         self.type = track_type
         self.path = track_path
         self.path = track_path
-        self.frame_begin = frame_begin
-        self.frames = frame_list
-
-    def last_frame(self):
-        """The number of last frame"""
-        return self.frame_begin + len(self.frames)
+        # default to linear
+        self.interp = LINEAR_INTERPOLATION
+        self.frames = list()
+        self.values = list()
+
+        for frame in frames:
+            self.frames.append(frame)
+        for value in values:
+            self.values.append(value)
+
+    def add_frame_data(self, frame, value):
+        """Add add frame to track"""
+        self.frames.append(frame)
+        self.values.append(value)
+
+    def frame_end(self):
+        """The frame number of last frame"""
+        if not self.frames:
+            return 0
+        return self.frames[-1]
+
+    def frame_begin(self):
+        """The frame number of first frame"""
+        if not self.frames:
+            return 0
+        return self.frames[0]
 
 
 
 
 class AnimationResource(InternalResource):
 class AnimationResource(InternalResource):
@@ -36,7 +77,7 @@ class AnimationResource(InternalResource):
 
 
     def add_track(self, track):
     def add_track(self, track):
         """add a track to animation resource"""
         """add a track to animation resource"""
-        track_length = track.last_frame() / bpy.context.scene.render.fps
+        track_length = track.frame_end() / bpy.context.scene.render.fps
         if track_length > self['length']:
         if track_length > self['length']:
             self['length'] = track_length
             self['length'] = track_length
 
 
@@ -44,12 +85,55 @@ class AnimationResource(InternalResource):
         self.track_count += 1
         self.track_count += 1
 
 
         self[track_id_str + '/type'] = '"{}"'.format(track.type)
         self[track_id_str + '/type'] = '"{}"'.format(track.type)
+        self[track_id_str + '/path'] = track.path
+        self[track_id_str + '/interp'] = track.interp
         if track.type == 'transform':
         if track.type == 'transform':
-            self[track_id_str + '/path'] = track.path
-            self[track_id_str + '/interp'] = LINEAR_INTERPOLATION
             self[track_id_str + '/keys'] = transform_frames_to_keys(
             self[track_id_str + '/keys'] = transform_frames_to_keys(
-                track.frame_begin, track.frames
+                track.frames, track.values, track.interp
             )
             )
+        elif track.type == 'value':
+            self[track_id_str + '/keys'] = value_frames_to_keys(
+                track.frames, track.values, track.interp
+            )
+
+    def add_track_via_attr_mapping(self, fcurves, conv_quad_tuple_list,
+                                   base_node_path):
+        """Accepts some attribute mapping relation between blender and godot,
+        and further call `add_simple_value_track` export tracks.
+
+        `conv_quad_tuple_list` is a list of quad tuple which compose of
+        of(bl_attr_name, gd_attr_name, converter_lambda, attr_type)"""
+        for item in conv_quad_tuple_list:
+            bl_attr, gd_attr, converter, val_type = item
+            # vector vallue animation need special treatment
+            if val_type in (CONVERT_AS_FLOAT, CONVERT_AS_BOOL):
+                if val_type == CONVERT_AS_FLOAT:
+                    track_builder = build_linear_interp_value_track
+                else:
+                    track_builder = build_const_interp_value_track
+                self.add_simple_value_track(
+                    fcurves,
+                    bl_attr,
+                    partial(
+                        track_builder,
+                        base_node_path.new_copy(gd_attr),
+                        converter
+                    )
+                )
+
+    def add_simple_value_track(self, fcurves, fcurve_data_path,
+                               fcurve_to_track_func):
+        """Add a simple value track into AnimationResource, simple value
+        track means it have a one-one mapping to fcurve.
+
+        Note that the fcurve_to_track_func is a partial of
+        function like build_linear_interp_value_track and
+        build_const_interp_value_track which create a track
+        from fcurve"""
+        fcurve = fcurves.find(fcurve_data_path)
+        if fcurve is not None:
+            new_track = fcurve_to_track_func(fcurve)
+            self.add_track(new_track)
 
 
 
 
 class AnimationPlayer(NodeTemplate):
 class AnimationPlayer(NodeTemplate):
@@ -78,22 +162,47 @@ class AnimationPlayer(NodeTemplate):
         return new_anim_resource
         return new_anim_resource
 
 
 
 
-def transform_frames_to_keys(first_frame, frame_list):
+def value_frames_to_keys(frame_list, value_list, interp):
+    """Serialize a value list to a track keys object"""
+    time_array = Array(prefix='PoolRealArray(', suffix=')')
+    transition_array = Array(prefix='PoolRealArray(', suffix=')')
+    value_array = Array(prefix='[', suffix=']')
+    for index, frame in enumerate(frame_list):
+        if (interp == LINEAR_INTERPOLATION and index > 0 and
+                value_list[index] == value_list[index - 1]):
+            continue
+
+        time = frame / bpy.context.scene.render.fps
+        time_array.append(time)
+        transition_array.append(1)
+        value_array.append(value_list[index])
+
+    keys_map = Map()
+    keys_map["times"] = time_array.to_string()
+    keys_map["transitions"] = transition_array.to_string()
+    keys_map["update"] = 0
+    keys_map["values"] = value_array.to_string()
+
+    return keys_map
+
+
+def transform_frames_to_keys(frame_list, value_list, interp):
     """Convert a list of transform matrix to the keyframes
     """Convert a list of transform matrix to the keyframes
     of an animation track"""
     of an animation track"""
     array = Array(prefix='[', suffix=']')
     array = Array(prefix='[', suffix=']')
-    for index, mat in enumerate(frame_list):
-        if index > 0 and frame_list[index] == frame_list[index - 1]:
+    for index, frame in enumerate(frame_list):
+        if (interp == LINEAR_INTERPOLATION and index > 0 and
+                value_list[index] == value_list[index - 1]):
             # do not export same keyframe
             # do not export same keyframe
             continue
             continue
 
 
-        frame = first_frame + index
         array.append(frame / bpy.context.scene.render.fps)
         array.append(frame / bpy.context.scene.render.fps)
 
 
         # transition default 1.0
         # transition default 1.0
         array.append(1.0)
         array.append(1.0)
 
 
         # convert from z-up to y-up
         # convert from z-up to y-up
+        mat = value_list[index]
         transform_mat = fix_matrix(mat)
         transform_mat = fix_matrix(mat)
         location = transform_mat.to_translation()
         location = transform_mat.to_translation()
         quaternion = transform_mat.to_quaternion()
         quaternion = transform_mat.to_quaternion()
@@ -160,9 +269,46 @@ def split_fcurve_data_path(data_path):
     return path_list[0], path_list[1]
     return path_list[0], path_list[1]
 
 
 
 
-def get_frame_range(action):
-    """Return the frame range of the action"""
-    return int(action.frame_range[0]), int(action.frame_range[1])
+def get_action_frame_range(action):
+    """Return the a tuple denoting the frame range of action"""
+    # in blender `last_frame` is included, here plus one to make it
+    # excluded to fit python convention
+    return int(action.frame_range[0]), int(action.frame_range[1]) + 1
+
+
+def get_fcurve_frame_range(fcurve):
+    """Return the a tuple denoting the frame range of fcurve"""
+    return int(fcurve.range()[0]), int(fcurve.range()[1]) + 1
+
+
+def build_const_interp_value_track(track_path, map_func, fcurve):
+    """Build a godot value track from a Blender const interpolation fcurve"""
+    track = Track('value', track_path)
+    track.interp = NEAREST_INTERPOLATION
+
+    if map_func is None:
+        for keyframe in fcurve.keyframe_points:
+            track.add_frame_data(int(keyframe.co[0]), keyframe.co[1])
+    else:
+        for keyframe in fcurve.keyframe_points:
+            track.add_frame_data(int(keyframe.co[0]), map_func(keyframe.co[1]))
+
+    return track
+
+
+def build_linear_interp_value_track(track_path, map_func, fcurve):
+    """Build a godot value track by evaluate every frame of Blender fcurve"""
+    track = Track('value', track_path)
+
+    frame_range = get_fcurve_frame_range(fcurve)
+    if map_func is None:
+        for frame in range(frame_range[0], frame_range[1]):
+            track.add_frame_data(frame, fcurve.evaluate(frame))
+    else:
+        for frame in range(frame_range[0], frame_range[1]):
+            track.add_frame_data(frame, map_func(fcurve.evaluate(frame)))
+
+    return track
 
 
 
 
 def export_transform_action(godot_node, animation_player,
 def export_transform_action(godot_node, animation_player,
@@ -213,12 +359,12 @@ def export_transform_action(godot_node, animation_player,
             sca_mat = mathutils.Matrix.Scale(1, 4, self.scale)
             sca_mat = mathutils.Matrix.Scale(1, 4, self.scale)
             return loc_mat * rot_mat * sca_mat
             return loc_mat * rot_mat * sca_mat
 
 
-    first_frame, last_frame = get_frame_range(action)
+    first_frame, last_frame = get_action_frame_range(action)
 
 
     # if no skeleton node exist, it will be None
     # if no skeleton node exist, it will be None
     skeleton_node = armature.find_skeletion_node(godot_node)
     skeleton_node = armature.find_skeletion_node(godot_node)
 
 
-    transform_frames_map = collections.OrderedDict()
+    transform_frame_values_map = collections.OrderedDict()
     for fcurve in action.fcurves:
     for fcurve in action.fcurves:
         # fcurve data are seperated into different channels,
         # fcurve data are seperated into different channels,
         # for example a transform action would have several fcurves
         # for example a transform action would have several fcurves
@@ -226,7 +372,7 @@ def export_transform_action(godot_node, animation_player,
         # are aggregated to object while being evaluted
         # are aggregated to object while being evaluted
         object_path, attribute = split_fcurve_data_path(fcurve.data_path)
         object_path, attribute = split_fcurve_data_path(fcurve.data_path)
 
 
-        if object_path not in transform_frames_map:
+        if object_path not in transform_frame_values_map:
             if attribute in TransformFrame.ATTRIBUTES:
             if attribute in TransformFrame.ATTRIBUTES:
 
 
                 default_frame = None
                 default_frame = None
@@ -253,37 +399,37 @@ def export_transform_action(godot_node, animation_player,
                         pose_bone.rotation_mode
                         pose_bone.rotation_mode
                     )
                     )
 
 
-                transform_frames_map[object_path] = [
+                transform_frame_values_map[object_path] = [
                     copy.deepcopy(default_frame)
                     copy.deepcopy(default_frame)
-                    for _ in range(last_frame - first_frame + 1)
+                    for _ in range(last_frame - first_frame)
                 ]
                 ]
 
 
         if attribute in TransformFrame.ATTRIBUTES:
         if attribute in TransformFrame.ATTRIBUTES:
 
 
-            for frame in range(first_frame, last_frame + 1):
-                transform_frames_map[
+            for frame in range(first_frame, last_frame):
+                transform_frame_values_map[
                     object_path][frame - first_frame].update(
                     object_path][frame - first_frame].update(
                         attribute,
                         attribute,
                         fcurve.array_index,
                         fcurve.array_index,
                         fcurve.evaluate(frame)
                         fcurve.evaluate(frame)
                     )
                     )
 
 
-    for object_path, frame_list in transform_frames_map.items():
+    for object_path, frame_value_list in transform_frame_values_map.items():
         if object_path == '':
         if object_path == '':
             # object_path equals '' represents node itself
             # object_path equals '' represents node itself
 
 
             # convert matrix_basis to matrix_local(parent space transform)
             # convert matrix_basis to matrix_local(parent space transform)
             if (godot_node.get_type()
             if (godot_node.get_type()
                     in ("SpotLight", "DirectionalLight", "Camera")):
                     in ("SpotLight", "DirectionalLight", "Camera")):
-                normalized_frame_list = [
+                normalized_value_list = [
                     fix_directional_transform(
                     fix_directional_transform(
                         blender_object.matrix_parent_inverse * x.to_matrix()
                         blender_object.matrix_parent_inverse * x.to_matrix()
-                    ) for x in frame_list
+                    ) for x in frame_value_list
                 ]
                 ]
             else:
             else:
-                normalized_frame_list = [
+                normalized_value_list = [
                     blender_object.matrix_parent_inverse *
                     blender_object.matrix_parent_inverse *
-                    x.to_matrix() for x in frame_list
+                    x.to_matrix() for x in frame_value_list
                 ]
                 ]
 
 
             track_path = NodePath(
             track_path = NodePath(
@@ -298,22 +444,184 @@ def export_transform_action(godot_node, animation_player,
                 blender_path_to_bone_name(object_path)
                 blender_path_to_bone_name(object_path)
             )
             )
 
 
-            normalized_frame_list = [x.to_matrix() for x in frame_list]
+            normalized_value_list = [x.to_matrix() for x in frame_value_list]
 
 
         animation_resource.add_track(
         animation_resource.add_track(
             Track(
             Track(
                 'transform',
                 'transform',
                 track_path,
                 track_path,
-                first_frame,
-                normalized_frame_list
+                range(first_frame, last_frame),
+                normalized_value_list
+            )
+        )
+
+
+def export_shapekey_action(godot_node, animation_player,
+                           blender_object, action, animation_resource):
+    """Export shapekey value action"""
+    first_frame, last_frame = get_action_frame_range(action)
+
+    for fcurve in action.fcurves:
+
+        object_path, attribute = split_fcurve_data_path(fcurve.data_path)
+
+        if attribute == 'value':
+            shapekey_name = re.search(r'key_blocks\["([^"]+)"\]',
+                                      object_path).group(1)
+
+            track_path = NodePath(
+                animation_player.parent.get_path(),
+                godot_node.get_path(),
+                "blend_shapes/{}".format(shapekey_name)
+            )
+
+            value_track = Track(
+                'value',
+                track_path,
             )
             )
+
+            for frame in range(first_frame, last_frame):
+                value_track.add_frame_data(frame, fcurve.evaluate(frame))
+
+            animation_resource.add_track(value_track)
+
+
+def export_light_action(light_node, animation_player,
+                        blender_lamp, action, animation_resource):
+    """Export light(lamp in Blender) action"""
+    if blender_lamp.animation_data is None:
+        return
+
+    first_frame, last_frame = get_action_frame_range(action)
+    base_node_path = NodePath(
+        animation_player.parent.get_path(), light_node.get_path()
+    )
+
+    animation_resource.add_simple_value_track(
+        action.fcurves, 'use_negative',
+        partial(
+            build_const_interp_value_track,
+            base_node_path.new_copy('light_negative'),
+            lambda x: x > 0.0,
         )
         )
+    )
+
+    animation_resource.add_track_via_attr_mapping(
+        action.fcurves,
+        light_node.attribute_conversion,
+        base_node_path
+    )
+
+    # color tracks is not one-one mapping to fcurve, they
+    # need to be treated like transform track
+    color_frame_values_map = collections.OrderedDict()
+
+    for fcurve in action.fcurves:
+        _, attribute = split_fcurve_data_path(fcurve.data_path)
+
+        if attribute in ('color', 'shadow_color'):
+            if attribute not in color_frame_values_map:
+                color_frame_values_map[attribute] = [
+                    mathutils.Color()
+                    for _ in range(first_frame, last_frame)
+                ]
+            color_list = color_frame_values_map[attribute]
+            for frame in range(first_frame, last_frame):
+                color_list[frame - first_frame][
+                    fcurve.array_index] = fcurve.evaluate(frame)
+
+    for attribute, frame_value_list in color_frame_values_map.items():
+        if attribute == 'color':
+            track_path = base_node_path.new_copy('light_color')
+        else:
+            track_path = base_node_path.new_copy('shadow_color')
+
+        animation_resource.add_track(
+            Track(
+                'value',
+                track_path,
+                range(first_frame, last_frame),
+                frame_value_list
+            )
+        )
+
+
+def export_camera_action(camera_node, animation_player,
+                         blender_cam, action, animation_resource):
+    """Export camera action"""
+    if blender_cam.animation_data is None:
+        return
+
+    first_frame, last_frame = get_action_frame_range(action)
+    base_node_path = NodePath(
+        animation_player.parent.get_path(), camera_node.get_path()
+    )
+
+    animation_resource.add_track_via_attr_mapping(
+        action.fcurves,
+        camera_node.attribute_conversion,
+        base_node_path
+    )
+
+    animation_resource.add_simple_value_track(
+        action.fcurves, 'type',
+        partial(
+            build_const_interp_value_track,
+            base_node_path.new_copy('projection'),
+            lambda x: 0 if x == 0.0 else 1,
+        )
+    )
+
+    # blender use sensor_width and f_lens to animate fov
+    # while godot directly use fov
+    fov_animated = False
+    focal_len_list = list()
+    sensor_size_list = list()
+
+    if action.fcurves.find('lens') is not None:
+        fcurve = action.fcurves.find('lens')
+        fov_animated = True
+        for frame in range(first_frame, last_frame):
+            focal_len_list.append(fcurve.evaluate(frame))
+    if action.fcurves.find('sensor_width') is not None:
+        fcurve = action.fcurves.find('sensor_width')
+        fov_animated = True
+        for frame in range(first_frame, last_frame):
+            sensor_size_list.append(fcurve.evaluate(frame))
+
+    if fov_animated:
+        # export fov track
+        if not focal_len_list:
+            focal_len_list = [blender_cam.lens
+                              for _ in range(first_frame, last_frame)]
+        if not sensor_size_list:
+            sensor_size_list = [blender_cam.sensor_width
+                                for _ in range(first_frame, last_frame)]
+
+        fov_list = list()
+        for index, flen in enumerate(focal_len_list):
+            fov_list.append(2 * math.degrees(
+                math.atan(
+                    sensor_size_list[index]/2/flen
+                )
+            ))
+
+        animation_resource.add_track(Track(
+            'value',
+            base_node_path.new_copy('fov'),
+            range(first_frame, last_frame),
+            fov_list
+        ))
+
 
 
 # ----------------------------------------------
 # ----------------------------------------------
 
 
 
 
 ACTION_EXPORTER_MAP = {
 ACTION_EXPORTER_MAP = {
     'transform': export_transform_action,
     'transform': export_transform_action,
+    'shapekey': export_shapekey_action,
+    'light': export_light_action,
+    'camera': export_camera_action,
 }
 }
 
 
 
 
@@ -322,6 +630,9 @@ def export_animation_data(escn_file, export_settings, godot_node,
     """Export the action and nla_tracks in blender_object.animation_data,
     """Export the action and nla_tracks in blender_object.animation_data,
     it will further call the action exporting function in AnimationDataExporter
     it will further call the action exporting function in AnimationDataExporter
     given by `func_name`"""
     given by `func_name`"""
+    if (blender_object.animation_data is None or
+            not export_settings['use_export_animation']):
+        return
     animation_player = get_animation_player(
     animation_player = get_animation_player(
         escn_file, export_settings, godot_node)
         escn_file, export_settings, godot_node)
 
 

+ 8 - 0
io_scene_godot/converters/mesh.py

@@ -8,6 +8,7 @@ from ..structures import (Array, NodeTemplate, InternalResource, NodePath,
                           ValidationError, Map)
                           ValidationError, Map)
 from . import physics
 from . import physics
 from . import armature
 from . import armature
+from . import animation
 
 
 MAX_BONE_PER_VERTEX = 4
 MAX_BONE_PER_VERTEX = 4
 
 
@@ -53,6 +54,13 @@ def export_mesh_node(escn_file, export_settings, node, parent_gd_node):
             mesh_node['transform'] = mathutils.Matrix.Identity(4)
             mesh_node['transform'] = mathutils.Matrix.Identity(4)
         escn_file.add_node(mesh_node)
         escn_file.add_node(mesh_node)
 
 
+        # export shape key animation
+        if (export_settings['use_export_shape_key'] and
+                node.data.shape_keys is not None):
+            animation.export_animation_data(
+                escn_file, export_settings, mesh_node,
+                node.data.shape_keys, 'shapekey')
+
         return mesh_node
         return mesh_node
 
 
 
 

+ 87 - 19
io_scene_godot/converters/simple_nodes.py

@@ -7,6 +7,9 @@ import math
 import logging
 import logging
 import mathutils
 import mathutils
 from ..structures import NodeTemplate, fix_directional_transform
 from ..structures import NodeTemplate, fix_directional_transform
+from .animation import (export_animation_data, AttributeConvertInfo,
+                        CONVERT_AS_BOOL, CONVERT_AS_FLOAT,
+                        CONVERT_AS_MULTI_VALUE)
 
 
 
 
 def export_empty_node(escn_file, export_settings, node, parent_gd_node):
 def export_empty_node(escn_file, export_settings, node, parent_gd_node):
@@ -20,31 +23,99 @@ def export_empty_node(escn_file, export_settings, node, parent_gd_node):
     return empty_node
     return empty_node
 
 
 
 
+class CameraNode(NodeTemplate):
+    """Camera node in godot scene"""
+    _cam_attr_conv = [
+        # blender attr, godot attr, converter lambda, type
+        AttributeConvertInfo(
+            'clip_end', 'far', lambda x: x, CONVERT_AS_FLOAT),
+        AttributeConvertInfo(
+            'clip_start', 'near', lambda x: x, CONVERT_AS_FLOAT),
+        AttributeConvertInfo(
+            'ortho_scale', 'size', lambda x: x, CONVERT_AS_FLOAT),
+    ]
+
+    def __init__(self, name, parent):
+        super().__init__(name, "Camera", parent)
+
+    @property
+    def attribute_conversion(self):
+        """Get a list of quaternary tuple
+        (blender_attr, godot_attr, lambda converter, attr type)"""
+        return self._cam_attr_conv
+
+
 def export_camera_node(escn_file, export_settings, node, parent_gd_node):
 def export_camera_node(escn_file, export_settings, node, parent_gd_node):
     """Exports a camera"""
     """Exports a camera"""
     if (node.data is None or node.hide_render or
     if (node.data is None or node.hide_render or
             "CAMERA" not in export_settings['object_types']):
             "CAMERA" not in export_settings['object_types']):
         return parent_gd_node
         return parent_gd_node
 
 
-    cam_node = NodeTemplate(node.name, "Camera", parent_gd_node)
+    cam_node = CameraNode(node.name, parent_gd_node)
     camera = node.data
     camera = node.data
 
 
-    cam_node['far'] = camera.clip_end
-    cam_node['near'] = camera.clip_start
+    for item in cam_node.attribute_conversion:
+        blender_attr, gd_attr, converter, _ = item
+        cam_node[gd_attr] = converter(getattr(camera, blender_attr))
 
 
     if camera.type == "PERSP":
     if camera.type == "PERSP":
         cam_node['projection'] = 0
         cam_node['projection'] = 0
-        cam_node['fov'] = math.degrees(camera.angle)
     else:
     else:
         cam_node['projection'] = 1
         cam_node['projection'] = 1
-        cam_node['size'] = camera.ortho_scale
+
+    # `fov` does not go into `attribute_conversion`, because it can not
+    # be animated
+    cam_node['fov'] = math.degrees(camera.angle)
 
 
     cam_node['transform'] = fix_directional_transform(node.matrix_local)
     cam_node['transform'] = fix_directional_transform(node.matrix_local)
     escn_file.add_node(cam_node)
     escn_file.add_node(cam_node)
 
 
+    export_animation_data(escn_file, export_settings,
+                          cam_node, node.data, 'camera')
+
     return cam_node
     return cam_node
 
 
 
 
+class LightNode(NodeTemplate):
+    """Base class for godot light node"""
+    _light_attr_conv = [
+        AttributeConvertInfo(
+            'use_specular', 'light_specular',
+            lambda x: 1.0 if x else 0.0, CONVERT_AS_BOOL),
+        AttributeConvertInfo(
+            'energy', 'light_energy', lambda x: x, CONVERT_AS_FLOAT),
+        AttributeConvertInfo(
+            'color', 'light_color', mathutils.Color, CONVERT_AS_MULTI_VALUE),
+        AttributeConvertInfo(
+            'shadow_color', 'shadow_color',
+            mathutils.Color, CONVERT_AS_MULTI_VALUE),
+    ]
+    _omni_attr_conv = [
+        AttributeConvertInfo(
+            'distance', 'omni_range', lambda x: x, CONVERT_AS_FLOAT)
+    ]
+    _spot_attr_conv = [
+        AttributeConvertInfo(
+            'spot_size', 'spot_angle',
+            lambda x: math.degrees(x/2), CONVERT_AS_FLOAT),
+        AttributeConvertInfo(
+            'spot_blend', 'spot_angle_attenuation',
+            lambda x: 0.2/(x + 0.01), CONVERT_AS_FLOAT),
+        AttributeConvertInfo('distance', 'spot_range',
+                             lambda x: x, CONVERT_AS_FLOAT),
+    ]
+
+    @property
+    def attribute_conversion(self):
+        """Get a list of quaternary tuple
+        (blender_attr, godot_attr, lambda converter, attr type)"""
+        if self.get_type() == 'OmniLight':
+            return self._light_attr_conv + self._omni_attr_conv
+        if self.get_type() == 'SpotLight':
+            return self._light_attr_conv + self._spot_attr_conv
+        return self._light_attr_conv
+
+
 def export_lamp_node(escn_file, export_settings, node, parent_gd_node):
 def export_lamp_node(escn_file, export_settings, node, parent_gd_node):
     """Exports lights - well, the ones it knows about. Other light types
     """Exports lights - well, the ones it knows about. Other light types
     just throw a warning"""
     just throw a warning"""
@@ -55,9 +126,7 @@ def export_lamp_node(escn_file, export_settings, node, parent_gd_node):
     light = node.data
     light = node.data
 
 
     if light.type == "POINT":
     if light.type == "POINT":
-        light_node = NodeTemplate(node.name, "OmniLight", parent_gd_node)
-        light_node['omni_range'] = light.distance
-        light_node['shadow_enabled'] = light.shadow_method != "NOSHADOW"
+        light_node = LightNode(node.name, 'OmniLight', parent_gd_node)
 
 
         if not light.use_sphere:
         if not light.use_sphere:
             logging.warning(
             logging.warning(
@@ -65,11 +134,7 @@ def export_lamp_node(escn_file, export_settings, node, parent_gd_node):
             )
             )
 
 
     elif light.type == "SPOT":
     elif light.type == "SPOT":
-        light_node = NodeTemplate(node.name, "SpotLight", parent_gd_node)
-        light_node['spot_range'] = light.distance
-        light_node['spot_angle'] = math.degrees(light.spot_size/2)
-        light_node['spot_angle_attenuation'] = 0.2/(light.spot_blend + 0.01)
-        light_node['shadow_enabled'] = light.shadow_method != "NOSHADOW"
+        light_node = LightNode(node.name, 'SpotLight', parent_gd_node)
 
 
         if not light.use_sphere:
         if not light.use_sphere:
             logging.warning(
             logging.warning(
@@ -77,9 +142,7 @@ def export_lamp_node(escn_file, export_settings, node, parent_gd_node):
             )
             )
 
 
     elif light.type == "SUN":
     elif light.type == "SUN":
-        light_node = NodeTemplate(node.name, "DirectionalLight",
-                                  parent_gd_node)
-        light_node['shadow_enabled'] = light.shadow_method != "NOSHADOW"
+        light_node = LightNode(node.name, 'DirectionalLight', parent_gd_node)
     else:
     else:
         light_node = None
         light_node = None
         logging.warning(
         logging.warning(
@@ -87,13 +150,18 @@ def export_lamp_node(escn_file, export_settings, node, parent_gd_node):
         )
         )
 
 
     if light_node is not None:
     if light_node is not None:
+        for item in light_node.attribute_conversion:
+            bl_attr, gd_attr, converter, _ = item
+            light_node[gd_attr] = converter(getattr(light, bl_attr))
+
         # Properties common to all lights
         # Properties common to all lights
-        light_node['light_color'] = mathutils.Color(light.color)
         light_node['transform'] = fix_directional_transform(node.matrix_local)
         light_node['transform'] = fix_directional_transform(node.matrix_local)
+        light_node['shadow_enabled'] = light.shadow_method != "NOSHADOW"
         light_node['light_negative'] = light.use_negative
         light_node['light_negative'] = light.use_negative
-        light_node['light_specular'] = 1.0 if light.use_specular else 0.0
-        light_node['light_energy'] = light.energy
 
 
         escn_file.add_node(light_node)
         escn_file.add_node(light_node)
 
 
+    export_animation_data(escn_file, export_settings,
+                          light_node, node.data, 'light')
+
     return light_node
     return light_node

+ 1 - 3
io_scene_godot/export_godot.py

@@ -91,9 +91,7 @@ class GodotExporter:
                                  parent_gd_node)
                                  parent_gd_node)
 
 
         # if the blender node is exported and it has animation data
         # if the blender node is exported and it has animation data
-        if (self.config["use_export_animation"] and
-                exported_node != parent_gd_node and
-                node.animation_data is not None):
+        if exported_node != parent_gd_node:
             converters.ANIMATION_DATA_EXPORTER(
             converters.ANIMATION_DATA_EXPORTER(
                 self.escn_file,
                 self.escn_file,
                 self.config,
                 self.config,

+ 9 - 0
io_scene_godot/structures.py

@@ -4,6 +4,7 @@ This file contains classes to help dealing with the actual writing to the file
 """
 """
 import os
 import os
 import math
 import math
+import copy
 import collections
 import collections
 import mathutils
 import mathutils
 
 
@@ -297,6 +298,14 @@ class NodePath:
         self.relative_path = os.path.relpath(to_there, from_here)
         self.relative_path = os.path.relpath(to_there, from_here)
         self.attribute_name = attribute_pointed
         self.attribute_name = attribute_pointed
 
 
+    def new_copy(self, attribute=None):
+        """Return a new instance of the current NodePath and
+        able to change the attribute pointed"""
+        new_node_path = copy.deepcopy(self)
+        if attribute is not None:
+            new_node_path.attribute_name = attribute
+        return new_node_path
+
     def to_string(self):
     def to_string(self):
         """Serialize a node path"""
         """Serialize a node path"""
         return 'NodePath("{}:{}")'.format(
         return 'NodePath("{}:{}")'.format(

+ 1 - 1
tests/reference_exports/animation_bone_transform.escn

@@ -3,7 +3,7 @@
 [sub_resource id=1 type="Animation"]
 [sub_resource id=1 type="Animation"]
 
 
 step = 0.1
 step = 0.1
-length = 3.375
+length = 3.33333
 tracks/0/type = "transform"
 tracks/0/type = "transform"
 tracks/0/path = NodePath("Armature:Bone.002")
 tracks/0/path = NodePath("Armature:Bone.002")
 tracks/0/interp = 1
 tracks/0/interp = 1

+ 101 - 0
tests/reference_exports/animation_camera.escn

@@ -0,0 +1,101 @@
+[gd_scene load_steps=1 format=2]
+
+[sub_resource id=1 type="SpatialMaterial"]
+
+flags_unshaded = false
+flags_vertex_lighting = false
+flags_transparent = false
+vertex_color_use_as_albedo = false
+albedo_color = Color(0.8, 0.8, 0.8, 1.0)
+subsurf_scatter_enabled = false
+
+[sub_resource id=2 type="ArrayMesh"]
+
+surfaces/0 = {
+	"material":SubResource(1),
+	"primitive":4,
+	"arrays":[
+		Vector3Array(1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 0.999999, 1.0, 1.0, 1.0, 1.0, -0.999999, 1.0, 1.0, -0.999999, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 0.999999, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -0.999999, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 0.999999, 1.0, 1.0, 1.0, 1.0, -0.999999, 0.999999, 1.0, 1.0, 1.0, -1.0, 1.0, 0.999999, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0),
+		Vector3Array(2.98023e-08, -1.0, 0.0, 2.98023e-08, -1.0, 0.0, 2.98023e-08, -1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, -2.38419e-07, 0.0, 1.0, -2.38419e-07, 0.0, 1.0, -2.38419e-07, 0.0, -8.9407e-08, -4.76837e-07, 1.0, -8.9407e-08, -4.76837e-07, 1.0, -8.9407e-08, -4.76837e-07, 1.0, -1.0, -1.19209e-07, -2.38419e-07, -1.0, -1.19209e-07, -2.38419e-07, -1.0, -1.19209e-07, -2.38419e-07, 2.68221e-07, 2.38419e-07, -1.0, 2.68221e-07, 2.38419e-07, -1.0, 2.68221e-07, 2.38419e-07, -1.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 5.96047e-08, 1.0, 0.0, 5.96047e-08, 1.0, 0.0, 5.96047e-08, 1.0, 0.0, 1.0, 3.27825e-07, 5.96046e-07, 1.0, 3.27825e-07, 5.96046e-07, 1.0, 3.27825e-07, 5.96046e-07, -4.76837e-07, 1.19209e-07, 1.0, -4.76837e-07, 1.19209e-07, 1.0, -4.76837e-07, 1.19209e-07, 1.0, -1.0, -1.49012e-07, -2.38419e-07, -1.0, -1.49012e-07, -2.38419e-07, -1.0, -1.49012e-07, -2.38419e-07, 2.08616e-07, 8.9407e-08, -1.0, 2.08616e-07, 8.9407e-08, -1.0, 2.08616e-07, 8.9407e-08, -1.0),
+		null, ; No Tangents,
+		null, ; no Vertex Colors,
+		null, ; No UV1,
+		null, ; No UV2,
+		null, ; No Bones,
+		null, ; No Weights,
+		IntArray(0, 2, 1, 3, 5, 4, 6, 8, 7, 9, 11, 10, 12, 14, 13, 15, 17, 16, 18, 20, 19, 21, 23, 22, 24, 26, 25, 27, 29, 28, 30, 32, 31, 33, 35, 34)
+	],
+	"morph_arrays":[]
+}
+
+[sub_resource id=3 type="Animation"]
+
+step = 0.1
+length = 2.91667
+tracks/0/type = "value"
+tracks/0/path = NodePath("Camera:far")
+tracks/0/interp = 1
+tracks/0/keys = {
+	"times":PoolRealArray(0.541667, 0.583333, 0.625, 0.666667, 0.708333, 0.75, 0.791667, 0.833333),
+	"transitions":PoolRealArray(1, 1, 1, 1, 1, 1, 1, 1),
+	"update":0,
+	"values":[100.0, 95.9174, 83.8392, 65.4366, 44.5634, 26.1609, 14.0826, 10.0]
+}
+tracks/1/type = "value"
+tracks/1/path = NodePath("Camera:near")
+tracks/1/interp = 1
+tracks/1/keys = {
+	"times":PoolRealArray(0.0416667, 0.0833333, 0.125, 0.166667, 0.208333, 0.25, 0.291667, 0.333333, 0.375, 0.416667),
+	"transitions":PoolRealArray(1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
+	"update":0,
+	"values":[0.1, 1.46695, 5.56822, 12.1535, 20.5309, 29.5691, 37.9465, 44.5318, 48.633, 50.0]
+}
+tracks/2/type = "value"
+tracks/2/path = NodePath("Camera:size")
+tracks/2/interp = 1
+tracks/2/keys = {
+	"times":PoolRealArray(2.5, 2.54167, 2.58333, 2.625, 2.66667, 2.70833, 2.75, 2.79167, 2.83333, 2.875, 2.91667),
+	"transitions":PoolRealArray(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
+	"update":0,
+	"values":[7.31429, 7.1743, 6.75302, 6.06803, 5.17209, 4.15714, 3.14219, 2.24626, 1.56127, 1.13998, 1.0]
+}
+tracks/3/type = "value"
+tracks/3/path = NodePath("Camera:projection")
+tracks/3/interp = 0
+tracks/3/keys = {
+	"times":PoolRealArray(0.0416667, 2.5),
+	"transitions":PoolRealArray(1, 1),
+	"update":0,
+	"values":[0, 1]
+}
+tracks/4/type = "value"
+tracks/4/path = NodePath("Camera:fov")
+tracks/4/interp = 1
+tracks/4/keys = {
+	"times":PoolRealArray(0.0416667, 1.29167, 1.33333, 1.375, 1.41667, 1.45833, 1.5, 1.54167, 1.58333, 1.625, 1.66667, 1.70833, 1.75, 1.79167, 1.83333, 1.875, 1.91667, 1.95833, 2.0, 2.04167, 2.08333, 2.125, 2.16667, 2.20833, 2.25, 2.29167, 2.33333, 2.375, 2.41667, 2.45833, 2.5, 2.54167, 2.58333, 2.625, 2.66667, 2.70833, 2.75, 2.79167, 2.83333, 2.875, 2.91667),
+	"transitions":PoolRealArray(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
+	"update":0,
+	"values":[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 name="Cube" 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)
+
+[node name="Camera" type="Camera" parent="."]
+
+far = 100.0
+near = 0.1
+size = 7.31429
+projection = 0
+fov = 49.1343
+transform = Transform(0.685921, -0.324014, 0.651558, 0.0, 0.895396, 0.445271, -0.727676, -0.305421, 0.61417, 7.48113, 5.34367, 6.50764)
+
+[node name="CameraAnimation" type="AnimationPlayer" parent="."]
+
+root_node = NodePath("..:")
+anims/CameraAction = SubResource(3)

+ 66 - 0
tests/reference_exports/animation_light_type_change.escn

@@ -0,0 +1,66 @@
+[gd_scene load_steps=1 format=2]
+
+[sub_resource id=1 type="SpatialMaterial"]
+
+flags_unshaded = false
+flags_vertex_lighting = false
+flags_transparent = false
+vertex_color_use_as_albedo = false
+albedo_color = Color(0.8, 0.8, 0.8, 1.0)
+subsurf_scatter_enabled = false
+
+[sub_resource id=2 type="ArrayMesh"]
+
+surfaces/0 = {
+	"material":SubResource(1),
+	"primitive":4,
+	"arrays":[
+		Vector3Array(1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 0.999999, 1.0, 1.0, 1.0, 1.0, -0.999999, 1.0, 1.0, -0.999999, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 0.999999, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -0.999999, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 0.999999, 1.0, 1.0, 1.0, 1.0, -0.999999, 0.999999, 1.0, 1.0, 1.0, -1.0, 1.0, 0.999999, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0),
+		Vector3Array(2.98023e-08, -1.0, 0.0, 2.98023e-08, -1.0, 0.0, 2.98023e-08, -1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, -2.38419e-07, 0.0, 1.0, -2.38419e-07, 0.0, 1.0, -2.38419e-07, 0.0, -8.9407e-08, -4.76837e-07, 1.0, -8.9407e-08, -4.76837e-07, 1.0, -8.9407e-08, -4.76837e-07, 1.0, -1.0, -1.19209e-07, -2.38419e-07, -1.0, -1.19209e-07, -2.38419e-07, -1.0, -1.19209e-07, -2.38419e-07, 2.68221e-07, 2.38419e-07, -1.0, 2.68221e-07, 2.38419e-07, -1.0, 2.68221e-07, 2.38419e-07, -1.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 5.96047e-08, 1.0, 0.0, 5.96047e-08, 1.0, 0.0, 5.96047e-08, 1.0, 0.0, 1.0, 3.27825e-07, 5.96046e-07, 1.0, 3.27825e-07, 5.96046e-07, 1.0, 3.27825e-07, 5.96046e-07, -4.76837e-07, 1.19209e-07, 1.0, -4.76837e-07, 1.19209e-07, 1.0, -4.76837e-07, 1.19209e-07, 1.0, -1.0, -1.49012e-07, -2.38419e-07, -1.0, -1.49012e-07, -2.38419e-07, -1.0, -1.49012e-07, -2.38419e-07, 2.08616e-07, 8.9407e-08, -1.0, 2.08616e-07, 8.9407e-08, -1.0, 2.08616e-07, 8.9407e-08, -1.0),
+		null, ; No Tangents,
+		null, ; no Vertex Colors,
+		null, ; No UV1,
+		null, ; No UV2,
+		null, ; No Bones,
+		null, ; No Weights,
+		IntArray(0, 2, 1, 3, 5, 4, 6, 8, 7, 9, 11, 10, 12, 14, 13, 15, 17, 16, 18, 20, 19, 21, 23, 22, 24, 26, 25, 27, 29, 28, 30, 32, 31, 33, 35, 34)
+	],
+	"morph_arrays":[]
+}
+
+[sub_resource id=3 type="Animation"]
+
+step = 0.1
+length = 2.91667
+tracks/0/type = "value"
+tracks/0/path = NodePath("Lamp:light_color")
+tracks/0/interp = 1
+tracks/0/keys = {
+	"times":PoolRealArray(0.0833333, 0.458333, 0.5, 0.541667, 0.583333, 0.625, 0.666667, 0.708333, 0.75, 0.791667, 0.833333),
+	"transitions":PoolRealArray(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
+	"update":0,
+	"values":[Color(1.0, 1.0, 1.0, 1.0), Color(0.978978, 1.0, 0.978395, 1.0), Color(0.91571, 1.0, 0.913372, 1.0), Color(0.81284, 1.0, 0.807649, 1.0), Color(0.67829, 1.0, 0.669369, 1.0), Color(0.525868, 1.0, 0.512719, 1.0), Color(0.373445, 1.0, 0.35607, 1.0), Color(0.238896, 1.0, 0.217789, 1.0), Color(0.136026, 1.0, 0.112066, 1.0), Color(0.072758, 1.0, 0.0470439, 1.0), Color(0.0517359, 1.0, 0.0254388, 1.0)]
+}
+[node type="Spatial" name="Scene"]
+
+
+[node name="Cube" 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)
+
+[node name="Lamp" type="DirectionalLight" parent="."]
+
+light_specular = 1.0
+light_energy = 1.0
+light_color = Color(1.0, 1.0, 1.0, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
+transform = Transform(-0.290865, -0.771101, 0.566393, -0.0551891, 0.604525, 0.794672, -0.955171, 0.199883, -0.218391, 4.07625, 5.90386, -1.00545)
+shadow_enabled = true
+light_negative = false
+
+[node name="LampAnimation" type="AnimationPlayer" parent="."]
+
+root_node = NodePath("..:")
+anims/LampAction = SubResource(3)

+ 1 - 1
tests/reference_exports/animation_object_transform.escn

@@ -31,7 +31,7 @@ surfaces/0 = {
 [sub_resource id=3 type="Animation"]
 [sub_resource id=3 type="Animation"]
 
 
 step = 0.1
 step = 0.1
-length = 6.04167
+length = 6.0
 tracks/0/type = "transform"
 tracks/0/type = "transform"
 tracks/0/path = NodePath("Cube:")
 tracks/0/path = NodePath("Cube:")
 tracks/0/interp = 1
 tracks/0/interp = 1

+ 1 - 1
tests/reference_exports/animation_parented_objects.escn

@@ -31,7 +31,7 @@ surfaces/0 = {
 [sub_resource id=3 type="Animation"]
 [sub_resource id=3 type="Animation"]
 
 
 step = 0.1
 step = 0.1
-length = 3.375
+length = 3.33333
 tracks/0/type = "transform"
 tracks/0/type = "transform"
 tracks/0/path = NodePath("Cube:")
 tracks/0/path = NodePath("Cube:")
 tracks/0/interp = 1
 tracks/0/interp = 1

+ 91 - 0
tests/reference_exports/animation_point_light_shadow.escn

@@ -0,0 +1,91 @@
+[gd_scene load_steps=1 format=2]
+
+[sub_resource id=1 type="ArrayMesh"]
+
+surfaces/0 = {
+	"primitive":4,
+	"arrays":[
+		Vector3Array(-1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0),
+		Vector3Array(-1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 1.0, 0.0),
+		null, ; No Tangents,
+		null, ; no Vertex Colors,
+		null, ; No UV1,
+		null, ; No UV2,
+		null, ; No Bones,
+		null, ; No Weights,
+		IntArray(0, 2, 1, 3, 5, 4, 6, 8, 7, 9, 11, 10, 12, 14, 13, 15, 17, 16, 0, 1, 18, 3, 4, 19, 6, 7, 20, 9, 10, 21, 12, 13, 22, 15, 16, 23)
+	],
+	"morph_arrays":[]
+}
+
+[sub_resource id=2 type="SpatialMaterial"]
+
+flags_unshaded = false
+flags_vertex_lighting = false
+flags_transparent = false
+vertex_color_use_as_albedo = false
+albedo_color = Color(0.8, 0.8, 0.8, 1.0)
+subsurf_scatter_enabled = false
+
+[sub_resource id=3 type="ArrayMesh"]
+
+surfaces/0 = {
+	"material":SubResource(2),
+	"primitive":4,
+	"arrays":[
+		Vector3Array(1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 0.999999, 1.0, 1.0, 1.0, 1.0, -0.999999, 1.0, 1.0, -0.999999, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 0.999999, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -0.999999, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 0.999999, 1.0, 1.0, 1.0, 1.0, -0.999999, 0.999999, 1.0, 1.0, 1.0, -1.0, 1.0, 0.999999, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0),
+		Vector3Array(2.98023e-08, -1.0, 0.0, 2.98023e-08, -1.0, 0.0, 2.98023e-08, -1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, -2.38419e-07, 0.0, 1.0, -2.38419e-07, 0.0, 1.0, -2.38419e-07, 0.0, -8.9407e-08, -4.76837e-07, 1.0, -8.9407e-08, -4.76837e-07, 1.0, -8.9407e-08, -4.76837e-07, 1.0, -1.0, -1.19209e-07, -2.38419e-07, -1.0, -1.19209e-07, -2.38419e-07, -1.0, -1.19209e-07, -2.38419e-07, 2.68221e-07, 2.38419e-07, -1.0, 2.68221e-07, 2.38419e-07, -1.0, 2.68221e-07, 2.38419e-07, -1.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 5.96047e-08, 1.0, 0.0, 5.96047e-08, 1.0, 0.0, 5.96047e-08, 1.0, 0.0, 1.0, 3.27825e-07, 5.96046e-07, 1.0, 3.27825e-07, 5.96046e-07, 1.0, 3.27825e-07, 5.96046e-07, -4.76837e-07, 1.19209e-07, 1.0, -4.76837e-07, 1.19209e-07, 1.0, -4.76837e-07, 1.19209e-07, 1.0, -1.0, -1.49012e-07, -2.38419e-07, -1.0, -1.49012e-07, -2.38419e-07, -1.0, -1.49012e-07, -2.38419e-07, 2.08616e-07, 8.9407e-08, -1.0, 2.08616e-07, 8.9407e-08, -1.0, 2.08616e-07, 8.9407e-08, -1.0),
+		null, ; No Tangents,
+		null, ; no Vertex Colors,
+		null, ; No UV1,
+		null, ; No UV2,
+		null, ; No Bones,
+		null, ; No Weights,
+		IntArray(0, 2, 1, 3, 5, 4, 6, 8, 7, 9, 11, 10, 12, 14, 13, 15, 17, 16, 18, 20, 19, 21, 23, 22, 24, 26, 25, 27, 29, 28, 30, 32, 31, 33, 35, 34)
+	],
+	"morph_arrays":[]
+}
+
+[sub_resource id=4 type="Animation"]
+
+step = 0.1
+length = 1.25
+tracks/0/type = "value"
+tracks/0/path = NodePath("Lamp:shadow_color")
+tracks/0/interp = 1
+tracks/0/keys = {
+	"times":PoolRealArray(0.0416667, 0.0833333, 0.125, 0.166667, 0.208333, 0.25, 0.291667, 0.333333, 0.375, 0.416667),
+	"transitions":PoolRealArray(1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
+	"update":0,
+	"values":[Color(0.0, 0.0, 0.0, 1.0), Color(0.00076126, 0.00840977, 0.000572418, 1.0), Color(0.00304528, 0.0336417, 0.00228985, 1.0), Color(0.00671266, 0.0741558, 0.00504748, 1.0), Color(0.0113781, 0.125695, 0.00855556, 1.0), Color(0.0164115, 0.181301, 0.0123404, 1.0), Color(0.0210769, 0.23284, 0.0158485, 1.0), Color(0.0247443, 0.273354, 0.0186061, 1.0), Color(0.0270283, 0.298586, 0.0203235, 1.0), Color(0.0277896, 0.306996, 0.020896, 1.0)]
+}
+[node type="Spatial" name="Scene"]
+
+
+[node name="Cube001" 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.16055, -1.22778, 0.861728)
+
+[node name="Cube" type="MeshInstance" parent="."]
+
+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.0, 0.0, 0.0)
+
+[node name="Lamp" type="OmniLight" parent="."]
+
+light_specular = 1.0
+light_energy = 1.0
+light_color = Color(1.0, 1.0, 1.0, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
+omni_range = 30.0
+transform = Transform(-0.290865, -0.771101, 0.566393, -0.0551891, 0.604525, 0.794672, -0.955171, 0.199883, -0.218391, 2.86902, 6.70935, -2.25395)
+shadow_enabled = true
+light_negative = false
+
+[node name="LampAnimation" type="AnimationPlayer" parent="."]
+
+root_node = NodePath("..:")
+anims/LampAction = SubResource(4)

+ 1 - 1
tests/reference_exports/animation_rotation_euler.escn

@@ -31,7 +31,7 @@ surfaces/0 = {
 [sub_resource id=3 type="Animation"]
 [sub_resource id=3 type="Animation"]
 
 
 step = 0.1
 step = 0.1
-length = 7.125
+length = 7.08333
 tracks/0/type = "transform"
 tracks/0/type = "transform"
 tracks/0/path = NodePath("Cube:")
 tracks/0/path = NodePath("Cube:")
 tracks/0/interp = 1
 tracks/0/interp = 1

文件差異過大導致無法顯示
+ 9 - 0
tests/reference_exports/animation_shapekey.escn


+ 87 - 0
tests/reference_exports/animation_spot_light.escn

@@ -0,0 +1,87 @@
+[gd_scene load_steps=1 format=2]
+
+[sub_resource id=1 type="SpatialMaterial"]
+
+flags_unshaded = false
+flags_vertex_lighting = false
+flags_transparent = false
+vertex_color_use_as_albedo = false
+albedo_color = Color(0.8, 0.8, 0.8, 1.0)
+subsurf_scatter_enabled = false
+
+[sub_resource id=2 type="ArrayMesh"]
+
+surfaces/0 = {
+	"material":SubResource(1),
+	"primitive":4,
+	"arrays":[
+		Vector3Array(1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 0.999999, 1.0, 1.0, 1.0, 1.0, -0.999999, 1.0, 1.0, -0.999999, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 0.999999, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -0.999999, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 0.999999, 1.0, 1.0, 1.0, 1.0, -0.999999, 0.999999, 1.0, 1.0, 1.0, -1.0, 1.0, 0.999999, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0),
+		Vector3Array(2.98023e-08, -1.0, 0.0, 2.98023e-08, -1.0, 0.0, 2.98023e-08, -1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, -2.38419e-07, 0.0, 1.0, -2.38419e-07, 0.0, 1.0, -2.38419e-07, 0.0, -8.9407e-08, -4.76837e-07, 1.0, -8.9407e-08, -4.76837e-07, 1.0, -8.9407e-08, -4.76837e-07, 1.0, -1.0, -1.19209e-07, -2.38419e-07, -1.0, -1.19209e-07, -2.38419e-07, -1.0, -1.19209e-07, -2.38419e-07, 2.68221e-07, 2.38419e-07, -1.0, 2.68221e-07, 2.38419e-07, -1.0, 2.68221e-07, 2.38419e-07, -1.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 5.96047e-08, 1.0, 0.0, 5.96047e-08, 1.0, 0.0, 5.96047e-08, 1.0, 0.0, 1.0, 3.27825e-07, 5.96046e-07, 1.0, 3.27825e-07, 5.96046e-07, 1.0, 3.27825e-07, 5.96046e-07, -4.76837e-07, 1.19209e-07, 1.0, -4.76837e-07, 1.19209e-07, 1.0, -4.76837e-07, 1.19209e-07, 1.0, -1.0, -1.49012e-07, -2.38419e-07, -1.0, -1.49012e-07, -2.38419e-07, -1.0, -1.49012e-07, -2.38419e-07, 2.08616e-07, 8.9407e-08, -1.0, 2.08616e-07, 8.9407e-08, -1.0, 2.08616e-07, 8.9407e-08, -1.0),
+		null, ; No Tangents,
+		null, ; no Vertex Colors,
+		null, ; No UV1,
+		null, ; No UV2,
+		null, ; No Bones,
+		null, ; No Weights,
+		IntArray(0, 2, 1, 3, 5, 4, 6, 8, 7, 9, 11, 10, 12, 14, 13, 15, 17, 16, 18, 20, 19, 21, 23, 22, 24, 26, 25, 27, 29, 28, 30, 32, 31, 33, 35, 34)
+	],
+	"morph_arrays":[]
+}
+
+[sub_resource id=3 type="Animation"]
+
+step = 0.1
+length = 2.5
+tracks/0/type = "value"
+tracks/0/path = NodePath("Lamp:spot_angle")
+tracks/0/interp = 1
+tracks/0/keys = {
+	"times":PoolRealArray(1.25, 1.29167, 1.33333, 1.375, 1.41667, 1.45833, 1.5, 1.54167, 1.58333, 1.625, 1.66667),
+	"transitions":PoolRealArray(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
+	"update":0,
+	"values":[7.5, 7.66627, 8.16667, 8.98029, 10.0445, 11.25, 12.4555, 13.5197, 14.3333, 14.8337, 15.0]
+}
+tracks/1/type = "value"
+tracks/1/path = NodePath("Lamp:spot_angle_attenuation")
+tracks/1/interp = 1
+tracks/1/keys = {
+	"times":PoolRealArray(2.08333, 2.125, 2.16667, 2.20833, 2.25, 2.29167, 2.33333, 2.375, 2.41667, 2.45833, 2.5),
+	"transitions":PoolRealArray(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
+	"update":0,
+	"values":[1.25, 1.22455, 1.15385, 1.05482, 0.948365, 0.851064, 0.771871, 0.713281, 0.674157, 0.652157, 0.645161]
+}
+tracks/2/type = "value"
+tracks/2/path = NodePath("Lamp:spot_range")
+tracks/2/interp = 1
+tracks/2/keys = {
+	"times":PoolRealArray(0.0416667, 0.0833333, 0.125, 0.166667, 0.208333, 0.25, 0.291667, 0.333333, 0.375, 0.416667, 0.458333, 0.5, 0.541667, 0.583333, 0.625, 0.666667, 0.708333, 0.75, 0.791667, 0.833333),
+	"transitions":PoolRealArray(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
+	"update":0,
+	"values":[30.0, 29.9084, 29.6314, 29.1684, 28.5235, 27.7059, 26.7321, 25.6258, 24.4181, 23.1466, 21.8534, 20.5819, 19.3742, 18.2679, 17.2941, 16.4765, 15.8315, 15.3686, 15.0916, 15.0]
+}
+[node type="Spatial" name="Scene"]
+
+
+[node name="Cube" 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)
+
+[node name="Lamp" type="SpotLight" parent="."]
+
+light_specular = 1.0
+light_energy = 1.0
+light_color = Color(1.0, 1.0, 1.0, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
+spot_angle = 7.5
+spot_angle_attenuation = 1.25
+spot_range = 30.0
+transform = Transform(-0.290865, -0.771101, 0.566393, -0.0551891, 0.604525, 0.794672, -0.955171, 0.199883, -0.218391, 3.97706, 6.35177, -1.33297)
+shadow_enabled = true
+light_negative = false
+
+[node name="LampAnimation" type="AnimationPlayer" parent="."]
+
+root_node = NodePath("..:")
+anims/LampAction = SubResource(3)

+ 7 - 6
tests/reference_exports/animation_spot_light_transform.escn

@@ -3,7 +3,7 @@
 [sub_resource id=1 type="Animation"]
 [sub_resource id=1 type="Animation"]
 
 
 step = 0.1
 step = 0.1
-length = 1.70833
+length = 1.66667
 tracks/0/type = "transform"
 tracks/0/type = "transform"
 tracks/0/path = NodePath("Spot:")
 tracks/0/path = NodePath("Spot:")
 tracks/0/interp = 1
 tracks/0/interp = 1
@@ -41,15 +41,16 @@ surfaces/0 = {
 
 
 [node name="Spot" type="SpotLight" parent="."]
 [node name="Spot" type="SpotLight" parent="."]
 
 
-spot_range = 10.0
+light_specular = 1.0
+light_energy = 1.0
+light_color = Color(1.0, 1.0, 1.0, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
 spot_angle = 22.5
 spot_angle = 22.5
 spot_angle_attenuation = 1.25
 spot_angle_attenuation = 1.25
-shadow_enabled = true
-light_color = Color(1.0, 1.0, 1.0, 1.0)
+spot_range = 10.0
 transform = Transform(0.664154, 0.183225, -0.724795, 0.688378, -0.528045, 0.497297, -0.291607, -0.829215, -0.476831, -8.94658, 5.71699, -3.33328)
 transform = Transform(0.664154, 0.183225, -0.724795, 0.688378, -0.528045, 0.497297, -0.291607, -0.829215, -0.476831, -8.94658, 5.71699, -3.33328)
+shadow_enabled = true
 light_negative = false
 light_negative = false
-light_specular = 1.0
-light_energy = 1.0
 
 
 [node name="SpotAnimation" type="AnimationPlayer" parent="."]
 [node name="SpotAnimation" type="AnimationPlayer" parent="."]
 
 

+ 83 - 0
tests/reference_exports/animation_sun.escn

@@ -0,0 +1,83 @@
+[gd_scene load_steps=1 format=2]
+
+[sub_resource id=1 type="ArrayMesh"]
+
+surfaces/0 = {
+	"primitive":4,
+	"arrays":[
+		Vector3Array(-1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0),
+		Vector3Array(-1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 1.0, 0.0),
+		null, ; No Tangents,
+		null, ; no Vertex Colors,
+		null, ; No UV1,
+		null, ; No UV2,
+		null, ; No Bones,
+		null, ; No Weights,
+		IntArray(0, 2, 1, 3, 5, 4, 6, 8, 7, 9, 11, 10, 12, 14, 13, 15, 17, 16, 0, 1, 18, 3, 4, 19, 6, 7, 20, 9, 10, 21, 12, 13, 22, 15, 16, 23)
+	],
+	"morph_arrays":[]
+}
+
+[sub_resource id=2 type="Animation"]
+
+step = 0.1
+length = 3.33333
+tracks/0/type = "value"
+tracks/0/path = NodePath("Sun:light_negative")
+tracks/0/interp = 0
+tracks/0/keys = {
+	"times":PoolRealArray(2.5, 2.91667, 3.33333),
+	"transitions":PoolRealArray(1, 1, 1),
+	"update":0,
+	"values":[false, true, true]
+}
+tracks/1/type = "value"
+tracks/1/path = NodePath("Sun:light_specular")
+tracks/1/interp = 0
+tracks/1/keys = {
+	"times":PoolRealArray(1.66667, 2.08333),
+	"transitions":PoolRealArray(1, 1),
+	"update":0,
+	"values":[1.0, 0.0]
+}
+tracks/2/type = "value"
+tracks/2/path = NodePath("Sun:light_energy")
+tracks/2/interp = 1
+tracks/2/keys = {
+	"times":PoolRealArray(0.833333, 0.875, 0.916667, 0.958333, 1.0, 1.04167, 1.08333, 1.125, 1.16667, 1.20833, 1.25),
+	"transitions":PoolRealArray(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
+	"update":0,
+	"values":[1.0, 1.08868, 1.35556, 1.78949, 2.35705, 3.0, 3.64295, 4.21051, 4.64444, 4.91132, 5.0]
+}
+tracks/3/type = "value"
+tracks/3/path = NodePath("Sun:light_color")
+tracks/3/interp = 1
+tracks/3/keys = {
+	"times":PoolRealArray(0.0416667, 0.0833333, 0.125, 0.166667, 0.208333, 0.25, 0.291667, 0.333333, 0.375, 0.416667),
+	"transitions":PoolRealArray(1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
+	"update":0,
+	"values":[Color(1.0, 1.0, 1.0, 1.0), Color(0.972876, 1.0, 0.972839, 1.0), Color(0.891497, 1.0, 0.891346, 1.0), Color(0.760829, 1.0, 0.760495, 1.0), Color(0.594602, 1.0, 0.594036, 1.0), Color(0.415261, 1.0, 0.414444, 1.0), Color(0.249034, 1.0, 0.247984, 1.0), Color(0.118366, 1.0, 0.117134, 1.0), Color(0.0369866, 1.0, 0.0356407, 1.0), Color(0.00986297, 1.0, 0.00847925, 1.0)]
+}
+[node type="Spatial" name="Scene"]
+
+
+[node name="Cube" 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, 0.0, 0.0, 0.0)
+
+[node name="Sun" type="DirectionalLight" parent="."]
+
+light_specular = 0.0
+light_energy = 5.0
+light_color = Color(0.00986297, 1.0, 0.00847925, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.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, 11.4136, 0.0)
+shadow_enabled = false
+light_negative = true
+
+[node name="SunAnimation" type="AnimationPlayer" parent="."]
+
+root_node = NodePath("..:")
+anims/SunAction = SubResource(2)

+ 1 - 1
tests/reference_exports/armature_with_non_deform_bone.escn

@@ -3,7 +3,7 @@
 [sub_resource id=1 type="Animation"]
 [sub_resource id=1 type="Animation"]
 
 
 step = 0.1
 step = 0.1
-length = 4.25
+length = 4.20833
 tracks/0/type = "transform"
 tracks/0/type = "transform"
 tracks/0/path = NodePath("Armature001:Bone.003")
 tracks/0/path = NodePath("Armature001:Bone.003")
 tracks/0/interp = 1
 tracks/0/interp = 1

+ 6 - 4
tests/reference_exports/just_armature.escn

@@ -34,18 +34,20 @@ bones/3/bound_children = []
 
 
 [node name="Lamp" type="OmniLight" parent="."]
 [node name="Lamp" type="OmniLight" parent="."]
 
 
-omni_range = 30.0
-shadow_enabled = true
+light_specular = 1.0
+light_energy = 1.0
 light_color = Color(1.0, 1.0, 1.0, 1.0)
 light_color = Color(1.0, 1.0, 1.0, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
+omni_range = 30.0
 transform = Transform(-0.290865, -0.771101, 0.566393, -0.0551891, 0.604525, 0.794672, -0.955171, 0.199883, -0.218391, 4.07625, 5.90386, -1.00545)
 transform = Transform(-0.290865, -0.771101, 0.566393, -0.0551891, 0.604525, 0.794672, -0.955171, 0.199883, -0.218391, 4.07625, 5.90386, -1.00545)
+shadow_enabled = true
 light_negative = false
 light_negative = false
-light_specular = 1.0
-light_energy = 1.0
 
 
 [node name="Camera" type="Camera" parent="."]
 [node name="Camera" type="Camera" parent="."]
 
 
 far = 100.0
 far = 100.0
 near = 0.1
 near = 0.1
+size = 7.31429
 projection = 0
 projection = 0
 fov = 49.1343
 fov = 49.1343
 transform = Transform(0.685921, -0.324014, 0.651558, 0.0, 0.895396, 0.445271, -0.727676, -0.305421, 0.61417, 7.48113, 5.34367, 6.50764)
 transform = Transform(0.685921, -0.324014, 0.651558, 0.0, 0.895396, 0.445271, -0.727676, -0.305421, 0.61417, 7.48113, 5.34367, 6.50764)

+ 7 - 2
tests/reference_exports/just_cameras.escn

@@ -8,6 +8,7 @@
 
 
 far = 10.0
 far = 10.0
 near = 5.0
 near = 5.0
+size = 6.0
 projection = 0
 projection = 0
 fov = 90.0
 fov = 90.0
 transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 5.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, 5.0, 0.0, 0.0)
@@ -16,22 +17,25 @@ transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 5.0, 0.0, 0.0
 
 
 far = 100.0
 far = 100.0
 near = 0.1
 near = 0.1
-projection = 1
 size = 6.0
 size = 6.0
+projection = 1
+fov = 90.0
 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)
 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="Camera002" type="Camera" parent="."]
 [node name="Camera002" type="Camera" parent="."]
 
 
 far = 100.0
 far = 100.0
 near = 0.1
 near = 0.1
-projection = 1
 size = 2.0
 size = 2.0
+projection = 1
+fov = 90.0
 transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, -2.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, -2.0, 0.0, 0.0)
 
 
 [node name="Camera001" type="Camera" parent="."]
 [node name="Camera001" type="Camera" parent="."]
 
 
 far = 100.0
 far = 100.0
 near = 0.1
 near = 0.1
+size = 6.0
 projection = 0
 projection = 0
 fov = 90.0
 fov = 90.0
 transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, -5.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, -5.0, 0.0, 0.0)
@@ -40,6 +44,7 @@ transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, -5.0, 0.0, 0.
 
 
 far = 100.0
 far = 100.0
 near = 0.1
 near = 0.1
+size = 6.0
 projection = 0
 projection = 0
 fov = 20.0
 fov = 20.0
 transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, -7.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, -7.0, 0.0, 0.0)

+ 21 - 16
tests/reference_exports/just_point_lights.escn

@@ -58,13 +58,14 @@ surfaces/0 = {
 
 
 [node name="Lamp003" type="OmniLight" parent="."]
 [node name="Lamp003" type="OmniLight" parent="."]
 
 
-omni_range = 5.0
-shadow_enabled = true
+light_specular = 1.0
+light_energy = 1.0
 light_color = Color(0.674415, 0.00033786, 1.0, 1.0)
 light_color = Color(0.674415, 0.00033786, 1.0, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
+omni_range = 5.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.4, 1.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.4, 1.0)
+shadow_enabled = true
 light_negative = false
 light_negative = false
-light_specular = 1.0
-light_energy = 1.0
 
 
 [node name="Cube001" type="MeshInstance" parent="."]
 [node name="Cube001" type="MeshInstance" parent="."]
 
 
@@ -80,23 +81,25 @@ transform = Transform(0.3, 0.0, 0.0, 0.0, 0.6, 0.0, 0.0, 0.0, 0.3, -2.0, 0.0, 0.
 
 
 [node name="Lamp002" type="OmniLight" parent="."]
 [node name="Lamp002" type="OmniLight" parent="."]
 
 
-omni_range = 5.0
-shadow_enabled = false
+light_specular = 1.0
+light_energy = 1.0
 light_color = Color(0.0, 1.0, 0.00242467, 1.0)
 light_color = Color(0.0, 1.0, 0.00242467, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
+omni_range = 5.0
 transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, 1.0, 0.0, -1.0, -4.37114e-08, -2.0, 0.4, 1.0)
 transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, 1.0, 0.0, -1.0, -4.37114e-08, -2.0, 0.4, 1.0)
+shadow_enabled = false
 light_negative = false
 light_negative = false
-light_specular = 1.0
-light_energy = 1.0
 
 
 [node name="Lamp001" type="OmniLight" parent="."]
 [node name="Lamp001" type="OmniLight" parent="."]
 
 
-omni_range = 2.0
-shadow_enabled = false
+light_specular = 1.0
+light_energy = 1.0
 light_color = Color(1.0, 0.0, 0.0, 1.0)
 light_color = Color(1.0, 0.0, 0.0, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
+omni_range = 2.0
 transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, 1.0, 0.0, -1.0, -4.37114e-08, -6.0, 1.0, 3.0)
 transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, 1.0, 0.0, -1.0, -4.37114e-08, -6.0, 1.0, 3.0)
+shadow_enabled = false
 light_negative = false
 light_negative = false
-light_specular = 1.0
-light_energy = 1.0
 
 
 [node name="Plane" type="MeshInstance" parent="."]
 [node name="Plane" type="MeshInstance" parent="."]
 
 
@@ -106,18 +109,20 @@ transform = Transform(10.0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0,
 
 
 [node name="Lamp" type="OmniLight" parent="."]
 [node name="Lamp" type="OmniLight" parent="."]
 
 
-omni_range = 2.0
-shadow_enabled = false
+light_specular = 1.0
+light_energy = 1.0
 light_color = Color(1.0, 1.0, 1.0, 1.0)
 light_color = Color(1.0, 1.0, 1.0, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
+omni_range = 2.0
 transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, 1.0, 0.0, -1.0, -4.37114e-08, -6.0, 1.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, -6.0, 1.0, 0.0)
+shadow_enabled = false
 light_negative = false
 light_negative = false
-light_specular = 1.0
-light_energy = 1.0
 
 
 [node name="Camera" type="Camera" parent="."]
 [node name="Camera" type="Camera" parent="."]
 
 
 far = 100.0
 far = 100.0
 near = 0.1
 near = 0.1
+size = 7.31429
 projection = 0
 projection = 0
 fov = 49.1343
 fov = 49.1343
 transform = Transform(0.685921, -0.324014, 0.651558, 0.0, 0.895396, 0.445271, -0.727676, -0.305421, 0.61417, 7.48113, 5.34367, 6.50764)
 transform = Transform(0.685921, -0.324014, 0.651558, 0.0, 0.895396, 0.445271, -0.727676, -0.305421, 0.61417, 7.48113, 5.34367, 6.50764)

+ 49 - 40
tests/reference_exports/just_spot_lights.escn

@@ -58,75 +58,81 @@ surfaces/0 = {
 
 
 [node name="Lamp007" type="SpotLight" parent="."]
 [node name="Lamp007" type="SpotLight" parent="."]
 
 
-spot_range = 3.0
+light_specular = 1.0
+light_energy = 5.0
+light_color = Color(0.0, 1.0, 0.796991, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
 spot_angle = 10.0
 spot_angle = 10.0
 spot_angle_attenuation = 20.0
 spot_angle_attenuation = 20.0
-shadow_enabled = false
-light_color = Color(0.0, 1.0, 0.796991, 1.0)
+spot_range = 3.0
 transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, 1.0, 0.0, -1.0, -4.37114e-08, 1.0, 2.0, -3.0)
 transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, 1.0, 0.0, -1.0, -4.37114e-08, 1.0, 2.0, -3.0)
+shadow_enabled = false
 light_negative = false
 light_negative = false
-light_specular = 1.0
-light_energy = 5.0
 
 
 [node name="Lamp006" type="SpotLight" parent="."]
 [node name="Lamp006" type="SpotLight" parent="."]
 
 
-spot_range = 3.0
+light_specular = 1.0
+light_energy = 5.0
+light_color = Color(0.0, 1.0, 0.796991, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
 spot_angle = 10.0
 spot_angle = 10.0
 spot_angle_attenuation = 0.19802
 spot_angle_attenuation = 0.19802
-shadow_enabled = false
-light_color = Color(0.0, 1.0, 0.796991, 1.0)
+spot_range = 3.0
 transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, 1.0, 0.0, -1.0, -4.37114e-08, 2.0, 2.0, -3.0)
 transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, 1.0, 0.0, -1.0, -4.37114e-08, 2.0, 2.0, -3.0)
+shadow_enabled = false
 light_negative = false
 light_negative = false
-light_specular = 1.0
-light_energy = 5.0
 
 
 [node name="Lamp005" type="SpotLight" parent="."]
 [node name="Lamp005" type="SpotLight" parent="."]
 
 
-spot_range = 3.0
+light_specular = 1.0
+light_energy = 1.0
+light_color = Color(0.0, 1.0, 0.796991, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
 spot_angle = 10.0
 spot_angle = 10.0
 spot_angle_attenuation = 0.19802
 spot_angle_attenuation = 0.19802
-shadow_enabled = false
-light_color = Color(0.0, 1.0, 0.796991, 1.0)
+spot_range = 3.0
 transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, 1.0, 0.0, -1.0, -4.37114e-08, 2.0, 2.0, -2.0)
 transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, 1.0, 0.0, -1.0, -4.37114e-08, 2.0, 2.0, -2.0)
+shadow_enabled = false
 light_negative = false
 light_negative = false
-light_specular = 1.0
-light_energy = 1.0
 
 
 [node name="Lamp002" type="SpotLight" parent="."]
 [node name="Lamp002" type="SpotLight" parent="."]
 
 
-spot_range = 3.0
+light_specular = 1.0
+light_energy = 1.0
+light_color = Color(0.0, 1.0, 0.796991, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
 spot_angle = 10.0
 spot_angle = 10.0
 spot_angle_attenuation = 20.0
 spot_angle_attenuation = 20.0
-shadow_enabled = false
-light_color = Color(0.0, 1.0, 0.796991, 1.0)
+spot_range = 3.0
 transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, 1.0, 0.0, -1.0, -4.37114e-08, 1.0, 2.0, -2.0)
 transform = Transform(1.0, 0.0, 0.0, 0.0, -4.37114e-08, 1.0, 0.0, -1.0, -4.37114e-08, 1.0, 2.0, -2.0)
+shadow_enabled = false
 light_negative = false
 light_negative = false
-light_specular = 1.0
-light_energy = 1.0
 
 
 [node name="Lamp001" type="SpotLight" parent="."]
 [node name="Lamp001" type="SpotLight" parent="."]
 
 
-spot_range = 3.0
+light_specular = 1.0
+light_energy = 1.0
+light_color = Color(0.0, 1.0, 0.796991, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
 spot_angle = 60.0
 spot_angle = 60.0
 spot_angle_attenuation = 1.25
 spot_angle_attenuation = 1.25
-shadow_enabled = false
-light_color = Color(0.0, 1.0, 0.796991, 1.0)
+spot_range = 3.0
 transform = Transform(1.0, 0.0, 0.0, 0.0, 0.906308, 0.422618, 0.0, -0.422618, 0.906308, 1.0, 1.0, 2.0)
 transform = Transform(1.0, 0.0, 0.0, 0.0, 0.906308, 0.422618, 0.0, -0.422618, 0.906308, 1.0, 1.0, 2.0)
+shadow_enabled = false
 light_negative = false
 light_negative = false
-light_specular = 1.0
-light_energy = 1.0
 
 
 [node name="Lamp000" type="SpotLight" parent="."]
 [node name="Lamp000" type="SpotLight" parent="."]
 
 
-spot_range = 5.0
+light_specular = 1.0
+light_energy = 1.0
+light_color = Color(0.00993437, 1.0, 0.0, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
 spot_angle = 10.0
 spot_angle = 10.0
 spot_angle_attenuation = 1.25
 spot_angle_attenuation = 1.25
-shadow_enabled = false
-light_color = Color(0.00993437, 1.0, 0.0, 1.0)
+spot_range = 5.0
 transform = Transform(1.0, 0.0, 0.0, 0.0, 0.906308, 0.422618, 0.0, -0.422618, 0.906308, -1.0, 1.0, 2.0)
 transform = Transform(1.0, 0.0, 0.0, 0.0, 0.906308, 0.422618, 0.0, -0.422618, 0.906308, -1.0, 1.0, 2.0)
+shadow_enabled = false
 light_negative = false
 light_negative = false
-light_specular = 1.0
-light_energy = 1.0
 
 
 [node name="Cube001" type="MeshInstance" parent="."]
 [node name="Cube001" type="MeshInstance" parent="."]
 
 
@@ -142,27 +148,29 @@ transform = Transform(0.3, 0.0, 0.0, 0.0, 0.6, 0.0, 0.0, 0.0, 0.3, -4.0, 0.0, 0.
 
 
 [node name="Lamp004" type="SpotLight" parent="."]
 [node name="Lamp004" type="SpotLight" parent="."]
 
 
-spot_range = 5.0
+light_specular = 1.0
+light_energy = 1.0
+light_color = Color(0.990355, 0.0, 1.0, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
 spot_angle = 37.5
 spot_angle = 37.5
 spot_angle_attenuation = 1.25
 spot_angle_attenuation = 1.25
-shadow_enabled = true
-light_color = Color(0.990355, 0.0, 1.0, 1.0)
+spot_range = 5.0
 transform = Transform(1.0, 0.0, 0.0, 0.0, 0.906308, 0.422618, 0.0, -0.422618, 0.906308, -4.0, 1.0, 2.0)
 transform = Transform(1.0, 0.0, 0.0, 0.0, 0.906308, 0.422618, 0.0, -0.422618, 0.906308, -4.0, 1.0, 2.0)
+shadow_enabled = true
 light_negative = false
 light_negative = false
-light_specular = 1.0
-light_energy = 1.0
 
 
 [node name="Lamp003" type="SpotLight" parent="."]
 [node name="Lamp003" type="SpotLight" parent="."]
 
 
-spot_range = 5.0
+light_specular = 1.0
+light_energy = 1.0
+light_color = Color(0.0, 0.340107, 1.0, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
 spot_angle = 37.5
 spot_angle = 37.5
 spot_angle_attenuation = 1.25
 spot_angle_attenuation = 1.25
-shadow_enabled = false
-light_color = Color(0.0, 0.340107, 1.0, 1.0)
+spot_range = 5.0
 transform = Transform(1.0, 0.0, 0.0, 0.0, 0.906308, 0.422618, 0.0, -0.422618, 0.906308, -6.0, 1.0, 2.0)
 transform = Transform(1.0, 0.0, 0.0, 0.0, 0.906308, 0.422618, 0.0, -0.422618, 0.906308, -6.0, 1.0, 2.0)
+shadow_enabled = false
 light_negative = false
 light_negative = false
-light_specular = 1.0
-light_energy = 1.0
 
 
 [node name="Plane" type="MeshInstance" parent="."]
 [node name="Plane" type="MeshInstance" parent="."]
 
 
@@ -174,6 +182,7 @@ transform = Transform(10.0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0,
 
 
 far = 100.0
 far = 100.0
 near = 0.1
 near = 0.1
+size = 7.31429
 projection = 0
 projection = 0
 fov = 49.1343
 fov = 49.1343
 transform = Transform(0.685921, -0.324014, 0.651558, 0.0, 0.895396, 0.445271, -0.727676, -0.305421, 0.61417, 7.48113, 5.34367, 6.50764)
 transform = Transform(0.685921, -0.324014, 0.651558, 0.0, 0.895396, 0.445271, -0.727676, -0.305421, 0.61417, 7.48113, 5.34367, 6.50764)

+ 6 - 4
tests/reference_exports/material_search.escn

@@ -30,18 +30,20 @@ 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
 
 
 [node name="Lamp" type="OmniLight" parent="."]
 [node name="Lamp" type="OmniLight" parent="."]
 
 
-omni_range = 30.0
-shadow_enabled = true
+light_specular = 1.0
+light_energy = 1.0
 light_color = Color(1.0, 1.0, 1.0, 1.0)
 light_color = Color(1.0, 1.0, 1.0, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
+omni_range = 30.0
 transform = Transform(-0.290865, -0.771101, 0.566393, -0.0551891, 0.604525, 0.794672, -0.955171, 0.199883, -0.218391, 4.07625, 5.90386, -1.00545)
 transform = Transform(-0.290865, -0.771101, 0.566393, -0.0551891, 0.604525, 0.794672, -0.955171, 0.199883, -0.218391, 4.07625, 5.90386, -1.00545)
+shadow_enabled = true
 light_negative = false
 light_negative = false
-light_specular = 1.0
-light_energy = 1.0
 
 
 [node name="Camera" type="Camera" parent="."]
 [node name="Camera" type="Camera" parent="."]
 
 
 far = 100.0
 far = 100.0
 near = 0.1
 near = 0.1
+size = 7.31429
 projection = 0
 projection = 0
 fov = 49.1343
 fov = 49.1343
 transform = Transform(0.685921, -0.324014, 0.651558, 0.0, 0.895396, 0.445271, -0.727676, -0.305421, 0.61417, 7.48113, 5.34367, 6.50764)
 transform = Transform(0.685921, -0.324014, 0.651558, 0.0, 0.895396, 0.445271, -0.727676, -0.305421, 0.61417, 7.48113, 5.34367, 6.50764)

+ 6 - 4
tests/reference_exports/physics.escn

@@ -299,18 +299,20 @@ 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
 
 
 [node name="Lamp" type="OmniLight" parent="."]
 [node name="Lamp" type="OmniLight" parent="."]
 
 
-omni_range = 30.0
-shadow_enabled = true
+light_specular = 1.0
+light_energy = 1.0
 light_color = Color(1.0, 1.0, 1.0, 1.0)
 light_color = Color(1.0, 1.0, 1.0, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
+omni_range = 30.0
 transform = Transform(-0.290865, -0.771101, 0.566393, -0.0551891, 0.604525, 0.794672, -0.955171, 0.199883, -0.218391, 4.07625, 7.32356, -1.00545)
 transform = Transform(-0.290865, -0.771101, 0.566393, -0.0551891, 0.604525, 0.794672, -0.955171, 0.199883, -0.218391, 4.07625, 7.32356, -1.00545)
+shadow_enabled = true
 light_negative = false
 light_negative = false
-light_specular = 1.0
-light_energy = 1.0
 
 
 [node name="Camera" type="Camera" parent="."]
 [node name="Camera" type="Camera" parent="."]
 
 
 far = 100.0
 far = 100.0
 near = 0.1
 near = 0.1
+size = 7.31429
 projection = 0
 projection = 0
 fov = 49.1343
 fov = 49.1343
 transform = Transform(0.685921, -0.324014, 0.651558, 0.0, 0.895396, 0.445271, -0.727676, -0.305421, 0.61417, 14.4308, 10.093, 13.0585)
 transform = Transform(0.685921, -0.324014, 0.651558, 0.0, 0.895396, 0.445271, -0.727676, -0.305421, 0.61417, 14.4308, 10.093, 13.0585)

+ 6 - 4
tests/reference_exports/simple_materials.escn

@@ -63,18 +63,20 @@ 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
 
 
 [node name="Lamp" type="OmniLight" parent="."]
 [node name="Lamp" type="OmniLight" parent="."]
 
 
-omni_range = 30.0
-shadow_enabled = true
+light_specular = 1.0
+light_energy = 1.0
 light_color = Color(1.0, 1.0, 1.0, 1.0)
 light_color = Color(1.0, 1.0, 1.0, 1.0)
+shadow_color = Color(0.0, 0.0, 0.0, 1.0)
+omni_range = 30.0
 transform = Transform(-0.290865, -0.771101, 0.566393, -0.0551891, 0.604525, 0.794672, -0.955171, 0.199883, -0.218391, 4.07625, 5.90386, -1.00545)
 transform = Transform(-0.290865, -0.771101, 0.566393, -0.0551891, 0.604525, 0.794672, -0.955171, 0.199883, -0.218391, 4.07625, 5.90386, -1.00545)
+shadow_enabled = true
 light_negative = false
 light_negative = false
-light_specular = 1.0
-light_energy = 1.0
 
 
 [node name="Camera" type="Camera" parent="."]
 [node name="Camera" type="Camera" parent="."]
 
 
 far = 100.0
 far = 100.0
 near = 0.1
 near = 0.1
+size = 7.31429
 projection = 0
 projection = 0
 fov = 49.1343
 fov = 49.1343
 transform = Transform(0.685921, -0.324014, 0.651558, 0.0, 0.895396, 0.445271, -0.727676, -0.305421, 0.61417, 7.48113, 5.34367, 6.50764)
 transform = Transform(0.685921, -0.324014, 0.651558, 0.0, 0.895396, 0.445271, -0.727676, -0.305421, 0.61417, 7.48113, 5.34367, 6.50764)

二進制
tests/test_scenes/animation_camera.blend


二進制
tests/test_scenes/animation_light_type_change.blend


二進制
tests/test_scenes/animation_point_light_shadow.blend


二進制
tests/test_scenes/animation_shapekey.blend


二進制
tests/test_scenes/animation_spot_light.blend


二進制
tests/test_scenes/animation_sun.blend


部分文件因文件數量過多而無法顯示