Browse Source

support bezier track as beta feature

Jason0214 6 years ago
parent
commit
ce76bb279e

+ 101 - 82
io_scene_godot/converters/animation/action.py

@@ -36,6 +36,7 @@ def get_strip_frame_range(strip):
 class ActionStrip:
 class ActionStrip:
     """Abstract of blender action strip, it may override attributes
     """Abstract of blender action strip, it may override attributes
     of an action object"""
     of an action object"""
+
     def __init__(self, action_or_strip):
     def __init__(self, action_or_strip):
         self.action = None
         self.action = None
         self.frame_range = (0, 0)
         self.frame_range = (0, 0)
@@ -103,9 +104,9 @@ def has_obj_fcurves(action_strip):
 
 
 
 
 # pylint: disable-msg=too-many-locals
 # pylint: disable-msg=too-many-locals
-def export_constrained_xform_action(godot_node, animation_player,
+def export_constrained_xform_action(godot_node, export_settings,
                                     blender_object, action_strip,
                                     blender_object, action_strip,
-                                    animation_resource):
+                                    anim_rsc):
     """Export transform animation of any object has constraints,
     """Export transform animation of any object has constraints,
     it use frame_set to traversal each frame, so it's costly"""
     it use frame_set to traversal each frame, so it's costly"""
     def build_pbone_parent_map(godot_node, blender_object):
     def build_pbone_parent_map(godot_node, blender_object):
@@ -160,7 +161,7 @@ def export_constrained_xform_action(godot_node, animation_player,
         ]
         ]
 
 
         track_path = NodePath(
         track_path = NodePath(
-            animation_player.parent.get_path(),
+            anim_rsc.anim_player.parent.get_path(),
             godot_node.get_path()
             godot_node.get_path()
         )
         )
 
 
@@ -170,7 +171,7 @@ def export_constrained_xform_action(godot_node, animation_player,
                 for x in xform_frames_list
                 for x in xform_frames_list
             ]
             ]
 
 
-        animation_resource.add_obj_xform_track(
+        anim_rsc.add_obj_xform_track(
             godot_node.get_type(), track_path,
             godot_node.get_type(), track_path,
             xform_frames_list, action_strip.frame_range,
             xform_frames_list, action_strip.frame_range,
             # no need for parent_inverse, as it is directly access matrix_local
             # no need for parent_inverse, as it is directly access matrix_local
@@ -185,12 +186,12 @@ def export_constrained_xform_action(godot_node, animation_player,
                 ]
                 ]
 
 
                 track_path = NodePath(
                 track_path = NodePath(
-                    animation_player.parent.get_path(),
+                    anim_rsc.anim_player.parent.get_path(),
                     godot_node.get_path(),
                     godot_node.get_path(),
                     godot_node.find_bone_name(pbone_name),
                     godot_node.find_bone_name(pbone_name),
                 )
                 )
 
 
-                animation_resource.add_track(
+                anim_rsc.add_track(
                     TransformTrack(
                     TransformTrack(
                         track_path,
                         track_path,
                         frames_iter=range(first_frame, last_frame),
                         frames_iter=range(first_frame, last_frame),
@@ -199,8 +200,8 @@ def export_constrained_xform_action(godot_node, animation_player,
                 )
                 )
 
 
 
 
-def export_transform_action(godot_node, animation_player, blender_object,
-                            action_strip, animation_resource):
+def export_transform_action(godot_node, export_settings, blender_object,
+                            action_strip, anim_rsc):
     """Export a action with bone and object transform"""
     """Export a action with bone and object transform"""
     def init_transform_frames_list(object_path, blender_object, godot_node,
     def init_transform_frames_list(object_path, blender_object, godot_node,
                                    first_frame, last_frame):
                                    first_frame, last_frame):
@@ -275,7 +276,7 @@ def export_transform_action(godot_node, animation_player, blender_object,
         if object_path == '':
         if object_path == '':
             # empty object_path represents transform of object itself
             # empty object_path represents transform of object itself
             track_path = NodePath(
             track_path = NodePath(
-                animation_player.parent.get_path(),
+                anim_rsc.anim_player.parent.get_path(),
                 godot_node.get_path()
                 godot_node.get_path()
             )
             )
 
 
@@ -285,7 +286,7 @@ def export_transform_action(godot_node, animation_player, blender_object,
                     for x in frame_value_list
                     for x in frame_value_list
                 ]
                 ]
 
 
-            animation_resource.add_obj_xform_track(
+            anim_rsc.add_obj_xform_track(
                 godot_node.get_type(), track_path,
                 godot_node.get_type(), track_path,
                 frame_value_list, action_strip.frame_range,
                 frame_value_list, action_strip.frame_range,
                 blender_object.matrix_parent_inverse
                 blender_object.matrix_parent_inverse
@@ -293,13 +294,13 @@ def export_transform_action(godot_node, animation_player, blender_object,
 
 
         elif object_path.startswith('pose'):
         elif object_path.startswith('pose'):
             track_path = NodePath(
             track_path = NodePath(
-                animation_player.parent.get_path(),
+                anim_rsc.anim_player.parent.get_path(),
                 godot_node.get_path(),
                 godot_node.get_path(),
                 godot_node.find_bone_name(
                 godot_node.find_bone_name(
                     blender_path_to_bone_name(object_path)
                     blender_path_to_bone_name(object_path)
                 ),
                 ),
             )
             )
-            animation_resource.add_track(
+            anim_rsc.add_track(
                 TransformTrack(
                 TransformTrack(
                     track_path,
                     track_path,
                     frames_iter=range(first_frame, last_frame),
                     frames_iter=range(first_frame, last_frame),
@@ -308,124 +309,142 @@ def export_transform_action(godot_node, animation_player, blender_object,
             )
             )
 
 
 
 
-def export_shapekey_action(godot_node, animation_player, blender_object,
-                           action_strip, animation_resource):
+def export_shapekey_action(godot_node, export_settings, blender_object,
+                           action_strip, anim_rsc):
     """Export shapekey value action"""
     """Export shapekey value action"""
-    first_frame, last_frame = action_strip.frame_range
+    base_node_path = NodePath(
+        anim_rsc.anim_player.parent.get_path(), godot_node.get_path()
+    )
     for fcurve in action_strip.action.fcurves:
     for fcurve in action_strip.action.fcurves:
-
         object_path, attribute = split_fcurve_data_path(fcurve.data_path)
         object_path, attribute = split_fcurve_data_path(fcurve.data_path)
 
 
         if attribute == 'value':
         if attribute == 'value':
             shapekey_name = re.search(r'key_blocks\["([^"]+)"\]',
             shapekey_name = re.search(r'key_blocks\["([^"]+)"\]',
                                       object_path).group(1)
                                       object_path).group(1)
 
 
-            track_path = NodePath(
-                animation_player.parent.get_path(),
-                godot_node.get_path(),
-                "blend_shapes/{}".format(shapekey_name)
+            anim_rsc.add_attribute_track(
+                action_strip,
+                fcurve,
+                lambda x: x,
+                base_node_path.new_copy(
+                    "blend_shapes/{}".format(shapekey_name)),
+                use_bezier=export_settings['feature_bezier_track']
             )
             )
 
 
-            track = FloatTrack(track_path)
 
 
-            for frame in range(first_frame, last_frame):
-                track.add_frame_data(
-                    frame,
-                    action_strip.evaluate_fcurve(fcurve, frame)
-                )
-
-            animation_resource.add_track(track)
-
-
-def export_light_action(light_node, animation_player, blender_lamp,
-                        action_strip, animation_resource):
+def export_light_action(light_node, export_settings, blender_lamp,
+                        action_strip, anim_rsc):
     """Export light(lamp in Blender) action"""
     """Export light(lamp in Blender) action"""
-    # pylint: disable-msg=R0914
+    # pylint: disable-msg=too-many-branches
     base_node_path = NodePath(
     base_node_path = NodePath(
-        animation_player.parent.get_path(), light_node.get_path()
+        anim_rsc.anim_player.parent.get_path(), light_node.get_path()
     )
     )
 
 
     fcurves = action_strip.action.fcurves
     fcurves = action_strip.action.fcurves
-
-    animation_resource.add_attribute_track(
+    # CONSTANT tracks
+    anim_rsc.add_attribute_track(
         action_strip,
         action_strip,
         fcurves.find('shadow_method'),
         fcurves.find('shadow_method'),
         lambda x: x > 0.0,
         lambda x: x > 0.0,
         base_node_path.new_copy('shadow_enabled'),
         base_node_path.new_copy('shadow_enabled'),
     )
     )
 
 
+    color_attr_conversion = list()
+    # simple value tracks
     for item in light_node.attribute_conversion:
     for item in light_node.attribute_conversion:
         bl_attr, gd_attr, converter = item
         bl_attr, gd_attr, converter = item
-        if bl_attr not in ('color', 'shadow_color'):
-            animation_resource.add_attribute_track(
+        if bl_attr in ('color', 'shadow_color'):
+            color_attr_conversion.append(item)
+        else:
+            anim_rsc.add_attribute_track(
                 action_strip,
                 action_strip,
                 fcurves.find(bl_attr),
                 fcurves.find(bl_attr),
                 converter,
                 converter,
-                base_node_path.new_copy(gd_attr)
+                base_node_path.new_copy(gd_attr),
+                use_bezier=export_settings["feature_bezier_track"]
             )
             )
 
 
-    # color tracks is not one-one mapping to fcurve, they
-    # need to be treated like transform track
-    color_frame_values_map = collections.OrderedDict()
+    if export_settings["feature_bezier_track"]:
+        rgba_names = ('r', 'g', 'b', 'a')
+        for fcurve in fcurves:
+            if fcurve is None:
+                continue
+            _, attribute = split_fcurve_data_path(fcurve.data_path)
+
+            for bl_attr, gd_attr, converter in color_attr_conversion:
+                if bl_attr == attribute:
+                    track_path = base_node_path.new_copy(
+                        gd_attr + ':' + rgba_names[fcurve.array_index]
+                    )
+                    anim_rsc.add_attribute_track(
+                        action_strip,
+                        fcurve,
+                        converter,
+                        track_path,
+                        use_bezier=True)
+    else:
+        # color tracks is not one-one mapping to fcurve, they
+        # need to be treated like transform track
+        color_frame_values_map = collections.OrderedDict()
+
+        first_frame, last_frame = action_strip.frame_range
+        for fcurve in 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] = (
+                        action_strip.evaluate_fcurve(fcurve, frame)
+                    )
 
 
-    first_frame, last_frame = action_strip.frame_range
-    for fcurve in 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)
+        for bl_attr, _, converter in light_node.attribute_conversion:
+            if (bl_attr in ('color', 'shadow_color') and
+                    bl_attr in color_frame_values_map):
+                color_frame_values_map[bl_attr] = [
+                    converter(x) for x in color_frame_values_map[bl_attr]
                 ]
                 ]
-            color_list = color_frame_values_map[attribute]
-            for frame in range(first_frame, last_frame):
-                color_list[frame - first_frame][fcurve.array_index] = (
-                    action_strip.evaluate_fcurve(fcurve, frame)
-                )
 
 
-    for bl_attr, _, converter in light_node.attribute_conversion:
-        if (bl_attr in ('color', 'shadow_color') and
-                bl_attr in color_frame_values_map):
-            color_frame_values_map[bl_attr] = [
-                converter(x) for x in color_frame_values_map[bl_attr]
-            ]
+        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')
 
 
-    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(
-            ColorTrack(
-                track_path,
-                frames_iter=range(first_frame, last_frame),
-                values_iter=frame_value_list
+            anim_rsc.add_track(
+                ColorTrack(
+                    track_path,
+                    frames_iter=range(first_frame, last_frame),
+                    values_iter=frame_value_list
+                )
             )
             )
-        )
 
 
 
 
-def export_camera_action(camera_node, animation_player, blender_cam,
-                         action_strip, animation_resource):
+def export_camera_action(camera_node, export_settings, blender_cam,
+                         action_strip, anim_rsc):
     """Export camera action"""
     """Export camera action"""
-    # pylint: disable-msg=R0914
     first_frame, last_frame = action_strip.frame_range
     first_frame, last_frame = action_strip.frame_range
     base_node_path = NodePath(
     base_node_path = NodePath(
-        animation_player.parent.get_path(), camera_node.get_path()
+        anim_rsc.anim_player.parent.get_path(), camera_node.get_path()
     )
     )
 
 
     fcurves = action_strip.action.fcurves
     fcurves = action_strip.action.fcurves
     for item in camera_node.attribute_conversion:
     for item in camera_node.attribute_conversion:
         bl_attr, gd_attr, converter = item
         bl_attr, gd_attr, converter = item
-        animation_resource.add_attribute_track(
+        anim_rsc.add_attribute_track(
             action_strip,
             action_strip,
             fcurves.find(bl_attr),
             fcurves.find(bl_attr),
             converter,
             converter,
-            base_node_path.new_copy(gd_attr)
+            base_node_path.new_copy(gd_attr),
+            use_bezier=export_settings['feature_bezier_track'],
         )
         )
 
 
-    animation_resource.add_attribute_track(
+    anim_rsc.add_attribute_track(
         action_strip,
         action_strip,
         fcurves.find('type'),
         fcurves.find('type'),
         lambda x: 0 if x == 0.0 else 1,
         lambda x: 0 if x == 0.0 else 1,
@@ -470,7 +489,7 @@ def export_camera_action(camera_node, animation_player, blender_cam,
                 )
                 )
             ))
             ))
 
 
-        animation_resource.add_track(
+        anim_rsc.add_track(
             FloatTrack(
             FloatTrack(
                 base_node_path.new_copy('fov'),
                 base_node_path.new_copy('fov'),
                 frames_iter=range(first_frame, last_frame),
                 frames_iter=range(first_frame, last_frame),

+ 15 - 11
io_scene_godot/converters/animation/animation_data.py

@@ -28,6 +28,7 @@ ACTION_EXPORTER_MAP = {
 class ObjectAnimationExporter:
 class ObjectAnimationExporter:
     """A helper class holding states while exporting
     """A helper class holding states while exporting
     animation data from a blender object"""
     animation data from a blender object"""
+
     def __init__(self, godot_node, blender_object, action_type):
     def __init__(self, godot_node, blender_object, action_type):
         self.godot_node = godot_node
         self.godot_node = godot_node
         self.blender_object = blender_object
         self.blender_object = blender_object
@@ -69,7 +70,7 @@ class ObjectAnimationExporter:
                 else:
                 else:
                     self.mute_nla_tracks.append(nla_track)
                     self.mute_nla_tracks.append(nla_track)
 
 
-    def export_active_action(self, escn_file, active_action):
+    def export_active_action(self, escn_file, export_settings, active_action):
         """Export the active action, if needed, would call bake.
         """Export the active action, if needed, would call bake.
         Note that active_action maybe None, which would happen when object has
         Note that active_action maybe None, which would happen when object has
         some constraint (so even no action it is still animated)"""
         some constraint (so even no action it is still animated)"""
@@ -87,7 +88,7 @@ class ObjectAnimationExporter:
 
 
         self.action_exporter_func(
         self.action_exporter_func(
             self.godot_node,
             self.godot_node,
-            self.animation_player,
+            export_settings,
             self.blender_object,
             self.blender_object,
             ActionStrip(active_action),
             ActionStrip(active_action),
             self.animation_player.active_animation
             self.animation_player.active_animation
@@ -102,13 +103,13 @@ class ObjectAnimationExporter:
                     if strip.action:
                     if strip.action:
                         self.action_exporter_func(
                         self.action_exporter_func(
                             self.godot_node,
                             self.godot_node,
-                            self.animation_player,
+                            export_settings,
                             self.blender_object,
                             self.blender_object,
                             ActionStrip(strip),
                             ActionStrip(strip),
                             self.animation_player.active_animation
                             self.animation_player.active_animation
                         )
                         )
 
 
-    def export_active_action_from_nla(self, escn_file):
+    def export_active_action_from_nla(self, escn_file, export_settings):
         """Export all unmute nla_tracks into an active action.
         """Export all unmute nla_tracks into an active action.
         Note that it would not do baking for constraint"""
         Note that it would not do baking for constraint"""
         if self.animation_player.active_animation is None:
         if self.animation_player.active_animation is None:
@@ -121,13 +122,13 @@ class ObjectAnimationExporter:
                 if strip.action:
                 if strip.action:
                     self.action_exporter_func(
                     self.action_exporter_func(
                         self.godot_node,
                         self.godot_node,
-                        self.animation_player,
+                        export_settings,
                         self.blender_object,
                         self.blender_object,
                         ActionStrip(strip),
                         ActionStrip(strip),
                         self.animation_player.active_animation
                         self.animation_player.active_animation
                     )
                     )
 
 
-    def export_stashed_track(self, escn_file, stashed_track):
+    def export_stashed_track(self, escn_file, export_settings, stashed_track):
         """Export a muted nla_track, track with all its contained action
         """Export a muted nla_track, track with all its contained action
         is exported to a single animation_resource.
         is exported to a single animation_resource.
         It works as an action lib"""
         It works as an action lib"""
@@ -152,7 +153,7 @@ class ObjectAnimationExporter:
             if strip.action:
             if strip.action:
                 self.action_exporter_func(
                 self.action_exporter_func(
                     self.godot_node,
                     self.godot_node,
-                    self.animation_player,
+                    export_settings,
                     self.blender_object,
                     self.blender_object,
                     ActionStrip(strip),
                     ActionStrip(strip),
                     anim_resource
                     anim_resource
@@ -167,7 +168,7 @@ class ObjectAnimationExporter:
                     if strip.action:
                     if strip.action:
                         self.action_exporter_func(
                         self.action_exporter_func(
                             self.godot_node,
                             self.godot_node,
-                            self.animation_player,
+                            export_settings,
                             self.blender_object,
                             self.blender_object,
                             ActionStrip(strip),
                             ActionStrip(strip),
                             anim_resource
                             anim_resource
@@ -201,10 +202,12 @@ def export_animation_data(escn_file, export_settings, godot_node,
         active_action = None
         active_action = None
 
 
     if (active_action is not None or anim_exporter.need_baking):
     if (active_action is not None or anim_exporter.need_baking):
-        anim_exporter.export_active_action(escn_file, active_action)
+        anim_exporter.export_active_action(
+            escn_file, export_settings, active_action)
     elif anim_exporter.unmute_nla_tracks:
     elif anim_exporter.unmute_nla_tracks:
         # if has effective nla_tracks but no active action, fake one
         # if has effective nla_tracks but no active action, fake one
-        anim_exporter.export_active_action_from_nla(escn_file)
+        anim_exporter.export_active_action_from_nla(
+            escn_file, export_settings)
 
 
     # export actions in nla_tracks, each exported to seperate
     # export actions in nla_tracks, each exported to seperate
     # animation resources
     # animation resources
@@ -215,7 +218,8 @@ def export_animation_data(escn_file, export_settings, godot_node,
             obj_use_nla_backup = blender_object.animation_data.use_nla
             obj_use_nla_backup = blender_object.animation_data.use_nla
             blender_object.animation_data.use_nla = True
             blender_object.animation_data.use_nla = True
         for stashed_track in anim_exporter.mute_nla_tracks:
         for stashed_track in anim_exporter.mute_nla_tracks:
-            anim_exporter.export_stashed_track(escn_file, stashed_track)
+            anim_exporter.export_stashed_track(
+                escn_file, export_settings, stashed_track)
         if blender_object.animation_data:
         if blender_object.animation_data:
             blender_object.animation_data.use_nla = obj_use_nla_backup
             blender_object.animation_data.use_nla = obj_use_nla_backup
 
 

+ 69 - 7
io_scene_godot/converters/animation/serializer.py

@@ -45,6 +45,14 @@ def strip_adjacent_dup_keyframes(frames, values):
     return stripped_frames, stripped_values
     return stripped_frames, stripped_values
 
 
 
 
+class BezierFrame:
+    """A keyframe point in a bezier fcurve"""
+    def __init__(self, value, left_handle, right_handle):
+        self.value = value
+        self.left_handle = left_handle
+        self.right_handle = right_handle
+
+
 class TransformFrame:
 class TransformFrame:
     """A data structure hold transform values of an animation key,
     """A data structure hold transform values of an animation key,
     it is used as an intermedia data structure, being updated during
     it is used as an intermedia data structure, being updated during
@@ -386,9 +394,33 @@ class ColorTrack(ValueTrack):
         )
         )
 
 
 
 
-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
+class BezierTrack(Track):
+    """Track using bezier interpolcation"""
+    def __init__(self, track_path, frames_iter=(), values_iter=()):
+        super().__init__("bezier", track_path, frames_iter, values_iter)
+
+    def blend_frames(self, frame_val1, frame_val2):
+        # xxx: default use REPLACE
+        return max(frame_val1, frame_val2)
+
+    def convert_to_keys_object(self):
+        """Convert a list of bezier point to a pool real array"""
+        time_array = Array(prefix='PoolRealArray(', suffix=')')
+        points_array = Array(prefix='PoolRealArray(', suffix=')')
+        fps = bpy.context.scene.render.fps
+        scene_frame_start = bpy.context.scene.frame_start
+        for frame, frame_val in zip(self.frames, self.values):
+            time = (frame - scene_frame_start) / fps
+            time_array.append(time)
+            points_array.append(frame_val.value)
+            points_array.append((frame_val.left_handle[0] - frame) / fps)
+            points_array.append(frame_val.left_handle[1])
+            points_array.append((frame_val.right_handle[0] - frame) / fps)
+            points_array.append(frame_val.right_handle[1])
+        keys_map = Map()
+        keys_map["points"] = points_array
+        keys_map["times"] = time_array
+        return keys_map
 
 
 
 
 def build_const_interp_value_track(track_path, action_strip, converter,
 def build_const_interp_value_track(track_path, action_strip, converter,
@@ -414,7 +446,7 @@ def build_linear_interp_value_track(track_path, action_strip, converter,
     """Build a godot value track by evaluate every frame of Blender fcurve"""
     """Build a godot value track by evaluate every frame of Blender fcurve"""
     track = FloatTrack(track_path)
     track = FloatTrack(track_path)
 
 
-    frame_range = get_fcurve_frame_range(fcurve)
+    frame_range = action_strip.frame_range
     if converter is None:
     if converter is None:
         for frame in range(frame_range[0], frame_range[1]):
         for frame in range(frame_range[0], frame_range[1]):
             track.add_frame_data(
             track.add_frame_data(
@@ -429,13 +461,38 @@ def build_linear_interp_value_track(track_path, action_strip, converter,
     return track
     return track
 
 
 
 
+def build_beizer_interp_value_track(track_path, action_strip, converter,
+                                    fcurve):
+    """Build a godot bezier track"""
+    track = BezierTrack(track_path)
+
+    for keyframe in fcurve.keyframe_points:
+        point_value = converter(keyframe.co[1])
+        # bezier curve handle use margin
+        track.add_frame_data(
+            int(keyframe.co[0]),
+            BezierFrame(
+                point_value,
+                (keyframe.handle_left.x,
+                 converter(keyframe.handle_left.y) - point_value),
+                (keyframe.handle_right.x,
+                 converter(keyframe.handle_right.y) - point_value),
+            )
+        )
+
+    return track
+
+
 class AnimationResource(InternalResource):
 class AnimationResource(InternalResource):
     """Internal resource with type Animation"""
     """Internal resource with type Animation"""
-    def __init__(self, name):
+    def __init__(self, name, owner_anim_player):
         super().__init__('Animation', name)
         super().__init__('Animation', name)
         self['step'] = 0.1
         self['step'] = 0.1
         self['length'] = 0
         self['length'] = 0
+
+        # helper attributes, not exported to ESCN
         self.tracks = collections.OrderedDict()
         self.tracks = collections.OrderedDict()
+        self.anim_player = owner_anim_player
 
 
     def add_track(self, track):
     def add_track(self, track):
         """add a track to animation resource"""
         """add a track to animation resource"""
@@ -475,8 +532,9 @@ class AnimationResource(InternalResource):
 
 
         self.add_track(track)
         self.add_track(track)
 
 
+    # pylint: disable-msg=too-many-arguments
     def add_attribute_track(self, action_strip, fcurve,
     def add_attribute_track(self, action_strip, fcurve,
-                            converter, node_path):
+                            converter, node_path, use_bezier=False):
         """Add a track into AnimationResource, the track is a
         """Add a track into AnimationResource, the track is a
         one-one mapping to one fcurve."""
         one-one mapping to one fcurve."""
         if fcurve is not None and fcurve.keyframe_points:
         if fcurve is not None and fcurve.keyframe_points:
@@ -485,6 +543,10 @@ class AnimationResource(InternalResource):
                 new_track = build_const_interp_value_track(
                 new_track = build_const_interp_value_track(
                     node_path, action_strip, converter, fcurve
                     node_path, action_strip, converter, fcurve
                 )
                 )
+            elif use_bezier and interpolation == 'BEZIER':
+                new_track = build_beizer_interp_value_track(
+                    node_path, action_strip, converter, fcurve
+                )
             else:
             else:
                 new_track = build_linear_interp_value_track(
                 new_track = build_linear_interp_value_track(
                     node_path, action_strip, converter, fcurve
                     node_path, action_strip, converter, fcurve
@@ -513,7 +575,7 @@ class AnimationPlayer(NodeTemplate):
         """Create a new animation resource and add it into escn file"""
         """Create a new animation resource and add it into escn file"""
         resource_name_filtered = re.sub(r'[\[\]\{\}]+', '', resource_name)
         resource_name_filtered = re.sub(r'[\[\]\{\}]+', '', resource_name)
 
 
-        new_anim_resource = AnimationResource(resource_name_filtered)
+        new_anim_resource = AnimationResource(resource_name_filtered, self)
         # add animation resource without checking hash,
         # add animation resource without checking hash,
         # blender action is in world space, while godot animation
         # blender action is in world space, while godot animation
         # is in local space (parent space),  so identical actions
         # is in local space (parent space),  so identical actions

+ 7 - 0
io_scene_godot/structures.py

@@ -386,6 +386,13 @@ def fix_bone_attachment_location(attachment_obj, location_vec):
 
 
 def gamma_correct(color):
 def gamma_correct(color):
     """Apply sRGB color space gamma correction to the given color"""
     """Apply sRGB color space gamma correction to the given color"""
+    if isinstance(color, float):
+        # seperate color channel
+        return color ** (1 / 2.2)
+
+    # mathutils.Color does not support alpha yet, so just use RGB
+    # see: https://developer.blender.org/T53540
+    color = color[0:3]
     # note that here use a widely mentioned sRGB approximation gamma = 2.2
     # note that here use a widely mentioned sRGB approximation gamma = 2.2
     # it is good enough, the exact gamma of sRGB can be find at
     # it is good enough, the exact gamma of sRGB can be find at
     # https://en.wikipedia.org/wiki/SRGB
     # https://en.wikipedia.org/wiki/SRGB

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

@@ -28,28 +28,28 @@ tracks/0/type = "value"
 tracks/0/path = NodePath(".:far")
 tracks/0/path = NodePath(".:far")
 tracks/0/interp = 1
 tracks/0/interp = 1
 tracks/0/keys = {
 tracks/0/keys = {
-	"times":PoolRealArray(0.5, 0.541667, 0.583333, 0.625, 0.666667, 0.708333, 0.75, 0.791667),
-	"transitions":PoolRealArray(1, 1, 1, 1, 1, 1, 1, 1),
+	"times":PoolRealArray(0.0, 0.0416667, 0.5, 0.541667, 0.583333, 0.625, 0.666667, 0.708333, 0.75, 0.791667, 0.833333, 2.875),
+	"transitions":PoolRealArray(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
 	"update":0,
 	"update":0,
-	"values":[100.0, 95.9174, 83.8392, 65.4366, 44.5634, 26.1609, 14.0826, 10.0]
+	"values":[100.0, 100.0, 100.0, 95.9174, 83.8392, 65.4366, 44.5634, 26.1609, 14.0826, 10.0, 10.0, 10.0]
 }
 }
 tracks/1/type = "value"
 tracks/1/type = "value"
 tracks/1/path = NodePath(".:near")
 tracks/1/path = NodePath(".:near")
 tracks/1/interp = 1
 tracks/1/interp = 1
 tracks/1/keys = {
 tracks/1/keys = {
-	"times":PoolRealArray(0.0, 0.0416667, 0.0833333, 0.125, 0.166667, 0.208333, 0.25, 0.291667, 0.333333, 0.375),
-	"transitions":PoolRealArray(1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
+	"times":PoolRealArray(0.0, 0.0416667, 0.0833333, 0.125, 0.166667, 0.208333, 0.25, 0.291667, 0.333333, 0.375, 0.416667, 2.875),
+	"transitions":PoolRealArray(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
 	"update":0,
 	"update":0,
-	"values":[0.1, 1.46695, 5.56822, 12.1535, 20.5309, 29.5691, 37.9465, 44.5318, 48.633, 50.0]
+	"values":[0.1, 1.46695, 5.56822, 12.1535, 20.5309, 29.5691, 37.9465, 44.5318, 48.633, 50.0, 50.0, 50.0]
 }
 }
 tracks/2/type = "value"
 tracks/2/type = "value"
 tracks/2/path = NodePath(".:size")
 tracks/2/path = NodePath(".:size")
 tracks/2/interp = 1
 tracks/2/interp = 1
 tracks/2/keys = {
 tracks/2/keys = {
-	"times":PoolRealArray(2.45833, 2.5, 2.54167, 2.58333, 2.625, 2.66667, 2.70833, 2.75, 2.79167, 2.83333, 2.875),
-	"transitions":PoolRealArray(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
+	"times":PoolRealArray(0.0, 0.0416667, 2.45833, 2.5, 2.54167, 2.58333, 2.625, 2.66667, 2.70833, 2.75, 2.79167, 2.83333, 2.875),
+	"transitions":PoolRealArray(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
 	"update":0,
 	"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]
+	"values":[7.31429, 7.31429, 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/type = "value"
 tracks/3/path = NodePath(".:projection")
 tracks/3/path = NodePath(".:projection")

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

@@ -23,15 +23,15 @@ surfaces/0 = {
 
 
 resource_name = "SunAction001"
 resource_name = "SunAction001"
 step = 0.1
 step = 0.1
-length = 1.25
+length = 4.58333
 tracks/0/type = "value"
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:light_energy")
 tracks/0/path = NodePath(".:light_energy")
 tracks/0/interp = 1
 tracks/0/interp = 1
 tracks/0/keys = {
 tracks/0/keys = {
-	"times":PoolRealArray(0.0, 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, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
+	"times":PoolRealArray(0.0, 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, 0.875, 0.916667, 0.958333, 1.0, 1.04167, 1.08333, 1.125, 1.16667, 1.20833, 1.25, 1.29167, 4.58333),
+	"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),
 	"update":0,
 	"update":0,
-	"values":[1.0, 0.992688, 0.970082, 0.931923, 0.879461, 0.815882, 0.746055, 0.675477, 0.608971, 0.549881, 0.5, 0.452336, 0.399918, 0.34426, 0.287871, 0.234118, 0.186613, 0.14834, 0.121029, 0.105095, 0.1, 0.115518, 0.162222, 0.23816, 0.337483, 0.45, 0.562517, 0.66184, 0.737778, 0.784482, 0.8]
+	"values":[1.0, 0.992688, 0.970082, 0.931923, 0.879461, 0.815882, 0.746055, 0.675477, 0.608971, 0.549881, 0.5, 0.452336, 0.399918, 0.34426, 0.287871, 0.234118, 0.186613, 0.14834, 0.121029, 0.105095, 0.1, 0.115518, 0.162222, 0.23816, 0.337483, 0.45, 0.562517, 0.66184, 0.737778, 0.784482, 0.8, 0.8, 0.8]
 }
 }
 [node type="Spatial" name="Scene"]
 [node type="Spatial" name="Scene"]